removed old rpc structure and added new inproc api client
This commit is contained in:
parent
3e1d635f8d
commit
a4a4e9fcf8
@ -1,937 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/ethash"
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/common/compiler"
|
|
||||||
"github.com/ethereum/go-ethereum/common/natspec"
|
|
||||||
"github.com/ethereum/go-ethereum/common/resolver"
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
"github.com/ethereum/go-ethereum/xeth"
|
|
||||||
"github.com/robertkrimen/otto"
|
|
||||||
"gopkg.in/fatih/set.v0"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
node admin bindings
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (js *jsre) adminBindings() {
|
|
||||||
ethO, _ := js.re.Get("eth")
|
|
||||||
eth := ethO.Object()
|
|
||||||
eth.Set("pendingTransactions", js.pendingTransactions)
|
|
||||||
eth.Set("resend", js.resend)
|
|
||||||
eth.Set("sign", js.sign)
|
|
||||||
|
|
||||||
js.re.Set("admin", struct{}{})
|
|
||||||
t, _ := js.re.Get("admin")
|
|
||||||
admin := t.Object()
|
|
||||||
admin.Set("addPeer", js.addPeer)
|
|
||||||
admin.Set("startRPC", js.startRPC)
|
|
||||||
admin.Set("stopRPC", js.stopRPC)
|
|
||||||
admin.Set("nodeInfo", js.nodeInfo)
|
|
||||||
admin.Set("peers", js.peers)
|
|
||||||
admin.Set("newAccount", js.newAccount)
|
|
||||||
admin.Set("unlock", js.unlock)
|
|
||||||
admin.Set("import", js.importChain)
|
|
||||||
admin.Set("export", js.exportChain)
|
|
||||||
admin.Set("verbosity", js.verbosity)
|
|
||||||
admin.Set("progress", js.syncProgress)
|
|
||||||
admin.Set("setSolc", js.setSolc)
|
|
||||||
|
|
||||||
admin.Set("contractInfo", struct{}{})
|
|
||||||
t, _ = admin.Get("contractInfo")
|
|
||||||
cinfo := t.Object()
|
|
||||||
// newRegistry officially not documented temporary option
|
|
||||||
cinfo.Set("start", js.startNatSpec)
|
|
||||||
cinfo.Set("stop", js.stopNatSpec)
|
|
||||||
cinfo.Set("newRegistry", js.newRegistry)
|
|
||||||
cinfo.Set("get", js.getContractInfo)
|
|
||||||
cinfo.Set("register", js.register)
|
|
||||||
cinfo.Set("registerUrl", js.registerUrl)
|
|
||||||
// cinfo.Set("verify", js.verify)
|
|
||||||
|
|
||||||
admin.Set("miner", struct{}{})
|
|
||||||
t, _ = admin.Get("miner")
|
|
||||||
miner := t.Object()
|
|
||||||
miner.Set("start", js.startMining)
|
|
||||||
miner.Set("stop", js.stopMining)
|
|
||||||
miner.Set("hashrate", js.hashrate)
|
|
||||||
miner.Set("setExtra", js.setExtra)
|
|
||||||
miner.Set("setGasPrice", js.setGasPrice)
|
|
||||||
miner.Set("startAutoDAG", js.startAutoDAG)
|
|
||||||
miner.Set("stopAutoDAG", js.stopAutoDAG)
|
|
||||||
miner.Set("makeDAG", js.makeDAG)
|
|
||||||
|
|
||||||
admin.Set("txPool", struct{}{})
|
|
||||||
t, _ = admin.Get("txPool")
|
|
||||||
txPool := t.Object()
|
|
||||||
txPool.Set("pending", js.allPendingTransactions)
|
|
||||||
txPool.Set("queued", js.allQueuedTransactions)
|
|
||||||
|
|
||||||
admin.Set("debug", struct{}{})
|
|
||||||
t, _ = admin.Get("debug")
|
|
||||||
debug := t.Object()
|
|
||||||
js.re.Set("sleep", js.sleep)
|
|
||||||
debug.Set("backtrace", js.backtrace)
|
|
||||||
debug.Set("printBlock", js.printBlock)
|
|
||||||
debug.Set("dumpBlock", js.dumpBlock)
|
|
||||||
debug.Set("getBlockRlp", js.getBlockRlp)
|
|
||||||
debug.Set("setHead", js.setHead)
|
|
||||||
debug.Set("processBlock", js.debugBlock)
|
|
||||||
debug.Set("seedhash", js.seedHash)
|
|
||||||
debug.Set("insertBlock", js.insertBlockRlp)
|
|
||||||
// undocumented temporary
|
|
||||||
debug.Set("waitForBlocks", js.waitForBlocks)
|
|
||||||
}
|
|
||||||
|
|
||||||
// generic helper to getBlock by Number/Height or Hex depending on autodetected input
|
|
||||||
// if argument is missing the current block is returned
|
|
||||||
// if block is not found or there is problem with decoding
|
|
||||||
// the appropriate value is returned and block is guaranteed to be nil
|
|
||||||
func (js *jsre) getBlock(call otto.FunctionCall) (*types.Block, error) {
|
|
||||||
var block *types.Block
|
|
||||||
if len(call.ArgumentList) > 0 {
|
|
||||||
if call.Argument(0).IsNumber() {
|
|
||||||
num, _ := call.Argument(0).ToInteger()
|
|
||||||
block = js.ethereum.ChainManager().GetBlockByNumber(uint64(num))
|
|
||||||
} else if call.Argument(0).IsString() {
|
|
||||||
hash, _ := call.Argument(0).ToString()
|
|
||||||
block = js.ethereum.ChainManager().GetBlock(common.HexToHash(hash))
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("invalid argument for dump. Either hex string or number")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
block = js.ethereum.ChainManager().CurrentBlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
if block == nil {
|
|
||||||
return nil, errors.New("block not found")
|
|
||||||
}
|
|
||||||
return block, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) seedHash(call otto.FunctionCall) otto.Value {
|
|
||||||
if len(call.ArgumentList) > 0 {
|
|
||||||
if call.Argument(0).IsNumber() {
|
|
||||||
num, _ := call.Argument(0).ToInteger()
|
|
||||||
hash, err := ethash.GetSeedHash(uint64(num))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
v, _ := call.Otto.ToValue(fmt.Sprintf("0x%x", hash))
|
|
||||||
return v
|
|
||||||
} else {
|
|
||||||
fmt.Println("arg not a number")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Println("requires number argument")
|
|
||||||
}
|
|
||||||
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) allPendingTransactions(call otto.FunctionCall) otto.Value {
|
|
||||||
txs := js.ethereum.TxPool().GetTransactions()
|
|
||||||
|
|
||||||
ltxs := make([]*tx, len(txs))
|
|
||||||
for i, tx := range txs {
|
|
||||||
// no need to check err
|
|
||||||
ltxs[i] = newTx(tx)
|
|
||||||
}
|
|
||||||
|
|
||||||
v, _ := call.Otto.ToValue(ltxs)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) allQueuedTransactions(call otto.FunctionCall) otto.Value {
|
|
||||||
txs := js.ethereum.TxPool().GetQueuedTransactions()
|
|
||||||
|
|
||||||
ltxs := make([]*tx, len(txs))
|
|
||||||
for i, tx := range txs {
|
|
||||||
// no need to check err
|
|
||||||
ltxs[i] = newTx(tx)
|
|
||||||
}
|
|
||||||
|
|
||||||
v, _ := call.Otto.ToValue(ltxs)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) pendingTransactions(call otto.FunctionCall) otto.Value {
|
|
||||||
txs := js.ethereum.TxPool().GetTransactions()
|
|
||||||
|
|
||||||
// grab the accounts from the account manager. This will help with determening which
|
|
||||||
// transactions should be returned.
|
|
||||||
accounts, err := js.ethereum.AccountManager().Accounts()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the accouns to a new set
|
|
||||||
accountSet := set.New()
|
|
||||||
for _, account := range accounts {
|
|
||||||
accountSet.Add(account.Address)
|
|
||||||
}
|
|
||||||
|
|
||||||
//ltxs := make([]*tx, len(txs))
|
|
||||||
var ltxs []*tx
|
|
||||||
for _, tx := range txs {
|
|
||||||
if from, _ := tx.From(); accountSet.Has(from) {
|
|
||||||
ltxs = append(ltxs, newTx(tx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v, _ := call.Otto.ToValue(ltxs)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) resend(call otto.FunctionCall) otto.Value {
|
|
||||||
if len(call.ArgumentList) == 0 {
|
|
||||||
fmt.Println("first argument must be a transaction")
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
v, err := call.Argument(0).Export()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
if tx, ok := v.(*tx); ok {
|
|
||||||
gl, gp := tx.GasLimit, tx.GasPrice
|
|
||||||
if len(call.ArgumentList) > 1 {
|
|
||||||
gp = call.Argument(1).String()
|
|
||||||
}
|
|
||||||
if len(call.ArgumentList) > 2 {
|
|
||||||
gl = call.Argument(2).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
ret, err := js.xeth.Transact(tx.From, tx.To, tx.Nonce, tx.Value, gl, gp, tx.Data)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
js.ethereum.TxPool().RemoveTransactions(types.Transactions{tx.tx})
|
|
||||||
|
|
||||||
v, _ := call.Otto.ToValue(ret)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("first argument must be a transaction")
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) sign(call otto.FunctionCall) otto.Value {
|
|
||||||
if len(call.ArgumentList) != 2 {
|
|
||||||
fmt.Println("requires 2 arguments: eth.sign(signer, data)")
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
signer, err := call.Argument(0).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := call.Argument(1).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
signed, err := js.xeth.Sign(signer, data, false)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
v, _ := call.Otto.ToValue(signed)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value {
|
|
||||||
block, err := js.getBlock(call)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
tstart := time.Now()
|
|
||||||
old := vm.Debug
|
|
||||||
|
|
||||||
if len(call.ArgumentList) > 1 {
|
|
||||||
vm.Debug, _ = call.Argument(1).ToBoolean()
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = js.ethereum.BlockProcessor().RetryProcess(block)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
r, _ := call.Otto.ToValue(map[string]interface{}{"success": false, "time": time.Since(tstart).Seconds()})
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
vm.Debug = old
|
|
||||||
|
|
||||||
r, _ := call.Otto.ToValue(map[string]interface{}{"success": true, "time": time.Since(tstart).Seconds()})
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) insertBlockRlp(call otto.FunctionCall) otto.Value {
|
|
||||||
tstart := time.Now()
|
|
||||||
|
|
||||||
var block types.Block
|
|
||||||
if call.Argument(0).IsString() {
|
|
||||||
blockRlp, _ := call.Argument(0).ToString()
|
|
||||||
err := rlp.DecodeBytes(common.Hex2Bytes(blockRlp), &block)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
old := vm.Debug
|
|
||||||
vm.Debug = true
|
|
||||||
_, err := js.ethereum.BlockProcessor().RetryProcess(&block)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
r, _ := call.Otto.ToValue(map[string]interface{}{"success": false, "time": time.Since(tstart).Seconds()})
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
vm.Debug = old
|
|
||||||
|
|
||||||
r, _ := call.Otto.ToValue(map[string]interface{}{"success": true, "time": time.Since(tstart).Seconds()})
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) setHead(call otto.FunctionCall) otto.Value {
|
|
||||||
block, err := js.getBlock(call)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
js.ethereum.ChainManager().SetHead(block)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) syncProgress(call otto.FunctionCall) otto.Value {
|
|
||||||
pending, cached, importing, eta := js.ethereum.Downloader().Stats()
|
|
||||||
v, _ := call.Otto.ToValue(map[string]interface{}{
|
|
||||||
"pending": pending,
|
|
||||||
"cached": cached,
|
|
||||||
"importing": importing,
|
|
||||||
"estimate": (eta / time.Second * time.Second).String(),
|
|
||||||
})
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) getBlockRlp(call otto.FunctionCall) otto.Value {
|
|
||||||
block, err := js.getBlock(call)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
encoded, _ := rlp.EncodeToBytes(block)
|
|
||||||
v, _ := call.Otto.ToValue(fmt.Sprintf("%x", encoded))
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) setExtra(call otto.FunctionCall) otto.Value {
|
|
||||||
extra, err := call.Argument(0).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(extra) > 1024 {
|
|
||||||
fmt.Println("error: cannot exceed 1024 bytes")
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
js.ethereum.Miner().SetExtra([]byte(extra))
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) setGasPrice(call otto.FunctionCall) otto.Value {
|
|
||||||
gasPrice, err := call.Argument(0).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
js.ethereum.Miner().SetGasPrice(common.String2Big(gasPrice))
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) hashrate(call otto.FunctionCall) otto.Value {
|
|
||||||
v, _ := call.Otto.ToValue(js.ethereum.Miner().HashRate())
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) makeDAG(call otto.FunctionCall) otto.Value {
|
|
||||||
blockNumber, err := call.Argument(1).ToInteger()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ethash.MakeDAG(uint64(blockNumber), "")
|
|
||||||
if err != nil {
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
return otto.TrueValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) startAutoDAG(otto.FunctionCall) otto.Value {
|
|
||||||
js.ethereum.StartAutoDAG()
|
|
||||||
return otto.TrueValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) stopAutoDAG(otto.FunctionCall) otto.Value {
|
|
||||||
js.ethereum.StopAutoDAG()
|
|
||||||
return otto.TrueValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) backtrace(call otto.FunctionCall) otto.Value {
|
|
||||||
tracestr, err := call.Argument(0).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
glog.GetTraceLocation().Set(tracestr)
|
|
||||||
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) verbosity(call otto.FunctionCall) otto.Value {
|
|
||||||
v, err := call.Argument(0).ToInteger()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
glog.SetV(int(v))
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) startMining(call otto.FunctionCall) otto.Value {
|
|
||||||
var (
|
|
||||||
threads int64
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(call.ArgumentList) > 0 {
|
|
||||||
threads, err = call.Argument(0).ToInteger()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
threads = int64(js.ethereum.MinerThreads)
|
|
||||||
}
|
|
||||||
|
|
||||||
// switch on DAG autogeneration when miner starts
|
|
||||||
js.ethereum.StartAutoDAG()
|
|
||||||
|
|
||||||
err = js.ethereum.StartMining(int(threads))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
return otto.TrueValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) stopMining(call otto.FunctionCall) otto.Value {
|
|
||||||
js.ethereum.StopMining()
|
|
||||||
js.ethereum.StopAutoDAG()
|
|
||||||
return otto.TrueValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) startRPC(call otto.FunctionCall) otto.Value {
|
|
||||||
addr, err := call.Argument(0).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
port, err := call.Argument(1).ToInteger()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
corsDomain := js.corsDomain
|
|
||||||
if len(call.ArgumentList) > 2 {
|
|
||||||
corsDomain, err = call.Argument(2).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config := rpc.RpcConfig{
|
|
||||||
ListenAddress: addr,
|
|
||||||
ListenPort: uint(port),
|
|
||||||
CorsDomain: corsDomain,
|
|
||||||
}
|
|
||||||
|
|
||||||
xeth := xeth.New(js.ethereum, nil)
|
|
||||||
err = rpc.Start(xeth, config)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
return otto.TrueValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) stopRPC(call otto.FunctionCall) otto.Value {
|
|
||||||
if rpc.Stop() == nil {
|
|
||||||
return otto.TrueValue()
|
|
||||||
}
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) addPeer(call otto.FunctionCall) otto.Value {
|
|
||||||
nodeURL, err := call.Argument(0).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
err = js.ethereum.AddPeer(nodeURL)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
return otto.TrueValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) unlock(call otto.FunctionCall) otto.Value {
|
|
||||||
addr, err := call.Argument(0).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
seconds, err := call.Argument(2).ToInteger()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
if seconds == 0 {
|
|
||||||
seconds = accounts.DefaultAccountUnlockDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
arg := call.Argument(1)
|
|
||||||
var passphrase string
|
|
||||||
if arg.IsUndefined() {
|
|
||||||
fmt.Println("Please enter a passphrase now.")
|
|
||||||
passphrase, err = utils.PromptPassword("Passphrase: ", true)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
passphrase, err = arg.ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
am := js.ethereum.AccountManager()
|
|
||||||
err = am.TimedUnlock(common.HexToAddress(addr), passphrase, time.Duration(seconds)*time.Second)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Unlock account failed '%v'\n", err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
return otto.TrueValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) newAccount(call otto.FunctionCall) otto.Value {
|
|
||||||
arg := call.Argument(0)
|
|
||||||
var passphrase string
|
|
||||||
if arg.IsUndefined() {
|
|
||||||
fmt.Println("The new account will be encrypted with a passphrase.")
|
|
||||||
fmt.Println("Please enter a passphrase now.")
|
|
||||||
auth, err := utils.PromptPassword("Passphrase: ", true)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
confirm, err := utils.PromptPassword("Repeat Passphrase: ", false)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
if auth != confirm {
|
|
||||||
fmt.Println("Passphrases did not match.")
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
passphrase = auth
|
|
||||||
} else {
|
|
||||||
var err error
|
|
||||||
passphrase, err = arg.ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
acct, err := js.ethereum.AccountManager().NewAccount(passphrase)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Could not create the account: %v", err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
v, _ := call.Otto.ToValue(acct.Address.Hex())
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) nodeInfo(call otto.FunctionCall) otto.Value {
|
|
||||||
v, _ := call.Otto.ToValue(js.ethereum.NodeInfo())
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) peers(call otto.FunctionCall) otto.Value {
|
|
||||||
v, _ := call.Otto.ToValue(js.ethereum.PeersInfo())
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) importChain(call otto.FunctionCall) otto.Value {
|
|
||||||
if len(call.ArgumentList) == 0 {
|
|
||||||
fmt.Println("require file name. admin.importChain(filename)")
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
fn, err := call.Argument(0).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
if err := utils.ImportChain(js.ethereum.ChainManager(), fn); err != nil {
|
|
||||||
fmt.Println("Import error: ", err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
return otto.TrueValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) exportChain(call otto.FunctionCall) otto.Value {
|
|
||||||
if len(call.ArgumentList) == 0 {
|
|
||||||
fmt.Println("require file name: admin.exportChain(filename)")
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn, err := call.Argument(0).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
if err := utils.ExportChain(js.ethereum.ChainManager(), fn); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
return otto.TrueValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) printBlock(call otto.FunctionCall) otto.Value {
|
|
||||||
block, err := js.getBlock(call)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(block)
|
|
||||||
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) dumpBlock(call otto.FunctionCall) otto.Value {
|
|
||||||
block, err := js.getBlock(call)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
statedb := state.New(block.Root(), js.ethereum.StateDb())
|
|
||||||
dump := statedb.RawDump()
|
|
||||||
v, _ := call.Otto.ToValue(dump)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) waitForBlocks(call otto.FunctionCall) otto.Value {
|
|
||||||
if len(call.ArgumentList) > 2 {
|
|
||||||
fmt.Println("requires 0, 1 or 2 arguments: admin.debug.waitForBlock(minHeight, timeout)")
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
var n, timeout int64
|
|
||||||
var timer <-chan time.Time
|
|
||||||
var height *big.Int
|
|
||||||
var err error
|
|
||||||
args := len(call.ArgumentList)
|
|
||||||
if args == 2 {
|
|
||||||
timeout, err = call.Argument(1).ToInteger()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
timer = time.NewTimer(time.Duration(timeout) * time.Second).C
|
|
||||||
}
|
|
||||||
if args >= 1 {
|
|
||||||
n, err = call.Argument(0).ToInteger()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
height = big.NewInt(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
if args == 0 {
|
|
||||||
height = js.xeth.CurrentBlock().Number()
|
|
||||||
height.Add(height, common.Big1)
|
|
||||||
}
|
|
||||||
|
|
||||||
wait := js.wait
|
|
||||||
js.wait <- height
|
|
||||||
select {
|
|
||||||
case <-timer:
|
|
||||||
// if times out make sure the xeth loop does not block
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case wait <- nil:
|
|
||||||
case <-wait:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
case height = <-wait:
|
|
||||||
}
|
|
||||||
v, _ := call.Otto.ToValue(height.Uint64())
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) sleep(call otto.FunctionCall) otto.Value {
|
|
||||||
sec, err := call.Argument(0).ToInteger()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
time.Sleep(time.Duration(sec) * time.Second)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) setSolc(call otto.FunctionCall) otto.Value {
|
|
||||||
if len(call.ArgumentList) != 1 {
|
|
||||||
fmt.Println("needs 1 argument: admin.contractInfo.setSolc(solcPath)")
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
solcPath, err := call.Argument(0).ToString()
|
|
||||||
if err != nil {
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
solc, err := js.xeth.SetSolc(solcPath)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
fmt.Println(solc.Info())
|
|
||||||
return otto.TrueValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) register(call otto.FunctionCall) otto.Value {
|
|
||||||
if len(call.ArgumentList) != 4 {
|
|
||||||
fmt.Println("requires 4 arguments: admin.contractInfo.register(fromaddress, contractaddress, contract, filename)")
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
sender, err := call.Argument(0).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
address, err := call.Argument(1).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := call.Argument(2).Export()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
jsonraw, err := json.Marshal(raw)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
var contract compiler.Contract
|
|
||||||
err = json.Unmarshal(jsonraw, &contract)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
filename, err := call.Argument(3).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
contenthash, err := compiler.ExtractInfo(&contract, filename)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
// sender and contract address are passed as hex strings
|
|
||||||
codeb := js.xeth.CodeAtBytes(address)
|
|
||||||
codehash := common.BytesToHash(crypto.Sha3(codeb))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
registry := resolver.New(js.xeth)
|
|
||||||
|
|
||||||
_, err = registry.RegisterContentHash(common.HexToAddress(sender), codehash, contenthash)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
v, _ := call.Otto.ToValue(contenthash.Hex())
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) registerUrl(call otto.FunctionCall) otto.Value {
|
|
||||||
if len(call.ArgumentList) != 3 {
|
|
||||||
fmt.Println("requires 3 arguments: admin.contractInfo.register(fromaddress, contenthash, filename)")
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
sender, err := call.Argument(0).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
contenthash, err := call.Argument(1).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
url, err := call.Argument(2).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
registry := resolver.New(js.xeth)
|
|
||||||
|
|
||||||
_, err = registry.RegisterUrl(common.HexToAddress(sender), common.HexToHash(contenthash), url)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
return otto.TrueValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) getContractInfo(call otto.FunctionCall) otto.Value {
|
|
||||||
if len(call.ArgumentList) != 1 {
|
|
||||||
fmt.Println("requires 1 argument: admin.contractInfo.register(contractaddress)")
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
addr, err := call.Argument(0).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
infoDoc, err := natspec.FetchDocsForContract(addr, js.xeth, ds)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
var info compiler.ContractInfo
|
|
||||||
err = json.Unmarshal(infoDoc, &info)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
v, _ := call.Otto.ToValue(info)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) startNatSpec(call otto.FunctionCall) otto.Value {
|
|
||||||
js.ethereum.NatSpec = true
|
|
||||||
return otto.TrueValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) stopNatSpec(call otto.FunctionCall) otto.Value {
|
|
||||||
js.ethereum.NatSpec = false
|
|
||||||
return otto.TrueValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (js *jsre) newRegistry(call otto.FunctionCall) otto.Value {
|
|
||||||
|
|
||||||
if len(call.ArgumentList) != 1 {
|
|
||||||
fmt.Println("requires 1 argument: admin.contractInfo.newRegistry(adminaddress)")
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
addr, err := call.Argument(0).ToString()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
registry := resolver.New(js.xeth)
|
|
||||||
err = registry.CreateContracts(common.HexToAddress(addr))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return otto.FalseValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
return otto.TrueValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
// internal transaction type which will allow us to resend transactions using `eth.resend`
|
|
||||||
type tx struct {
|
|
||||||
tx *types.Transaction
|
|
||||||
|
|
||||||
To string
|
|
||||||
From string
|
|
||||||
Nonce string
|
|
||||||
Value string
|
|
||||||
Data string
|
|
||||||
GasLimit string
|
|
||||||
GasPrice string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTx(t *types.Transaction) *tx {
|
|
||||||
from, _ := t.From()
|
|
||||||
var to string
|
|
||||||
if t := t.To(); t != nil {
|
|
||||||
to = t.Hex()
|
|
||||||
}
|
|
||||||
|
|
||||||
return &tx{
|
|
||||||
tx: t,
|
|
||||||
To: to,
|
|
||||||
From: from.Hex(),
|
|
||||||
Value: t.Amount.String(),
|
|
||||||
Nonce: strconv.Itoa(int(t.Nonce())),
|
|
||||||
Data: "0x" + common.Bytes2Hex(t.Data()),
|
|
||||||
GasLimit: t.GasLimit.String(),
|
|
||||||
GasPrice: t.GasPrice().String(),
|
|
||||||
}
|
|
||||||
}
|
|
189
cmd/geth/js.go
189
cmd/geth/js.go
@ -33,6 +33,10 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
re "github.com/ethereum/go-ethereum/jsre"
|
re "github.com/ethereum/go-ethereum/jsre"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc/api"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc/comms"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||||
"github.com/ethereum/go-ethereum/xeth"
|
"github.com/ethereum/go-ethereum/xeth"
|
||||||
"github.com/peterh/liner"
|
"github.com/peterh/liner"
|
||||||
"github.com/robertkrimen/otto"
|
"github.com/robertkrimen/otto"
|
||||||
@ -70,10 +74,70 @@ type jsre struct {
|
|||||||
ps1 string
|
ps1 string
|
||||||
atexit func()
|
atexit func()
|
||||||
corsDomain string
|
corsDomain string
|
||||||
|
client comms.EthereumClient
|
||||||
prompter
|
prompter
|
||||||
}
|
}
|
||||||
|
|
||||||
func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain, ipcpath string, interactive bool, f xeth.Frontend) *jsre {
|
var (
|
||||||
|
loadedModulesMethods map[string][]string
|
||||||
|
)
|
||||||
|
|
||||||
|
func keywordCompleter(line string) []string {
|
||||||
|
results := make([]string, 0)
|
||||||
|
|
||||||
|
if strings.Contains(line, ".") {
|
||||||
|
elements := strings.Split(line, ".")
|
||||||
|
if len(elements) == 2 {
|
||||||
|
module := elements[0]
|
||||||
|
partialMethod := elements[1]
|
||||||
|
if methods, found := loadedModulesMethods[module]; found {
|
||||||
|
for _, method := range methods {
|
||||||
|
if strings.HasPrefix(method, partialMethod) { // e.g. debug.se
|
||||||
|
results = append(results, module+"."+method)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for module, methods := range loadedModulesMethods {
|
||||||
|
if line == module { // user typed in full module name, show all methods
|
||||||
|
for _, method := range methods {
|
||||||
|
results = append(results, module+"."+method)
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(module, line) { // partial method name, e.g. admi
|
||||||
|
results = append(results, module)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiWordCompleter(line string, pos int) (head string, completions []string, tail string) {
|
||||||
|
if len(line) == 0 {
|
||||||
|
return "", nil, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for i = pos - 1; i > 0; i-- {
|
||||||
|
if line[i] == '.' || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= 'A' && line[i] <= 'Z') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if i >= 3 && line[i] == '3' && line[i-3] == 'w' && line[i-2] == 'e' && line[i-1] == 'b' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
begin := line[:i]
|
||||||
|
keyword := line[i:pos]
|
||||||
|
end := line[pos:]
|
||||||
|
|
||||||
|
completionWords := keywordCompleter(keyword)
|
||||||
|
return begin, completionWords, end
|
||||||
|
}
|
||||||
|
|
||||||
|
func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, client comms.EthereumClient, interactive bool, f xeth.Frontend) *jsre {
|
||||||
js := &jsre{ethereum: ethereum, ps1: "> "}
|
js := &jsre{ethereum: ethereum, ps1: "> "}
|
||||||
// set default cors domain used by startRpc from CLI flag
|
// set default cors domain used by startRpc from CLI flag
|
||||||
js.corsDomain = corsDomain
|
js.corsDomain = corsDomain
|
||||||
@ -82,10 +146,16 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain, ipcpath string, intera
|
|||||||
}
|
}
|
||||||
js.xeth = xeth.New(ethereum, f)
|
js.xeth = xeth.New(ethereum, f)
|
||||||
js.wait = js.xeth.UpdateState()
|
js.wait = js.xeth.UpdateState()
|
||||||
|
js.client = client
|
||||||
|
if clt, ok := js.client.(*comms.InProcClient); ok {
|
||||||
|
clt.Initialize(js.xeth, ethereum)
|
||||||
|
}
|
||||||
|
|
||||||
// update state in separare forever blocks
|
// update state in separare forever blocks
|
||||||
js.re = re.New(libPath)
|
js.re = re.New(libPath)
|
||||||
js.apiBindings(ipcpath, f)
|
if err := js.apiBindings(f); err != nil {
|
||||||
js.adminBindings()
|
utils.Fatalf("Unable to connect - %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if !liner.TerminalSupported() || !interactive {
|
if !liner.TerminalSupported() || !interactive {
|
||||||
js.prompter = dumbterm{bufio.NewReader(os.Stdin)}
|
js.prompter = dumbterm{bufio.NewReader(os.Stdin)}
|
||||||
@ -93,6 +163,9 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain, ipcpath string, intera
|
|||||||
lr := liner.NewLiner()
|
lr := liner.NewLiner()
|
||||||
js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) })
|
js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) })
|
||||||
lr.SetCtrlCAborts(true)
|
lr.SetCtrlCAborts(true)
|
||||||
|
js.loadAutoCompletion()
|
||||||
|
lr.SetWordCompleter(apiWordCompleter)
|
||||||
|
lr.SetTabCompletionStyle(liner.TabPrints)
|
||||||
js.prompter = lr
|
js.prompter = lr
|
||||||
js.atexit = func() {
|
js.atexit = func() {
|
||||||
js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) })
|
js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) })
|
||||||
@ -103,8 +176,117 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain, ipcpath string, intera
|
|||||||
return js
|
return js
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *jsre) loadAutoCompletion() {
|
||||||
|
if modules, err := self.suportedApis(); err == nil {
|
||||||
|
loadedModulesMethods = make(map[string][]string)
|
||||||
|
for module, _ := range modules {
|
||||||
|
loadedModulesMethods[module] = api.AutoCompletion[module]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *jsre) suportedApis() (map[string]string, error) {
|
||||||
|
req := shared.Request{
|
||||||
|
Id: 1,
|
||||||
|
Jsonrpc: "2.0",
|
||||||
|
Method: "modules",
|
||||||
|
}
|
||||||
|
|
||||||
|
err := self.client.Send(&req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := self.client.Recv()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if sucRes, ok := res.(map[string]string); ok {
|
||||||
|
if err == nil {
|
||||||
|
return sucRes, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("Unable to determine supported API's")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (js *jsre) apiBindings(f xeth.Frontend) error {
|
||||||
|
apis, err := js.suportedApis()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
apiNames := make([]string, 0, len(apis))
|
||||||
|
for a, _ := range apis {
|
||||||
|
apiNames = append(apiNames, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
apiImpl, err := api.ParseApiString(strings.Join(apiNames, ","), codec.JSON, js.xeth, js.ethereum)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Unable to determine supported api's: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
jeth := rpc.NewJeth(api.Merge(apiImpl...), js.re, js.client)
|
||||||
|
js.re.Set("jeth", struct{}{})
|
||||||
|
t, _ := js.re.Get("jeth")
|
||||||
|
jethObj := t.Object()
|
||||||
|
|
||||||
|
jethObj.Set("send", jeth.Send)
|
||||||
|
jethObj.Set("sendAsync", jeth.Send)
|
||||||
|
|
||||||
|
err = js.re.Compile("bignumber.js", re.BigNumber_JS)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Error loading bignumber.js: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = js.re.Compile("ethereum.js", re.Web3_JS)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Error loading web3.js: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = js.re.Eval("var web3 = require('web3');")
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Error requiring web3: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = js.re.Eval("web3.setProvider(jeth)")
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Error setting web3 provider: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// load only supported API's in javascript runtime
|
||||||
|
shortcuts := "var eth = web3.eth; "
|
||||||
|
for _, apiName := range apiNames {
|
||||||
|
if apiName == api.Web3ApiName || apiName == api.EthApiName {
|
||||||
|
continue // manually mapped
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = js.re.Compile(fmt.Sprintf("%s.js", apiName), api.Javascript(apiName)); err == nil {
|
||||||
|
shortcuts += fmt.Sprintf("var %s = web3.%s; ", apiName, apiName)
|
||||||
|
} else {
|
||||||
|
utils.Fatalf("Error loading %s.js: %v", apiName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = js.re.Eval(shortcuts)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Error setting namespaces: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
js.re.Eval(globalRegistrar + "registrar = GlobalRegistrar.at(\"" + globalRegistrarAddr + "\");")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func (js *jsre) apiBindings(ipcpath string, f xeth.Frontend) {
|
func (js *jsre) apiBindings(ipcpath string, f xeth.Frontend) {
|
||||||
xe := xeth.New(js.ethereum, f)
|
xe := xeth.New(js.ethereum, f)
|
||||||
|
apiNames, err := js.suportedApis(ipcpath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ethApi := rpc.NewEthereumApi(xe)
|
ethApi := rpc.NewEthereumApi(xe)
|
||||||
jeth := rpc.NewJeth(ethApi, js.re, ipcpath)
|
jeth := rpc.NewJeth(ethApi, js.re, ipcpath)
|
||||||
|
|
||||||
@ -146,6 +328,7 @@ var net = web3.net;
|
|||||||
|
|
||||||
js.re.Eval(globalRegistrar + "registrar = GlobalRegistrar.at(\"" + globalRegistrarAddr + "\");")
|
js.re.Eval(globalRegistrar + "registrar = GlobalRegistrar.at(\"" + globalRegistrarAddr + "\");")
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
var ds, _ = docserver.New("/")
|
var ds, _ = docserver.New("/")
|
||||||
|
|
||||||
|
@ -38,6 +38,8 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc/comms"
|
||||||
"github.com/mattn/go-colorable"
|
"github.com/mattn/go-colorable"
|
||||||
"github.com/mattn/go-isatty"
|
"github.com/mattn/go-isatty"
|
||||||
)
|
)
|
||||||
@ -310,12 +312,14 @@ func console(ctx *cli.Context) {
|
|||||||
utils.Fatalf("%v", err)
|
utils.Fatalf("%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := comms.NewInProcClient(codec.JSON)
|
||||||
|
|
||||||
startEth(ctx, ethereum)
|
startEth(ctx, ethereum)
|
||||||
repl := newJSRE(
|
repl := newJSRE(
|
||||||
ethereum,
|
ethereum,
|
||||||
ctx.String(utils.JSpathFlag.Name),
|
ctx.String(utils.JSpathFlag.Name),
|
||||||
ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
|
ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
|
||||||
utils.IpcSocketPath(ctx),
|
client,
|
||||||
true,
|
true,
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
@ -332,12 +336,13 @@ func execJSFiles(ctx *cli.Context) {
|
|||||||
utils.Fatalf("%v", err)
|
utils.Fatalf("%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := comms.NewInProcClient(codec.JSON)
|
||||||
startEth(ctx, ethereum)
|
startEth(ctx, ethereum)
|
||||||
repl := newJSRE(
|
repl := newJSRE(
|
||||||
ethereum,
|
ethereum,
|
||||||
ctx.String(utils.JSpathFlag.Name),
|
ctx.String(utils.JSpathFlag.Name),
|
||||||
ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
|
ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
|
||||||
utils.IpcSocketPath(ctx),
|
client,
|
||||||
false,
|
false,
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
@ -211,7 +211,7 @@ var (
|
|||||||
RpcApiFlag = cli.StringFlag{
|
RpcApiFlag = cli.StringFlag{
|
||||||
Name: "rpcapi",
|
Name: "rpcapi",
|
||||||
Usage: "Specify the API's which are offered over the HTTP RPC interface",
|
Usage: "Specify the API's which are offered over the HTTP RPC interface",
|
||||||
Value: api.DefaultHttpRpcApis,
|
Value: comms.DefaultHttpRpcApis,
|
||||||
}
|
}
|
||||||
IPCDisabledFlag = cli.BoolFlag{
|
IPCDisabledFlag = cli.BoolFlag{
|
||||||
Name: "ipcdisable",
|
Name: "ipcdisable",
|
||||||
@ -220,7 +220,7 @@ var (
|
|||||||
IPCApiFlag = cli.StringFlag{
|
IPCApiFlag = cli.StringFlag{
|
||||||
Name: "ipcapi",
|
Name: "ipcapi",
|
||||||
Usage: "Specify the API's which are offered over the IPC interface",
|
Usage: "Specify the API's which are offered over the IPC interface",
|
||||||
Value: api.DefaultIpcApis,
|
Value: comms.DefaultIpcApis,
|
||||||
}
|
}
|
||||||
IPCPathFlag = DirectoryFlag{
|
IPCPathFlag = DirectoryFlag{
|
||||||
Name: "ipcpath",
|
Name: "ipcpath",
|
||||||
|
@ -149,7 +149,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
|
|||||||
return ErrInvalidSender
|
return ErrInvalidSender
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the account exist. Non existant accounts
|
// Make sure the account exist. Non existent accounts
|
||||||
// haven't got funds and well therefor never pass.
|
// haven't got funds and well therefor never pass.
|
||||||
if !pool.currentState().HasAccount(from) {
|
if !pool.currentState().HasAccount(from) {
|
||||||
return ErrNonExistentAccount
|
return ErrNonExistentAccount
|
||||||
|
646
rpc/api.go
646
rpc/api.go
@ -1,646 +0,0 @@
|
|||||||
package rpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/xeth"
|
|
||||||
)
|
|
||||||
|
|
||||||
type EthereumApi struct {
|
|
||||||
eth *xeth.XEth
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEthereumApi(xeth *xeth.XEth) *EthereumApi {
|
|
||||||
api := &EthereumApi{
|
|
||||||
eth: xeth,
|
|
||||||
}
|
|
||||||
|
|
||||||
return api
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *EthereumApi) xeth() *xeth.XEth {
|
|
||||||
return api.eth
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *EthereumApi) xethAtStateNum(num int64) *xeth.XEth {
|
|
||||||
return api.xeth().AtStateNum(num)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error {
|
|
||||||
// Spec at https://github.com/ethereum/wiki/wiki/JSON-RPC
|
|
||||||
glog.V(logger.Debug).Infof("%s %s", req.Method, req.Params)
|
|
||||||
|
|
||||||
switch req.Method {
|
|
||||||
case "web3_sha3":
|
|
||||||
args := new(Sha3Args)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*reply = common.ToHex(crypto.Sha3(common.FromHex(args.Data)))
|
|
||||||
case "web3_clientVersion":
|
|
||||||
*reply = api.xeth().ClientVersion()
|
|
||||||
case "net_version":
|
|
||||||
*reply = api.xeth().NetworkVersion()
|
|
||||||
case "net_listening":
|
|
||||||
*reply = api.xeth().IsListening()
|
|
||||||
case "net_peerCount":
|
|
||||||
*reply = newHexNum(api.xeth().PeerCount())
|
|
||||||
case "eth_protocolVersion":
|
|
||||||
*reply = api.xeth().EthVersion()
|
|
||||||
case "eth_coinbase":
|
|
||||||
*reply = newHexData(api.xeth().Coinbase())
|
|
||||||
case "eth_mining":
|
|
||||||
*reply = api.xeth().IsMining()
|
|
||||||
case "eth_gasPrice":
|
|
||||||
v := api.xeth().DefaultGasPrice()
|
|
||||||
*reply = newHexNum(v.Bytes())
|
|
||||||
case "eth_accounts":
|
|
||||||
*reply = api.xeth().Accounts()
|
|
||||||
case "eth_blockNumber":
|
|
||||||
v := api.xeth().CurrentBlock().Number()
|
|
||||||
*reply = newHexNum(v.Bytes())
|
|
||||||
case "eth_getBalance":
|
|
||||||
args := new(GetBalanceArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*reply = api.xethAtStateNum(args.BlockNumber).BalanceAt(args.Address)
|
|
||||||
//v := api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address).Balance()
|
|
||||||
//*reply = common.ToHex(v.Bytes())
|
|
||||||
case "eth_getStorage", "eth_storageAt":
|
|
||||||
args := new(GetStorageArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*reply = api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage()
|
|
||||||
case "eth_getStorageAt":
|
|
||||||
args := new(GetStorageAtArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*reply = api.xethAtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key)
|
|
||||||
case "eth_getTransactionCount":
|
|
||||||
args := new(GetTxCountArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
count := api.xethAtStateNum(args.BlockNumber).TxCountAt(args.Address)
|
|
||||||
*reply = newHexNum(big.NewInt(int64(count)).Bytes())
|
|
||||||
case "eth_getBlockTransactionCountByHash":
|
|
||||||
args := new(HashArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
block := NewBlockRes(api.xeth().EthBlockByHash(args.Hash), false)
|
|
||||||
if block == nil {
|
|
||||||
*reply = nil
|
|
||||||
} else {
|
|
||||||
*reply = newHexNum(big.NewInt(int64(len(block.Transactions))).Bytes())
|
|
||||||
}
|
|
||||||
case "eth_getBlockTransactionCountByNumber":
|
|
||||||
args := new(BlockNumArg)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
block := NewBlockRes(api.xeth().EthBlockByNumber(args.BlockNumber), false)
|
|
||||||
if block == nil {
|
|
||||||
*reply = nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
*reply = newHexNum(big.NewInt(int64(len(block.Transactions))).Bytes())
|
|
||||||
case "eth_getUncleCountByBlockHash":
|
|
||||||
args := new(HashArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
block := api.xeth().EthBlockByHash(args.Hash)
|
|
||||||
br := NewBlockRes(block, false)
|
|
||||||
if br == nil {
|
|
||||||
*reply = nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
*reply = newHexNum(big.NewInt(int64(len(br.Uncles))).Bytes())
|
|
||||||
case "eth_getUncleCountByBlockNumber":
|
|
||||||
args := new(BlockNumArg)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
block := api.xeth().EthBlockByNumber(args.BlockNumber)
|
|
||||||
br := NewBlockRes(block, false)
|
|
||||||
if br == nil {
|
|
||||||
*reply = nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
*reply = newHexNum(big.NewInt(int64(len(br.Uncles))).Bytes())
|
|
||||||
|
|
||||||
case "eth_getData", "eth_getCode":
|
|
||||||
args := new(GetDataArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v := api.xethAtStateNum(args.BlockNumber).CodeAtBytes(args.Address)
|
|
||||||
*reply = newHexData(v)
|
|
||||||
|
|
||||||
case "eth_sign":
|
|
||||||
args := new(NewSigArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v, err := api.xeth().Sign(args.From, args.Data, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*reply = v
|
|
||||||
|
|
||||||
case "eth_sendRawTransaction":
|
|
||||||
args := new(NewDataArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v, err := api.xeth().PushTx(args.Data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*reply = v
|
|
||||||
|
|
||||||
case "eth_sendTransaction", "eth_transact":
|
|
||||||
args := new(NewTxArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// nonce may be nil ("guess" mode)
|
|
||||||
var nonce string
|
|
||||||
if args.Nonce != nil {
|
|
||||||
nonce = args.Nonce.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
var gas string
|
|
||||||
if args.Gas == nil {
|
|
||||||
gas = ""
|
|
||||||
} else {
|
|
||||||
gas = args.Gas.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
var gasprice string
|
|
||||||
if args.GasPrice == nil {
|
|
||||||
gasprice = ""
|
|
||||||
} else {
|
|
||||||
gasprice = args.GasPrice.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
v, err := api.xeth().Transact(args.From, args.To, nonce, args.Value.String(), gas, gasprice, args.Data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*reply = v
|
|
||||||
case "eth_estimateGas":
|
|
||||||
_, gas, err := api.doCall(req.Params)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO unwrap the parent method's ToHex call
|
|
||||||
if len(gas) == 0 {
|
|
||||||
*reply = newHexNum(0)
|
|
||||||
} else {
|
|
||||||
*reply = newHexNum(gas)
|
|
||||||
}
|
|
||||||
case "eth_call":
|
|
||||||
v, _, err := api.doCall(req.Params)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO unwrap the parent method's ToHex call
|
|
||||||
if v == "0x0" {
|
|
||||||
*reply = newHexData([]byte{})
|
|
||||||
} else {
|
|
||||||
*reply = newHexData(common.FromHex(v))
|
|
||||||
}
|
|
||||||
case "eth_flush":
|
|
||||||
return NewNotImplementedError(req.Method)
|
|
||||||
case "eth_getBlockByHash":
|
|
||||||
args := new(GetBlockByHashArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
block := api.xeth().EthBlockByHash(args.BlockHash)
|
|
||||||
br := NewBlockRes(block, args.IncludeTxs)
|
|
||||||
|
|
||||||
*reply = br
|
|
||||||
case "eth_getBlockByNumber":
|
|
||||||
args := new(GetBlockByNumberArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
block := api.xeth().EthBlockByNumber(args.BlockNumber)
|
|
||||||
br := NewBlockRes(block, args.IncludeTxs)
|
|
||||||
// If request was for "pending", nil nonsensical fields
|
|
||||||
if args.BlockNumber == -2 {
|
|
||||||
br.BlockHash = nil
|
|
||||||
br.BlockNumber = nil
|
|
||||||
br.Miner = nil
|
|
||||||
br.Nonce = nil
|
|
||||||
br.LogsBloom = nil
|
|
||||||
}
|
|
||||||
*reply = br
|
|
||||||
case "eth_getTransactionByHash":
|
|
||||||
args := new(HashArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tx, bhash, bnum, txi := api.xeth().EthTransactionByHash(args.Hash)
|
|
||||||
if tx != nil {
|
|
||||||
v := NewTransactionRes(tx)
|
|
||||||
// if the blockhash is 0, assume this is a pending transaction
|
|
||||||
if bytes.Compare(bhash.Bytes(), bytes.Repeat([]byte{0}, 32)) != 0 {
|
|
||||||
v.BlockHash = newHexData(bhash)
|
|
||||||
v.BlockNumber = newHexNum(bnum)
|
|
||||||
v.TxIndex = newHexNum(txi)
|
|
||||||
}
|
|
||||||
*reply = v
|
|
||||||
}
|
|
||||||
case "eth_getTransactionByBlockHashAndIndex":
|
|
||||||
args := new(HashIndexArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
block := api.xeth().EthBlockByHash(args.Hash)
|
|
||||||
br := NewBlockRes(block, true)
|
|
||||||
if br == nil {
|
|
||||||
*reply = nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.Index >= int64(len(br.Transactions)) || args.Index < 0 {
|
|
||||||
// return NewValidationError("Index", "does not exist")
|
|
||||||
*reply = nil
|
|
||||||
} else {
|
|
||||||
*reply = br.Transactions[args.Index]
|
|
||||||
}
|
|
||||||
case "eth_getTransactionByBlockNumberAndIndex":
|
|
||||||
args := new(BlockNumIndexArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
block := api.xeth().EthBlockByNumber(args.BlockNumber)
|
|
||||||
v := NewBlockRes(block, true)
|
|
||||||
if v == nil {
|
|
||||||
*reply = nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.Index >= int64(len(v.Transactions)) || args.Index < 0 {
|
|
||||||
// return NewValidationError("Index", "does not exist")
|
|
||||||
*reply = nil
|
|
||||||
} else {
|
|
||||||
*reply = v.Transactions[args.Index]
|
|
||||||
}
|
|
||||||
case "eth_getUncleByBlockHashAndIndex":
|
|
||||||
args := new(HashIndexArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
br := NewBlockRes(api.xeth().EthBlockByHash(args.Hash), false)
|
|
||||||
if br == nil {
|
|
||||||
*reply = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.Index >= int64(len(br.Uncles)) || args.Index < 0 {
|
|
||||||
// return NewValidationError("Index", "does not exist")
|
|
||||||
*reply = nil
|
|
||||||
} else {
|
|
||||||
*reply = br.Uncles[args.Index]
|
|
||||||
}
|
|
||||||
case "eth_getUncleByBlockNumberAndIndex":
|
|
||||||
args := new(BlockNumIndexArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
block := api.xeth().EthBlockByNumber(args.BlockNumber)
|
|
||||||
v := NewBlockRes(block, true)
|
|
||||||
|
|
||||||
if v == nil {
|
|
||||||
*reply = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.Index >= int64(len(v.Uncles)) || args.Index < 0 {
|
|
||||||
// return NewValidationError("Index", "does not exist")
|
|
||||||
*reply = nil
|
|
||||||
} else {
|
|
||||||
*reply = v.Uncles[args.Index]
|
|
||||||
}
|
|
||||||
|
|
||||||
case "eth_getCompilers":
|
|
||||||
var lang string
|
|
||||||
if solc, _ := api.xeth().Solc(); solc != nil {
|
|
||||||
lang = "Solidity"
|
|
||||||
}
|
|
||||||
c := []string{lang}
|
|
||||||
*reply = c
|
|
||||||
|
|
||||||
case "eth_compileLLL", "eth_compileSerpent":
|
|
||||||
return NewNotImplementedError(req.Method)
|
|
||||||
|
|
||||||
case "eth_compileSolidity":
|
|
||||||
solc, _ := api.xeth().Solc()
|
|
||||||
if solc == nil {
|
|
||||||
return NewNotAvailableError(req.Method, "solc (solidity compiler) not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
args := new(SourceArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
contracts, err := solc.Compile(args.Source)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*reply = contracts
|
|
||||||
|
|
||||||
case "eth_newFilter":
|
|
||||||
args := new(BlockFilterArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
id := api.xeth().NewLogFilter(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics)
|
|
||||||
*reply = newHexNum(big.NewInt(int64(id)).Bytes())
|
|
||||||
|
|
||||||
case "eth_newBlockFilter":
|
|
||||||
*reply = newHexNum(api.xeth().NewBlockFilter())
|
|
||||||
case "eth_newPendingTransactionFilter":
|
|
||||||
*reply = newHexNum(api.xeth().NewTransactionFilter())
|
|
||||||
case "eth_uninstallFilter":
|
|
||||||
args := new(FilterIdArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*reply = api.xeth().UninstallFilter(args.Id)
|
|
||||||
case "eth_getFilterChanges":
|
|
||||||
args := new(FilterIdArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch api.xeth().GetFilterType(args.Id) {
|
|
||||||
case xeth.BlockFilterTy:
|
|
||||||
*reply = NewHashesRes(api.xeth().BlockFilterChanged(args.Id))
|
|
||||||
case xeth.TransactionFilterTy:
|
|
||||||
*reply = NewHashesRes(api.xeth().TransactionFilterChanged(args.Id))
|
|
||||||
case xeth.LogFilterTy:
|
|
||||||
*reply = NewLogsRes(api.xeth().LogFilterChanged(args.Id))
|
|
||||||
default:
|
|
||||||
*reply = []string{} // reply empty string slice
|
|
||||||
}
|
|
||||||
case "eth_getFilterLogs":
|
|
||||||
args := new(FilterIdArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*reply = NewLogsRes(api.xeth().Logs(args.Id))
|
|
||||||
case "eth_getLogs":
|
|
||||||
args := new(BlockFilterArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*reply = NewLogsRes(api.xeth().AllLogs(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics))
|
|
||||||
case "eth_getWork":
|
|
||||||
api.xeth().SetMining(true, 0)
|
|
||||||
*reply = api.xeth().RemoteMining().GetWork()
|
|
||||||
case "eth_submitWork":
|
|
||||||
args := new(SubmitWorkArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*reply = api.xeth().RemoteMining().SubmitWork(args.Nonce, common.HexToHash(args.Digest), common.HexToHash(args.Header))
|
|
||||||
case "db_putString":
|
|
||||||
args := new(DbArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := args.requirements(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
api.xeth().DbPut([]byte(args.Database+args.Key), args.Value)
|
|
||||||
|
|
||||||
*reply = true
|
|
||||||
case "db_getString":
|
|
||||||
args := new(DbArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := args.requirements(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
res, _ := api.xeth().DbGet([]byte(args.Database + args.Key))
|
|
||||||
*reply = string(res)
|
|
||||||
case "db_putHex":
|
|
||||||
args := new(DbHexArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := args.requirements(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
api.xeth().DbPut([]byte(args.Database+args.Key), args.Value)
|
|
||||||
*reply = true
|
|
||||||
case "db_getHex":
|
|
||||||
args := new(DbHexArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := args.requirements(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
res, _ := api.xeth().DbGet([]byte(args.Database + args.Key))
|
|
||||||
*reply = newHexData(res)
|
|
||||||
|
|
||||||
case "shh_version":
|
|
||||||
// Short circuit if whisper is not running
|
|
||||||
if api.xeth().Whisper() == nil {
|
|
||||||
return NewNotAvailableError(req.Method, "whisper offline")
|
|
||||||
}
|
|
||||||
// Retrieves the currently running whisper protocol version
|
|
||||||
*reply = api.xeth().WhisperVersion()
|
|
||||||
|
|
||||||
case "shh_post":
|
|
||||||
// Short circuit if whisper is not running
|
|
||||||
if api.xeth().Whisper() == nil {
|
|
||||||
return NewNotAvailableError(req.Method, "whisper offline")
|
|
||||||
}
|
|
||||||
// Injects a new message into the whisper network
|
|
||||||
args := new(WhisperMessageArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err := api.xeth().Whisper().Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*reply = true
|
|
||||||
|
|
||||||
case "shh_newIdentity":
|
|
||||||
// Short circuit if whisper is not running
|
|
||||||
if api.xeth().Whisper() == nil {
|
|
||||||
return NewNotAvailableError(req.Method, "whisper offline")
|
|
||||||
}
|
|
||||||
// Creates a new whisper identity to use for sending/receiving messages
|
|
||||||
*reply = api.xeth().Whisper().NewIdentity()
|
|
||||||
|
|
||||||
case "shh_hasIdentity":
|
|
||||||
// Short circuit if whisper is not running
|
|
||||||
if api.xeth().Whisper() == nil {
|
|
||||||
return NewNotAvailableError(req.Method, "whisper offline")
|
|
||||||
}
|
|
||||||
// Checks if an identity if owned or not
|
|
||||||
args := new(WhisperIdentityArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*reply = api.xeth().Whisper().HasIdentity(args.Identity)
|
|
||||||
|
|
||||||
case "shh_newFilter":
|
|
||||||
// Short circuit if whisper is not running
|
|
||||||
if api.xeth().Whisper() == nil {
|
|
||||||
return NewNotAvailableError(req.Method, "whisper offline")
|
|
||||||
}
|
|
||||||
// Create a new filter to watch and match messages with
|
|
||||||
args := new(WhisperFilterArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
id := api.xeth().NewWhisperFilter(args.To, args.From, args.Topics)
|
|
||||||
*reply = newHexNum(big.NewInt(int64(id)).Bytes())
|
|
||||||
|
|
||||||
case "shh_uninstallFilter":
|
|
||||||
// Short circuit if whisper is not running
|
|
||||||
if api.xeth().Whisper() == nil {
|
|
||||||
return NewNotAvailableError(req.Method, "whisper offline")
|
|
||||||
}
|
|
||||||
// Remove an existing filter watching messages
|
|
||||||
args := new(FilterIdArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*reply = api.xeth().UninstallWhisperFilter(args.Id)
|
|
||||||
|
|
||||||
case "shh_getFilterChanges":
|
|
||||||
// Short circuit if whisper is not running
|
|
||||||
if api.xeth().Whisper() == nil {
|
|
||||||
return NewNotAvailableError(req.Method, "whisper offline")
|
|
||||||
}
|
|
||||||
// Retrieve all the new messages arrived since the last request
|
|
||||||
args := new(FilterIdArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*reply = api.xeth().WhisperMessagesChanged(args.Id)
|
|
||||||
|
|
||||||
case "shh_getMessages":
|
|
||||||
// Short circuit if whisper is not running
|
|
||||||
if api.xeth().Whisper() == nil {
|
|
||||||
return NewNotAvailableError(req.Method, "whisper offline")
|
|
||||||
}
|
|
||||||
// Retrieve all the cached messages matching a specific, existing filter
|
|
||||||
args := new(FilterIdArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*reply = api.xeth().WhisperMessages(args.Id)
|
|
||||||
|
|
||||||
case "eth_hashrate":
|
|
||||||
*reply = newHexNum(api.xeth().HashRate())
|
|
||||||
case "ext_disasm":
|
|
||||||
args := new(SourceArgs)
|
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*reply = vm.Disasm(common.FromHex(args.Source))
|
|
||||||
|
|
||||||
// case "eth_register":
|
|
||||||
// // Placeholder for actual type
|
|
||||||
// args := new(HashIndexArgs)
|
|
||||||
// if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// *reply = api.xeth().Register(args.Hash)
|
|
||||||
// case "eth_unregister":
|
|
||||||
// args := new(HashIndexArgs)
|
|
||||||
// if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// *reply = api.xeth().Unregister(args.Hash)
|
|
||||||
// case "eth_watchTx":
|
|
||||||
// args := new(HashIndexArgs)
|
|
||||||
// if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// *reply = api.xeth().PullWatchTx(args.Hash)
|
|
||||||
default:
|
|
||||||
return NewNotImplementedError(req.Method)
|
|
||||||
}
|
|
||||||
|
|
||||||
// glog.V(logger.Detail).Infof("Reply: %v\n", reply)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *EthereumApi) doCall(params json.RawMessage) (string, string, error) {
|
|
||||||
args := new(CallArgs)
|
|
||||||
if err := json.Unmarshal(params, &args); err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var gas string
|
|
||||||
if args.Gas == nil {
|
|
||||||
gas = ""
|
|
||||||
} else {
|
|
||||||
gas = args.Gas.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
var gasprice string
|
|
||||||
if args.GasPrice == nil {
|
|
||||||
gasprice = ""
|
|
||||||
} else {
|
|
||||||
gasprice = args.GasPrice.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
return api.xethAtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), gas, gasprice, args.Data)
|
|
||||||
}
|
|
@ -18,15 +18,13 @@ const (
|
|||||||
TxPoolApiName = "txpool"
|
TxPoolApiName = "txpool"
|
||||||
PersonalApiName = "personal"
|
PersonalApiName = "personal"
|
||||||
Web3ApiName = "web3"
|
Web3ApiName = "web3"
|
||||||
|
|
||||||
|
JsonRpcVersion = "2.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultHttpRpcApis = strings.Join([]string{
|
// All API's
|
||||||
DbApiName, EthApiName, NetApiName, Web3ApiName,
|
AllApis = strings.Join([]string{
|
||||||
}, ",")
|
|
||||||
|
|
||||||
// List with all API's which are offered over the IPC interface by default
|
|
||||||
DefaultIpcApis = strings.Join([]string{
|
|
||||||
AdminApiName, DbApiName, EthApiName, DebugApiName, MinerApiName, NetApiName,
|
AdminApiName, DbApiName, EthApiName, DebugApiName, MinerApiName, NetApiName,
|
||||||
ShhApiName, TxPoolApiName, PersonalApiName, Web3ApiName,
|
ShhApiName, TxPoolApiName, PersonalApiName, Web3ApiName,
|
||||||
}, ",")
|
}, ",")
|
||||||
|
57
rpc/api/args.go
Normal file
57
rpc/api/args.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CompileArgs struct {
|
||||||
|
Source string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (args *CompileArgs) UnmarshalJSON(b []byte) (err error) {
|
||||||
|
var obj []interface{}
|
||||||
|
if err := json.Unmarshal(b, &obj); err != nil {
|
||||||
|
return shared.NewDecodeParamError(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(obj) < 1 {
|
||||||
|
return shared.NewInsufficientParamsError(len(obj), 1)
|
||||||
|
}
|
||||||
|
argstr, ok := obj[0].(string)
|
||||||
|
if !ok {
|
||||||
|
return shared.NewInvalidTypeError("arg0", "is not a string")
|
||||||
|
}
|
||||||
|
args.Source = argstr
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type FilterStringArgs struct {
|
||||||
|
Word string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) {
|
||||||
|
var obj []interface{}
|
||||||
|
if err := json.Unmarshal(b, &obj); err != nil {
|
||||||
|
return shared.NewDecodeParamError(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(obj) < 1 {
|
||||||
|
return shared.NewInsufficientParamsError(len(obj), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var argstr string
|
||||||
|
argstr, ok := obj[0].(string)
|
||||||
|
if !ok {
|
||||||
|
return shared.NewInvalidTypeError("filter", "not a string")
|
||||||
|
}
|
||||||
|
switch argstr {
|
||||||
|
case "latest", "pending":
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return shared.NewValidationError("Word", "Must be `latest` or `pending`")
|
||||||
|
}
|
||||||
|
args.Word = argstr
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package rpc
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBlockheightInvalidString(t *testing.T) {
|
func TestBlockheightInvalidString(t *testing.T) {
|
||||||
@ -68,7 +69,7 @@ func ExpectValidationError(err error) string {
|
|||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
str = "Expected error but didn't get one"
|
str = "Expected error but didn't get one"
|
||||||
case *ValidationError:
|
case *shared.ValidationError:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
str = fmt.Sprintf("Expected *rpc.ValidationError but got %T with message `%s`", err, err.Error())
|
str = fmt.Sprintf("Expected *rpc.ValidationError but got %T with message `%s`", err, err.Error())
|
||||||
@ -81,7 +82,7 @@ func ExpectInvalidTypeError(err error) string {
|
|||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
str = "Expected error but didn't get one"
|
str = "Expected error but didn't get one"
|
||||||
case *InvalidTypeError:
|
case *shared.InvalidTypeError:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
str = fmt.Sprintf("Expected *rpc.InvalidTypeError but got %T with message `%s`", err, err.Error())
|
str = fmt.Sprintf("Expected *rpc.InvalidTypeError but got %T with message `%s`", err, err.Error())
|
||||||
@ -94,7 +95,7 @@ func ExpectInsufficientParamsError(err error) string {
|
|||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
str = "Expected error but didn't get one"
|
str = "Expected error but didn't get one"
|
||||||
case *InsufficientParamsError:
|
case *shared.InsufficientParamsError:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
str = fmt.Sprintf("Expected *rpc.InsufficientParamsError but got %T with message %s", err, err.Error())
|
str = fmt.Sprintf("Expected *rpc.InsufficientParamsError but got %T with message %s", err, err.Error())
|
||||||
@ -107,7 +108,7 @@ func ExpectDecodeParamError(err error) string {
|
|||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
str = "Expected error but didn't get one"
|
str = "Expected error but didn't get one"
|
||||||
case *DecodeParamError:
|
case *shared.DecodeParamError:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
str = fmt.Sprintf("Expected *rpc.DecodeParamError but got %T with message `%s`", err, err.Error())
|
str = fmt.Sprintf("Expected *rpc.DecodeParamError but got %T with message `%s`", err, err.Error())
|
205
rpc/api_test.go
205
rpc/api_test.go
@ -1,205 +0,0 @@
|
|||||||
package rpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/compiler"
|
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
|
||||||
"github.com/ethereum/go-ethereum/xeth"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestWeb3Sha3(t *testing.T) {
|
|
||||||
jsonstr := `{"jsonrpc":"2.0","method":"web3_sha3","params":["0x68656c6c6f20776f726c64"],"id":64}`
|
|
||||||
expected := "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"
|
|
||||||
|
|
||||||
api := &EthereumApi{}
|
|
||||||
|
|
||||||
var req RpcRequest
|
|
||||||
json.Unmarshal([]byte(jsonstr), &req)
|
|
||||||
|
|
||||||
var response interface{}
|
|
||||||
_ = api.GetRequestReply(&req, &response)
|
|
||||||
|
|
||||||
if response.(string) != expected {
|
|
||||||
t.Errorf("Expected %s got %s", expected, response)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const solcVersion = "0.9.23"
|
|
||||||
|
|
||||||
func TestCompileSolidity(t *testing.T) {
|
|
||||||
|
|
||||||
solc, err := compiler.New("")
|
|
||||||
if solc == nil {
|
|
||||||
t.Skip("no solc found: skip")
|
|
||||||
} else if solc.Version() != solcVersion {
|
|
||||||
t.Skip("WARNING: skipping test because of solc different version (%v, test written for %v, may need to update)", solc.Version(), solcVersion)
|
|
||||||
}
|
|
||||||
source := `contract test {\n` +
|
|
||||||
" /// @notice Will multiply `a` by 7." + `\n` +
|
|
||||||
` function multiply(uint a) returns(uint d) {\n` +
|
|
||||||
` return a * 7;\n` +
|
|
||||||
` }\n` +
|
|
||||||
`}\n`
|
|
||||||
|
|
||||||
jsonstr := `{"jsonrpc":"2.0","method":"eth_compileSolidity","params":["` + source + `"],"id":64}`
|
|
||||||
|
|
||||||
expCode := "0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056"
|
|
||||||
expAbiDefinition := `[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]`
|
|
||||||
expUserDoc := `{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}}`
|
|
||||||
expDeveloperDoc := `{"methods":{}}`
|
|
||||||
expCompilerVersion := solc.Version()
|
|
||||||
expLanguage := "Solidity"
|
|
||||||
expLanguageVersion := "0"
|
|
||||||
expSource := source
|
|
||||||
|
|
||||||
api := NewEthereumApi(xeth.NewTest(ð.Ethereum{}, nil))
|
|
||||||
|
|
||||||
var req RpcRequest
|
|
||||||
json.Unmarshal([]byte(jsonstr), &req)
|
|
||||||
|
|
||||||
var response interface{}
|
|
||||||
err = api.GetRequestReply(&req, &response)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("expected no error, got %v", err)
|
|
||||||
}
|
|
||||||
respjson, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("expected no error, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var contracts = make(map[string]*compiler.Contract)
|
|
||||||
err = json.Unmarshal(respjson, &contracts)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("expected no error, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(contracts) != 1 {
|
|
||||||
t.Errorf("expected one contract, got %v", len(contracts))
|
|
||||||
}
|
|
||||||
|
|
||||||
contract := contracts["test"]
|
|
||||||
|
|
||||||
if contract.Code != expCode {
|
|
||||||
t.Errorf("Expected \n%s got \n%s", expCode, contract.Code)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strconv.Quote(contract.Info.Source) != `"`+expSource+`"` {
|
|
||||||
t.Errorf("Expected \n'%s' got \n'%s'", expSource, strconv.Quote(contract.Info.Source))
|
|
||||||
}
|
|
||||||
|
|
||||||
if contract.Info.Language != expLanguage {
|
|
||||||
t.Errorf("Expected %s got %s", expLanguage, contract.Info.Language)
|
|
||||||
}
|
|
||||||
|
|
||||||
if contract.Info.LanguageVersion != expLanguageVersion {
|
|
||||||
t.Errorf("Expected %s got %s", expLanguageVersion, contract.Info.LanguageVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
if contract.Info.CompilerVersion != expCompilerVersion {
|
|
||||||
t.Errorf("Expected %s got %s", expCompilerVersion, contract.Info.CompilerVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
userdoc, err := json.Marshal(contract.Info.UserDoc)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("expected no error, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
devdoc, err := json.Marshal(contract.Info.DeveloperDoc)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("expected no error, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
abidef, err := json.Marshal(contract.Info.AbiDefinition)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("expected no error, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if string(abidef) != expAbiDefinition {
|
|
||||||
t.Errorf("Expected \n'%s' got \n'%s'", expAbiDefinition, string(abidef))
|
|
||||||
}
|
|
||||||
|
|
||||||
if string(userdoc) != expUserDoc {
|
|
||||||
t.Errorf("Expected \n'%s' got \n'%s'", expUserDoc, string(userdoc))
|
|
||||||
}
|
|
||||||
|
|
||||||
if string(devdoc) != expDeveloperDoc {
|
|
||||||
t.Errorf("Expected %s got %s", expDeveloperDoc, string(devdoc))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// func TestDbStr(t *testing.T) {
|
|
||||||
// jsonput := `{"jsonrpc":"2.0","method":"db_putString","params":["testDB","myKey","myString"],"id":64}`
|
|
||||||
// jsonget := `{"jsonrpc":"2.0","method":"db_getString","params":["testDB","myKey"],"id":64}`
|
|
||||||
// expected := "myString"
|
|
||||||
|
|
||||||
// xeth := &xeth.XEth{}
|
|
||||||
// api := NewEthereumApi(xeth)
|
|
||||||
// var response interface{}
|
|
||||||
|
|
||||||
// var req RpcRequest
|
|
||||||
// json.Unmarshal([]byte(jsonput), &req)
|
|
||||||
// _ = api.GetRequestReply(&req, &response)
|
|
||||||
|
|
||||||
// json.Unmarshal([]byte(jsonget), &req)
|
|
||||||
// _ = api.GetRequestReply(&req, &response)
|
|
||||||
|
|
||||||
// if response.(string) != expected {
|
|
||||||
// t.Errorf("Expected %s got %s", expected, response)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func TestDbHexStr(t *testing.T) {
|
|
||||||
// jsonput := `{"jsonrpc":"2.0","method":"db_putHex","params":["testDB","beefKey","0xbeef"],"id":64}`
|
|
||||||
// jsonget := `{"jsonrpc":"2.0","method":"db_getHex","params":["testDB","beefKey"],"id":64}`
|
|
||||||
// expected := "0xbeef"
|
|
||||||
|
|
||||||
// xeth := &xeth.XEth{}
|
|
||||||
// api := NewEthereumApi(xeth)
|
|
||||||
// defer api.db.Close()
|
|
||||||
// var response interface{}
|
|
||||||
|
|
||||||
// var req RpcRequest
|
|
||||||
// json.Unmarshal([]byte(jsonput), &req)
|
|
||||||
// _ = api.GetRequestReply(&req, &response)
|
|
||||||
|
|
||||||
// json.Unmarshal([]byte(jsonget), &req)
|
|
||||||
// _ = api.GetRequestReply(&req, &response)
|
|
||||||
|
|
||||||
// if response.(string) != expected {
|
|
||||||
// t.Errorf("Expected %s got %s", expected, response)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func TestFilterClose(t *testing.T) {
|
|
||||||
// t.Skip()
|
|
||||||
// api := &EthereumApi{
|
|
||||||
// logs: make(map[int]*logFilter),
|
|
||||||
// messages: make(map[int]*whisperFilter),
|
|
||||||
// quit: make(chan struct{}),
|
|
||||||
// }
|
|
||||||
|
|
||||||
// filterTickerTime = 1
|
|
||||||
// api.logs[0] = &logFilter{}
|
|
||||||
// api.messages[0] = &whisperFilter{}
|
|
||||||
// var wg sync.WaitGroup
|
|
||||||
// wg.Add(1)
|
|
||||||
// go api.start()
|
|
||||||
// go func() {
|
|
||||||
// select {
|
|
||||||
// case <-time.After(500 * time.Millisecond):
|
|
||||||
// api.stop()
|
|
||||||
// wg.Done()
|
|
||||||
// }
|
|
||||||
// }()
|
|
||||||
// wg.Wait()
|
|
||||||
// if len(api.logs) != 0 {
|
|
||||||
// t.Error("expected logs to be empty")
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if len(api.messages) != 0 {
|
|
||||||
// t.Error("expected messages to be empty")
|
|
||||||
// }
|
|
||||||
// }
|
|
1230
rpc/args.go
1230
rpc/args.go
File diff suppressed because it is too large
Load Diff
@ -9,16 +9,32 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/rpc/api"
|
"github.com/ethereum/go-ethereum/rpc/api"
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
jsonrpcver = "2.0"
|
|
||||||
maxHttpSizeReqLength = 1024 * 1024 // 1MB
|
maxHttpSizeReqLength = 1024 * 1024 // 1MB
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// List with all API's which are offered over the in proc interface by default
|
||||||
|
DefaultInProcApis = api.AllApis
|
||||||
|
|
||||||
|
// List with all API's which are offered over the IPC interface by default
|
||||||
|
DefaultIpcApis = api.AllApis
|
||||||
|
|
||||||
|
// List with API's which are offered over thr HTTP/RPC interface by default
|
||||||
|
DefaultHttpRpcApis = strings.Join([]string{
|
||||||
|
api.DbApiName, api.EthApiName, api.NetApiName, api.Web3ApiName,
|
||||||
|
}, ",")
|
||||||
|
)
|
||||||
|
|
||||||
type EthereumClient interface {
|
type EthereumClient interface {
|
||||||
|
// Close underlaying connection
|
||||||
Close()
|
Close()
|
||||||
|
// Send request
|
||||||
Send(interface{}) error
|
Send(interface{}) error
|
||||||
|
// Receive response
|
||||||
Recv() (interface{}, error)
|
Recv() (interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,3 +63,27 @@ func StopHttp() {
|
|||||||
httpListener = nil
|
httpListener = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type httpClient struct {
|
||||||
|
codec codec.ApiCoder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new in process client
|
||||||
|
func NewHttpClient(cfg HttpConfig, codec codec.Codec) *httpClient {
|
||||||
|
return &httpClient{
|
||||||
|
codec: codec.New(nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *httpClient) Close() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *httpClient) Send(req interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *httpClient) Recv() (interface{}, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
@ -90,7 +90,7 @@ func newStoppableHandler(h http.Handler, stop chan struct{}) http.Handler {
|
|||||||
case <-stop:
|
case <-stop:
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
err := fmt.Errorf("RPC service stopped")
|
err := fmt.Errorf("RPC service stopped")
|
||||||
response := shared.NewRpcResponse(-1, jsonrpcver, nil, err)
|
response := shared.NewRpcResponse(-1, api.JsonRpcVersion, nil, err)
|
||||||
httpSend(w, response)
|
httpSend(w, response)
|
||||||
default:
|
default:
|
||||||
h.ServeHTTP(w, r)
|
h.ServeHTTP(w, r)
|
||||||
@ -110,14 +110,14 @@ func httpSend(writer io.Writer, v interface{}) (n int, err error) {
|
|||||||
return writer.Write(payload)
|
return writer.Write(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
func gethHttpHandler(codec codec.Codec, api api.EthereumApi) http.Handler {
|
func gethHttpHandler(codec codec.Codec, a api.EthereumApi) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
// Limit request size to resist DoS
|
// Limit request size to resist DoS
|
||||||
if req.ContentLength > maxHttpSizeReqLength {
|
if req.ContentLength > maxHttpSizeReqLength {
|
||||||
err := fmt.Errorf("Request too large")
|
err := fmt.Errorf("Request too large")
|
||||||
response := shared.NewRpcErrorResponse(-1, jsonrpcver, -32700, err)
|
response := shared.NewRpcErrorResponse(-1, api.JsonRpcVersion, -32700, err)
|
||||||
httpSend(w, &response)
|
httpSend(w, &response)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -126,7 +126,7 @@ func gethHttpHandler(codec codec.Codec, api api.EthereumApi) http.Handler {
|
|||||||
payload, err := ioutil.ReadAll(req.Body)
|
payload, err := ioutil.ReadAll(req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("Could not read request body")
|
err := fmt.Errorf("Could not read request body")
|
||||||
response := shared.NewRpcErrorResponse(-1, jsonrpcver, -32700, err)
|
response := shared.NewRpcErrorResponse(-1, api.JsonRpcVersion, -32700, err)
|
||||||
httpSend(w, &response)
|
httpSend(w, &response)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -134,7 +134,7 @@ func gethHttpHandler(codec codec.Codec, api api.EthereumApi) http.Handler {
|
|||||||
c := codec.New(nil)
|
c := codec.New(nil)
|
||||||
var rpcReq shared.Request
|
var rpcReq shared.Request
|
||||||
if err = c.Decode(payload, &rpcReq); err == nil {
|
if err = c.Decode(payload, &rpcReq); err == nil {
|
||||||
reply, err := api.Execute(&rpcReq)
|
reply, err := a.Execute(&rpcReq)
|
||||||
res := shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
|
res := shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
|
||||||
httpSend(w, &res)
|
httpSend(w, &res)
|
||||||
return
|
return
|
||||||
@ -146,7 +146,7 @@ func gethHttpHandler(codec codec.Codec, api api.EthereumApi) http.Handler {
|
|||||||
resCount := 0
|
resCount := 0
|
||||||
|
|
||||||
for i, rpcReq := range reqBatch {
|
for i, rpcReq := range reqBatch {
|
||||||
reply, err := api.Execute(&rpcReq)
|
reply, err := a.Execute(&rpcReq)
|
||||||
if rpcReq.Id != nil { // this leaves nil entries in the response batch for later removal
|
if rpcReq.Id != nil { // this leaves nil entries in the response batch for later removal
|
||||||
resBatch[i] = shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
|
resBatch[i] = shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
|
||||||
resCount += 1
|
resCount += 1
|
||||||
@ -161,7 +161,7 @@ func gethHttpHandler(codec codec.Codec, api api.EthereumApi) http.Handler {
|
|||||||
|
|
||||||
// invalid request
|
// invalid request
|
||||||
err = fmt.Errorf("Could not decode request")
|
err = fmt.Errorf("Could not decode request")
|
||||||
res := shared.NewRpcErrorResponse(-1, jsonrpcver, -32600, err)
|
res := shared.NewRpcErrorResponse(-1, api.JsonRpcVersion, -32600, err)
|
||||||
httpSend(w, res)
|
httpSend(w, res)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
53
rpc/comms/inproc.go
Normal file
53
rpc/comms/inproc.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package comms
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/rpc/api"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||||
|
"github.com/ethereum/go-ethereum/xeth"
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InProcClient struct {
|
||||||
|
api api.EthereumApi
|
||||||
|
codec codec.Codec
|
||||||
|
lastId interface{}
|
||||||
|
lastJsonrpc string
|
||||||
|
lastErr error
|
||||||
|
lastRes interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new in process client
|
||||||
|
func NewInProcClient(codec codec.Codec) *InProcClient {
|
||||||
|
return &InProcClient{
|
||||||
|
codec: codec,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *InProcClient) Close() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to setup api support
|
||||||
|
func (self *InProcClient) Initialize(xeth *xeth.XEth, eth *eth.Ethereum) {
|
||||||
|
if apis, err := api.ParseApiString(api.AllApis, self.codec, xeth, eth); err == nil {
|
||||||
|
self.api = api.Merge(apis...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *InProcClient) Send(req interface{}) error {
|
||||||
|
if r, ok := req.(*shared.Request); ok {
|
||||||
|
self.lastId = r.Id
|
||||||
|
self.lastJsonrpc = r.Jsonrpc
|
||||||
|
self.lastRes, self.lastErr = self.api.Execute(r)
|
||||||
|
return self.lastErr
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Invalid request (%T)", req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *InProcClient) Recv() (interface{}, error) {
|
||||||
|
return self.lastRes, self.lastErr
|
||||||
|
//return *shared.NewRpcResponse(self.lastId, self.lastJsonrpc, self.lastRes, self.lastErr), nil
|
||||||
|
}
|
@ -10,19 +10,19 @@ type IpcConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ipcClient struct {
|
type ipcClient struct {
|
||||||
c codec.ApiCoder
|
codec codec.ApiCoder
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ipcClient) Close() {
|
func (self *ipcClient) Close() {
|
||||||
self.c.Close()
|
self.codec.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ipcClient) Send(req interface{}) error {
|
func (self *ipcClient) Send(req interface{}) error {
|
||||||
return self.c.WriteResponse(req)
|
return self.codec.WriteResponse(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ipcClient) Recv() (interface{}, error) {
|
func (self *ipcClient) Recv() (interface{}, error) {
|
||||||
return self.c.ReadResponse()
|
return self.codec.ReadResponse()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new IPC client, UNIX domain socket on posix, named pipe on Windows
|
// Create a new IPC client, UNIX domain socket on posix, named pipe on Windows
|
||||||
|
163
rpc/http.go
163
rpc/http.go
@ -1,163 +0,0 @@
|
|||||||
package rpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/xeth"
|
|
||||||
"github.com/rs/cors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var rpclistener *stoppableTCPListener
|
|
||||||
|
|
||||||
const (
|
|
||||||
jsonrpcver = "2.0"
|
|
||||||
maxSizeReqLength = 1024 * 1024 // 1MB
|
|
||||||
)
|
|
||||||
|
|
||||||
func Start(pipe *xeth.XEth, config RpcConfig) error {
|
|
||||||
if rpclistener != nil {
|
|
||||||
if fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort) != rpclistener.Addr().String() {
|
|
||||||
return fmt.Errorf("RPC service already running on %s ", rpclistener.Addr().String())
|
|
||||||
}
|
|
||||||
return nil // RPC service already running on given host/port
|
|
||||||
}
|
|
||||||
|
|
||||||
l, err := newStoppableTCPListener(fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort))
|
|
||||||
if err != nil {
|
|
||||||
glog.V(logger.Error).Infof("Can't listen on %s:%d: %v", config.ListenAddress, config.ListenPort, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rpclistener = l
|
|
||||||
|
|
||||||
var handler http.Handler
|
|
||||||
if len(config.CorsDomain) > 0 {
|
|
||||||
var opts cors.Options
|
|
||||||
opts.AllowedMethods = []string{"POST"}
|
|
||||||
opts.AllowedOrigins = strings.Split(config.CorsDomain, " ")
|
|
||||||
|
|
||||||
c := cors.New(opts)
|
|
||||||
handler = newStoppableHandler(c.Handler(JSONRPC(pipe)), l.stop)
|
|
||||||
} else {
|
|
||||||
handler = newStoppableHandler(JSONRPC(pipe), l.stop)
|
|
||||||
}
|
|
||||||
|
|
||||||
go http.Serve(l, handler)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Stop() error {
|
|
||||||
if rpclistener != nil {
|
|
||||||
rpclistener.Stop()
|
|
||||||
rpclistener = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSONRPC returns a handler that implements the Ethereum JSON-RPC API.
|
|
||||||
func JSONRPC(pipe *xeth.XEth) http.Handler {
|
|
||||||
api := NewEthereumApi(pipe)
|
|
||||||
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
// Limit request size to resist DoS
|
|
||||||
if req.ContentLength > maxSizeReqLength {
|
|
||||||
jsonerr := &RpcErrorObject{-32700, "Request too large"}
|
|
||||||
send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read request body
|
|
||||||
defer req.Body.Close()
|
|
||||||
body, err := ioutil.ReadAll(req.Body)
|
|
||||||
if err != nil {
|
|
||||||
jsonerr := &RpcErrorObject{-32700, "Could not read request body"}
|
|
||||||
send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to parse the request as a single
|
|
||||||
var reqSingle RpcRequest
|
|
||||||
if err := json.Unmarshal(body, &reqSingle); err == nil {
|
|
||||||
response := RpcResponse(api, &reqSingle)
|
|
||||||
if reqSingle.Id != nil {
|
|
||||||
send(w, &response)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to parse the request to batch
|
|
||||||
var reqBatch []RpcRequest
|
|
||||||
if err := json.Unmarshal(body, &reqBatch); err == nil {
|
|
||||||
// Build response batch
|
|
||||||
resBatch := make([]*interface{}, len(reqBatch))
|
|
||||||
resCount := 0
|
|
||||||
|
|
||||||
for i, request := range reqBatch {
|
|
||||||
response := RpcResponse(api, &request)
|
|
||||||
// this leaves nil entries in the response batch for later removal
|
|
||||||
if request.Id != nil {
|
|
||||||
resBatch[i] = response
|
|
||||||
resCount = resCount + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// make response omitting nil entries
|
|
||||||
respBatchComp := make([]*interface{}, resCount)
|
|
||||||
for _, v := range resBatch {
|
|
||||||
if v != nil {
|
|
||||||
respBatchComp[len(respBatchComp)-resCount] = v
|
|
||||||
resCount = resCount - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
send(w, respBatchComp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not a batch or single request, error
|
|
||||||
jsonerr := &RpcErrorObject{-32600, "Could not decode request"}
|
|
||||||
send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func RpcResponse(api *EthereumApi, request *RpcRequest) *interface{} {
|
|
||||||
var reply, response interface{}
|
|
||||||
reserr := api.GetRequestReply(request, &reply)
|
|
||||||
switch reserr.(type) {
|
|
||||||
case nil:
|
|
||||||
response = &RpcSuccessResponse{Jsonrpc: jsonrpcver, Id: request.Id, Result: reply}
|
|
||||||
case *NotImplementedError, *NotAvailableError:
|
|
||||||
jsonerr := &RpcErrorObject{-32601, reserr.Error()}
|
|
||||||
response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
|
|
||||||
case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError:
|
|
||||||
jsonerr := &RpcErrorObject{-32602, reserr.Error()}
|
|
||||||
response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
|
|
||||||
default:
|
|
||||||
jsonerr := &RpcErrorObject{-32603, reserr.Error()}
|
|
||||||
response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
|
|
||||||
}
|
|
||||||
|
|
||||||
glog.V(logger.Detail).Infof("Generated response: %T %s", response, response)
|
|
||||||
return &response
|
|
||||||
}
|
|
||||||
|
|
||||||
func send(writer io.Writer, v interface{}) (n int, err error) {
|
|
||||||
var payload []byte
|
|
||||||
payload, err = json.MarshalIndent(v, "", "\t")
|
|
||||||
if err != nil {
|
|
||||||
glog.V(logger.Error).Infoln("Error marshalling JSON", err)
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
glog.V(logger.Detail).Infof("Sending payload: %s", payload)
|
|
||||||
|
|
||||||
return writer.Write(payload)
|
|
||||||
}
|
|
42
rpc/jeth.go
42
rpc/jeth.go
@ -1,31 +1,28 @@
|
|||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/jsre"
|
"github.com/ethereum/go-ethereum/jsre"
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
"github.com/ethereum/go-ethereum/rpc/api"
|
||||||
"github.com/ethereum/go-ethereum/rpc/comms"
|
"github.com/ethereum/go-ethereum/rpc/comms"
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||||
"github.com/robertkrimen/otto"
|
"github.com/robertkrimen/otto"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Jeth struct {
|
type Jeth struct {
|
||||||
ethApi *EthereumApi
|
ethApi api.EthereumApi
|
||||||
re *jsre.JSRE
|
re *jsre.JSRE
|
||||||
ipcpath string
|
client comms.EthereumClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJeth(ethApi *EthereumApi, re *jsre.JSRE, ipcpath string) *Jeth {
|
func NewJeth(ethApi api.EthereumApi, re *jsre.JSRE, client comms.EthereumClient) *Jeth {
|
||||||
return &Jeth{ethApi, re, ipcpath}
|
return &Jeth{ethApi, re, client}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id interface{}) (response otto.Value) {
|
func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id interface{}) (response otto.Value) {
|
||||||
rpcerr := &RpcErrorObject{code, msg}
|
rpcerr := &shared.ErrorObject{code, msg}
|
||||||
call.Otto.Set("ret_jsonrpc", jsonrpcver)
|
call.Otto.Set("ret_jsonrpc", api.JsonRpcVersion)
|
||||||
call.Otto.Set("ret_id", id)
|
call.Otto.Set("ret_id", id)
|
||||||
call.Otto.Set("ret_error", rpcerr)
|
call.Otto.Set("ret_error", rpcerr)
|
||||||
response, _ = call.Otto.Run(`
|
response, _ = call.Otto.Run(`
|
||||||
@ -34,6 +31,7 @@ func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id interface
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) {
|
func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) {
|
||||||
reqif, err := call.Argument(0).Export()
|
reqif, err := call.Argument(0).Export()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -41,11 +39,11 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jsonreq, err := json.Marshal(reqif)
|
jsonreq, err := json.Marshal(reqif)
|
||||||
var reqs []RpcRequest
|
var reqs []shared.Request
|
||||||
batch := true
|
batch := true
|
||||||
err = json.Unmarshal(jsonreq, &reqs)
|
err = json.Unmarshal(jsonreq, &reqs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reqs = make([]RpcRequest, 1)
|
reqs = make([]shared.Request, 1)
|
||||||
err = json.Unmarshal(jsonreq, &reqs[0])
|
err = json.Unmarshal(jsonreq, &reqs[0])
|
||||||
batch = false
|
batch = false
|
||||||
}
|
}
|
||||||
@ -55,12 +53,18 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) {
|
|||||||
|
|
||||||
for i, req := range reqs {
|
for i, req := range reqs {
|
||||||
var respif interface{}
|
var respif interface{}
|
||||||
err = self.ethApi.GetRequestReply(&req, &respif)
|
err := self.client.Send(&req)//self.ethApi.Execute(&req)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error request:", err)
|
||||||
|
return self.err(call, -32603, err.Error(), req.Id)
|
||||||
|
}
|
||||||
|
respif, err = self.client.Recv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error response:", err)
|
fmt.Println("Error response:", err)
|
||||||
return self.err(call, -32603, err.Error(), req.Id)
|
return self.err(call, -32603, err.Error(), req.Id)
|
||||||
}
|
}
|
||||||
call.Otto.Set("ret_jsonrpc", jsonrpcver)
|
|
||||||
|
call.Otto.Set("ret_jsonrpc", api.JsonRpcVersion)
|
||||||
call.Otto.Set("ret_id", req.Id)
|
call.Otto.Set("ret_id", req.Id)
|
||||||
|
|
||||||
res, _ := json.Marshal(respif)
|
res, _ := json.Marshal(respif)
|
||||||
@ -88,6 +92,7 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func (self *Jeth) SendIpc(call otto.FunctionCall) (response otto.Value) {
|
func (self *Jeth) SendIpc(call otto.FunctionCall) (response otto.Value) {
|
||||||
reqif, err := call.Argument(0).Export()
|
reqif, err := call.Argument(0).Export()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -102,11 +107,11 @@ func (self *Jeth) SendIpc(call otto.FunctionCall) (response otto.Value) {
|
|||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
jsonreq, err := json.Marshal(reqif)
|
jsonreq, err := json.Marshal(reqif)
|
||||||
var reqs []RpcRequest
|
var reqs []shared.Request
|
||||||
batch := true
|
batch := true
|
||||||
err = json.Unmarshal(jsonreq, &reqs)
|
err = json.Unmarshal(jsonreq, &reqs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reqs = make([]RpcRequest, 1)
|
reqs = make([]shared.Request, 1)
|
||||||
err = json.Unmarshal(jsonreq, &reqs[0])
|
err = json.Unmarshal(jsonreq, &reqs[0])
|
||||||
batch = false
|
batch = false
|
||||||
}
|
}
|
||||||
@ -169,3 +174,4 @@ func (self *Jeth) SendIpc(call otto.FunctionCall) (response otto.Value) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
316
rpc/responses.go
316
rpc/responses.go
@ -1,316 +0,0 @@
|
|||||||
package rpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type BlockRes struct {
|
|
||||||
fullTx bool
|
|
||||||
|
|
||||||
BlockNumber *hexnum `json:"number"`
|
|
||||||
BlockHash *hexdata `json:"hash"`
|
|
||||||
ParentHash *hexdata `json:"parentHash"`
|
|
||||||
Nonce *hexdata `json:"nonce"`
|
|
||||||
Sha3Uncles *hexdata `json:"sha3Uncles"`
|
|
||||||
LogsBloom *hexdata `json:"logsBloom"`
|
|
||||||
TransactionRoot *hexdata `json:"transactionsRoot"`
|
|
||||||
StateRoot *hexdata `json:"stateRoot"`
|
|
||||||
Miner *hexdata `json:"miner"`
|
|
||||||
Difficulty *hexnum `json:"difficulty"`
|
|
||||||
TotalDifficulty *hexnum `json:"totalDifficulty"`
|
|
||||||
Size *hexnum `json:"size"`
|
|
||||||
ExtraData *hexdata `json:"extraData"`
|
|
||||||
GasLimit *hexnum `json:"gasLimit"`
|
|
||||||
GasUsed *hexnum `json:"gasUsed"`
|
|
||||||
UnixTimestamp *hexnum `json:"timestamp"`
|
|
||||||
Transactions []*TransactionRes `json:"transactions"`
|
|
||||||
Uncles []*UncleRes `json:"uncles"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BlockRes) MarshalJSON() ([]byte, error) {
|
|
||||||
if b.fullTx {
|
|
||||||
var ext struct {
|
|
||||||
BlockNumber *hexnum `json:"number"`
|
|
||||||
BlockHash *hexdata `json:"hash"`
|
|
||||||
ParentHash *hexdata `json:"parentHash"`
|
|
||||||
Nonce *hexdata `json:"nonce"`
|
|
||||||
Sha3Uncles *hexdata `json:"sha3Uncles"`
|
|
||||||
LogsBloom *hexdata `json:"logsBloom"`
|
|
||||||
TransactionRoot *hexdata `json:"transactionsRoot"`
|
|
||||||
StateRoot *hexdata `json:"stateRoot"`
|
|
||||||
Miner *hexdata `json:"miner"`
|
|
||||||
Difficulty *hexnum `json:"difficulty"`
|
|
||||||
TotalDifficulty *hexnum `json:"totalDifficulty"`
|
|
||||||
Size *hexnum `json:"size"`
|
|
||||||
ExtraData *hexdata `json:"extraData"`
|
|
||||||
GasLimit *hexnum `json:"gasLimit"`
|
|
||||||
GasUsed *hexnum `json:"gasUsed"`
|
|
||||||
UnixTimestamp *hexnum `json:"timestamp"`
|
|
||||||
Transactions []*TransactionRes `json:"transactions"`
|
|
||||||
Uncles []*hexdata `json:"uncles"`
|
|
||||||
}
|
|
||||||
|
|
||||||
ext.BlockNumber = b.BlockNumber
|
|
||||||
ext.BlockHash = b.BlockHash
|
|
||||||
ext.ParentHash = b.ParentHash
|
|
||||||
ext.Nonce = b.Nonce
|
|
||||||
ext.Sha3Uncles = b.Sha3Uncles
|
|
||||||
ext.LogsBloom = b.LogsBloom
|
|
||||||
ext.TransactionRoot = b.TransactionRoot
|
|
||||||
ext.StateRoot = b.StateRoot
|
|
||||||
ext.Miner = b.Miner
|
|
||||||
ext.Difficulty = b.Difficulty
|
|
||||||
ext.TotalDifficulty = b.TotalDifficulty
|
|
||||||
ext.Size = b.Size
|
|
||||||
ext.ExtraData = b.ExtraData
|
|
||||||
ext.GasLimit = b.GasLimit
|
|
||||||
ext.GasUsed = b.GasUsed
|
|
||||||
ext.UnixTimestamp = b.UnixTimestamp
|
|
||||||
ext.Transactions = b.Transactions
|
|
||||||
ext.Uncles = make([]*hexdata, len(b.Uncles))
|
|
||||||
for i, u := range b.Uncles {
|
|
||||||
ext.Uncles[i] = u.BlockHash
|
|
||||||
}
|
|
||||||
return json.Marshal(ext)
|
|
||||||
} else {
|
|
||||||
var ext struct {
|
|
||||||
BlockNumber *hexnum `json:"number"`
|
|
||||||
BlockHash *hexdata `json:"hash"`
|
|
||||||
ParentHash *hexdata `json:"parentHash"`
|
|
||||||
Nonce *hexdata `json:"nonce"`
|
|
||||||
Sha3Uncles *hexdata `json:"sha3Uncles"`
|
|
||||||
LogsBloom *hexdata `json:"logsBloom"`
|
|
||||||
TransactionRoot *hexdata `json:"transactionsRoot"`
|
|
||||||
StateRoot *hexdata `json:"stateRoot"`
|
|
||||||
Miner *hexdata `json:"miner"`
|
|
||||||
Difficulty *hexnum `json:"difficulty"`
|
|
||||||
TotalDifficulty *hexnum `json:"totalDifficulty"`
|
|
||||||
Size *hexnum `json:"size"`
|
|
||||||
ExtraData *hexdata `json:"extraData"`
|
|
||||||
GasLimit *hexnum `json:"gasLimit"`
|
|
||||||
GasUsed *hexnum `json:"gasUsed"`
|
|
||||||
UnixTimestamp *hexnum `json:"timestamp"`
|
|
||||||
Transactions []*hexdata `json:"transactions"`
|
|
||||||
Uncles []*hexdata `json:"uncles"`
|
|
||||||
}
|
|
||||||
|
|
||||||
ext.BlockNumber = b.BlockNumber
|
|
||||||
ext.BlockHash = b.BlockHash
|
|
||||||
ext.ParentHash = b.ParentHash
|
|
||||||
ext.Nonce = b.Nonce
|
|
||||||
ext.Sha3Uncles = b.Sha3Uncles
|
|
||||||
ext.LogsBloom = b.LogsBloom
|
|
||||||
ext.TransactionRoot = b.TransactionRoot
|
|
||||||
ext.StateRoot = b.StateRoot
|
|
||||||
ext.Miner = b.Miner
|
|
||||||
ext.Difficulty = b.Difficulty
|
|
||||||
ext.TotalDifficulty = b.TotalDifficulty
|
|
||||||
ext.Size = b.Size
|
|
||||||
ext.ExtraData = b.ExtraData
|
|
||||||
ext.GasLimit = b.GasLimit
|
|
||||||
ext.GasUsed = b.GasUsed
|
|
||||||
ext.UnixTimestamp = b.UnixTimestamp
|
|
||||||
ext.Transactions = make([]*hexdata, len(b.Transactions))
|
|
||||||
for i, tx := range b.Transactions {
|
|
||||||
ext.Transactions[i] = tx.Hash
|
|
||||||
}
|
|
||||||
ext.Uncles = make([]*hexdata, len(b.Uncles))
|
|
||||||
for i, u := range b.Uncles {
|
|
||||||
ext.Uncles[i] = u.BlockHash
|
|
||||||
}
|
|
||||||
return json.Marshal(ext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBlockRes(block *types.Block, fullTx bool) *BlockRes {
|
|
||||||
if block == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
res := new(BlockRes)
|
|
||||||
res.fullTx = fullTx
|
|
||||||
res.BlockNumber = newHexNum(block.Number())
|
|
||||||
res.BlockHash = newHexData(block.Hash())
|
|
||||||
res.ParentHash = newHexData(block.ParentHash())
|
|
||||||
res.Nonce = newHexData(block.Nonce())
|
|
||||||
res.Sha3Uncles = newHexData(block.Header().UncleHash)
|
|
||||||
res.LogsBloom = newHexData(block.Bloom())
|
|
||||||
res.TransactionRoot = newHexData(block.Header().TxHash)
|
|
||||||
res.StateRoot = newHexData(block.Root())
|
|
||||||
res.Miner = newHexData(block.Header().Coinbase)
|
|
||||||
res.Difficulty = newHexNum(block.Difficulty())
|
|
||||||
res.TotalDifficulty = newHexNum(block.Td)
|
|
||||||
res.Size = newHexNum(block.Size().Int64())
|
|
||||||
res.ExtraData = newHexData(block.Header().Extra)
|
|
||||||
res.GasLimit = newHexNum(block.GasLimit())
|
|
||||||
res.GasUsed = newHexNum(block.GasUsed())
|
|
||||||
res.UnixTimestamp = newHexNum(block.Time())
|
|
||||||
|
|
||||||
res.Transactions = make([]*TransactionRes, len(block.Transactions()))
|
|
||||||
for i, tx := range block.Transactions() {
|
|
||||||
res.Transactions[i] = NewTransactionRes(tx)
|
|
||||||
res.Transactions[i].BlockHash = res.BlockHash
|
|
||||||
res.Transactions[i].BlockNumber = res.BlockNumber
|
|
||||||
res.Transactions[i].TxIndex = newHexNum(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
res.Uncles = make([]*UncleRes, len(block.Uncles()))
|
|
||||||
for i, uncle := range block.Uncles() {
|
|
||||||
res.Uncles[i] = NewUncleRes(uncle)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
type TransactionRes struct {
|
|
||||||
Hash *hexdata `json:"hash"`
|
|
||||||
Nonce *hexnum `json:"nonce"`
|
|
||||||
BlockHash *hexdata `json:"blockHash"`
|
|
||||||
BlockNumber *hexnum `json:"blockNumber"`
|
|
||||||
TxIndex *hexnum `json:"transactionIndex"`
|
|
||||||
From *hexdata `json:"from"`
|
|
||||||
To *hexdata `json:"to"`
|
|
||||||
Value *hexnum `json:"value"`
|
|
||||||
Gas *hexnum `json:"gas"`
|
|
||||||
GasPrice *hexnum `json:"gasPrice"`
|
|
||||||
Input *hexdata `json:"input"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTransactionRes(tx *types.Transaction) *TransactionRes {
|
|
||||||
if tx == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var v = new(TransactionRes)
|
|
||||||
v.Hash = newHexData(tx.Hash())
|
|
||||||
v.Nonce = newHexNum(tx.Nonce())
|
|
||||||
// v.BlockHash =
|
|
||||||
// v.BlockNumber =
|
|
||||||
// v.TxIndex =
|
|
||||||
from, _ := tx.From()
|
|
||||||
v.From = newHexData(from)
|
|
||||||
v.To = newHexData(tx.To())
|
|
||||||
v.Value = newHexNum(tx.Value())
|
|
||||||
v.Gas = newHexNum(tx.Gas())
|
|
||||||
v.GasPrice = newHexNum(tx.GasPrice())
|
|
||||||
v.Input = newHexData(tx.Data())
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
type UncleRes struct {
|
|
||||||
BlockNumber *hexnum `json:"number"`
|
|
||||||
BlockHash *hexdata `json:"hash"`
|
|
||||||
ParentHash *hexdata `json:"parentHash"`
|
|
||||||
Nonce *hexdata `json:"nonce"`
|
|
||||||
Sha3Uncles *hexdata `json:"sha3Uncles"`
|
|
||||||
ReceiptHash *hexdata `json:"receiptHash"`
|
|
||||||
LogsBloom *hexdata `json:"logsBloom"`
|
|
||||||
TransactionRoot *hexdata `json:"transactionsRoot"`
|
|
||||||
StateRoot *hexdata `json:"stateRoot"`
|
|
||||||
Miner *hexdata `json:"miner"`
|
|
||||||
Difficulty *hexnum `json:"difficulty"`
|
|
||||||
ExtraData *hexdata `json:"extraData"`
|
|
||||||
GasLimit *hexnum `json:"gasLimit"`
|
|
||||||
GasUsed *hexnum `json:"gasUsed"`
|
|
||||||
UnixTimestamp *hexnum `json:"timestamp"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewUncleRes(h *types.Header) *UncleRes {
|
|
||||||
if h == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var v = new(UncleRes)
|
|
||||||
v.BlockNumber = newHexNum(h.Number)
|
|
||||||
v.BlockHash = newHexData(h.Hash())
|
|
||||||
v.ParentHash = newHexData(h.ParentHash)
|
|
||||||
v.Sha3Uncles = newHexData(h.UncleHash)
|
|
||||||
v.Nonce = newHexData(h.Nonce[:])
|
|
||||||
v.LogsBloom = newHexData(h.Bloom)
|
|
||||||
v.TransactionRoot = newHexData(h.TxHash)
|
|
||||||
v.StateRoot = newHexData(h.Root)
|
|
||||||
v.Miner = newHexData(h.Coinbase)
|
|
||||||
v.Difficulty = newHexNum(h.Difficulty)
|
|
||||||
v.ExtraData = newHexData(h.Extra)
|
|
||||||
v.GasLimit = newHexNum(h.GasLimit)
|
|
||||||
v.GasUsed = newHexNum(h.GasUsed)
|
|
||||||
v.UnixTimestamp = newHexNum(h.Time)
|
|
||||||
v.ReceiptHash = newHexData(h.ReceiptHash)
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// type FilterLogRes struct {
|
|
||||||
// Hash string `json:"hash"`
|
|
||||||
// Address string `json:"address"`
|
|
||||||
// Data string `json:"data"`
|
|
||||||
// BlockNumber string `json:"blockNumber"`
|
|
||||||
// TransactionHash string `json:"transactionHash"`
|
|
||||||
// BlockHash string `json:"blockHash"`
|
|
||||||
// TransactionIndex string `json:"transactionIndex"`
|
|
||||||
// LogIndex string `json:"logIndex"`
|
|
||||||
// }
|
|
||||||
|
|
||||||
// type FilterWhisperRes struct {
|
|
||||||
// Hash string `json:"hash"`
|
|
||||||
// From string `json:"from"`
|
|
||||||
// To string `json:"to"`
|
|
||||||
// Expiry string `json:"expiry"`
|
|
||||||
// Sent string `json:"sent"`
|
|
||||||
// Ttl string `json:"ttl"`
|
|
||||||
// Topics string `json:"topics"`
|
|
||||||
// Payload string `json:"payload"`
|
|
||||||
// WorkProved string `json:"workProved"`
|
|
||||||
// }
|
|
||||||
|
|
||||||
type LogRes struct {
|
|
||||||
Address *hexdata `json:"address"`
|
|
||||||
Topics []*hexdata `json:"topics"`
|
|
||||||
Data *hexdata `json:"data"`
|
|
||||||
BlockNumber *hexnum `json:"blockNumber"`
|
|
||||||
LogIndex *hexnum `json:"logIndex"`
|
|
||||||
BlockHash *hexdata `json:"blockHash"`
|
|
||||||
TransactionHash *hexdata `json:"transactionHash"`
|
|
||||||
TransactionIndex *hexnum `json:"transactionIndex"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLogRes(log *state.Log) LogRes {
|
|
||||||
var l LogRes
|
|
||||||
l.Topics = make([]*hexdata, len(log.Topics))
|
|
||||||
for j, topic := range log.Topics {
|
|
||||||
l.Topics[j] = newHexData(topic)
|
|
||||||
}
|
|
||||||
l.Address = newHexData(log.Address)
|
|
||||||
l.Data = newHexData(log.Data)
|
|
||||||
l.BlockNumber = newHexNum(log.Number)
|
|
||||||
l.LogIndex = newHexNum(log.Index)
|
|
||||||
l.TransactionHash = newHexData(log.TxHash)
|
|
||||||
l.TransactionIndex = newHexNum(log.TxIndex)
|
|
||||||
l.BlockHash = newHexData(log.BlockHash)
|
|
||||||
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLogsRes(logs state.Logs) (ls []LogRes) {
|
|
||||||
ls = make([]LogRes, len(logs))
|
|
||||||
|
|
||||||
for i, log := range logs {
|
|
||||||
ls[i] = NewLogRes(log)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHashesRes(hs []common.Hash) []string {
|
|
||||||
hashes := make([]string, len(hs))
|
|
||||||
|
|
||||||
for i, hash := range hs {
|
|
||||||
hashes[i] = hash.Hex()
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashes
|
|
||||||
}
|
|
@ -1,295 +0,0 @@
|
|||||||
package rpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
reHash = `"0x[0-9a-f]{64}"` // 32 bytes
|
|
||||||
reHashOpt = `"(0x[0-9a-f]{64})"|null` // 32 bytes or null
|
|
||||||
reAddress = `"0x[0-9a-f]{40}"` // 20 bytes
|
|
||||||
reAddressOpt = `"0x[0-9a-f]{40}"|null` // 20 bytes or null
|
|
||||||
reNum = `"0x([1-9a-f][0-9a-f]{0,15})|0"` // must not have left-padded zeros
|
|
||||||
reNumNonZero = `"0x([1-9a-f][0-9a-f]{0,15})"` // non-zero required must not have left-padded zeros
|
|
||||||
reNumOpt = `"0x([1-9a-f][0-9a-f]{0,15})|0"|null` // must not have left-padded zeros or null
|
|
||||||
reData = `"0x[0-9a-f]*"` // can be "empty"
|
|
||||||
// reListHash = `[("\w":"0x[0-9a-f]{64}",?)*]`
|
|
||||||
// reListObj = `[("\w":(".+"|null),?)*]`
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewBlockRes(t *testing.T) {
|
|
||||||
tests := map[string]string{
|
|
||||||
"number": reNum,
|
|
||||||
"hash": reHash,
|
|
||||||
"parentHash": reHash,
|
|
||||||
"nonce": reData,
|
|
||||||
"sha3Uncles": reHash,
|
|
||||||
"logsBloom": reData,
|
|
||||||
"transactionsRoot": reHash,
|
|
||||||
"stateRoot": reHash,
|
|
||||||
"miner": reAddress,
|
|
||||||
"difficulty": `"0x1"`,
|
|
||||||
"totalDifficulty": reNum,
|
|
||||||
"size": reNumNonZero,
|
|
||||||
"extraData": reData,
|
|
||||||
"gasLimit": reNum,
|
|
||||||
// "minGasPrice": "0x",
|
|
||||||
"gasUsed": reNum,
|
|
||||||
"timestamp": reNum,
|
|
||||||
// "transactions": reListHash,
|
|
||||||
// "uncles": reListHash,
|
|
||||||
}
|
|
||||||
|
|
||||||
block := makeBlock()
|
|
||||||
v := NewBlockRes(block, false)
|
|
||||||
j, _ := json.Marshal(v)
|
|
||||||
|
|
||||||
for k, re := range tests {
|
|
||||||
match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j))
|
|
||||||
if !match {
|
|
||||||
t.Error(fmt.Sprintf("%s output json does not match format %s. Got %s", k, re, j))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewBlockResTxFull(t *testing.T) {
|
|
||||||
tests := map[string]string{
|
|
||||||
"number": reNum,
|
|
||||||
"hash": reHash,
|
|
||||||
"parentHash": reHash,
|
|
||||||
"nonce": reData,
|
|
||||||
"sha3Uncles": reHash,
|
|
||||||
"logsBloom": reData,
|
|
||||||
"transactionsRoot": reHash,
|
|
||||||
"stateRoot": reHash,
|
|
||||||
"miner": reAddress,
|
|
||||||
"difficulty": `"0x1"`,
|
|
||||||
"totalDifficulty": reNum,
|
|
||||||
"size": reNumNonZero,
|
|
||||||
"extraData": reData,
|
|
||||||
"gasLimit": reNum,
|
|
||||||
// "minGasPrice": "0x",
|
|
||||||
"gasUsed": reNum,
|
|
||||||
"timestamp": reNum,
|
|
||||||
// "transactions": reListHash,
|
|
||||||
// "uncles": reListHash,
|
|
||||||
}
|
|
||||||
|
|
||||||
block := makeBlock()
|
|
||||||
v := NewBlockRes(block, true)
|
|
||||||
j, _ := json.Marshal(v)
|
|
||||||
|
|
||||||
for k, re := range tests {
|
|
||||||
match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j))
|
|
||||||
if !match {
|
|
||||||
t.Error(fmt.Sprintf("%s output json does not match format %s. Got %s", k, re, j))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBlockNil(t *testing.T) {
|
|
||||||
var block *types.Block
|
|
||||||
block = nil
|
|
||||||
u := NewBlockRes(block, false)
|
|
||||||
j, _ := json.Marshal(u)
|
|
||||||
if string(j) != "null" {
|
|
||||||
t.Errorf("Expected null but got %v", string(j))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewTransactionRes(t *testing.T) {
|
|
||||||
to := common.HexToAddress("0x02")
|
|
||||||
amount := big.NewInt(1)
|
|
||||||
gasAmount := big.NewInt(1)
|
|
||||||
gasPrice := big.NewInt(1)
|
|
||||||
data := []byte{1, 2, 3}
|
|
||||||
tx := types.NewTransactionMessage(to, amount, gasAmount, gasPrice, data)
|
|
||||||
|
|
||||||
tests := map[string]string{
|
|
||||||
"hash": reHash,
|
|
||||||
"nonce": reNum,
|
|
||||||
"blockHash": reHashOpt,
|
|
||||||
"blockNum": reNumOpt,
|
|
||||||
"transactionIndex": reNumOpt,
|
|
||||||
"from": reAddress,
|
|
||||||
"to": reAddressOpt,
|
|
||||||
"value": reNum,
|
|
||||||
"gas": reNum,
|
|
||||||
"gasPrice": reNum,
|
|
||||||
"input": reData,
|
|
||||||
}
|
|
||||||
|
|
||||||
v := NewTransactionRes(tx)
|
|
||||||
v.BlockHash = newHexData(common.HexToHash("0x030201"))
|
|
||||||
v.BlockNumber = newHexNum(5)
|
|
||||||
v.TxIndex = newHexNum(0)
|
|
||||||
j, _ := json.Marshal(v)
|
|
||||||
for k, re := range tests {
|
|
||||||
match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j))
|
|
||||||
if !match {
|
|
||||||
t.Error(fmt.Sprintf("`%s` output json does not match format %s. Source %s", k, re, j))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTransactionNil(t *testing.T) {
|
|
||||||
var tx *types.Transaction
|
|
||||||
tx = nil
|
|
||||||
u := NewTransactionRes(tx)
|
|
||||||
j, _ := json.Marshal(u)
|
|
||||||
if string(j) != "null" {
|
|
||||||
t.Errorf("Expected null but got %v", string(j))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewUncleRes(t *testing.T) {
|
|
||||||
header := makeHeader()
|
|
||||||
u := NewUncleRes(header)
|
|
||||||
tests := map[string]string{
|
|
||||||
"number": reNum,
|
|
||||||
"hash": reHash,
|
|
||||||
"parentHash": reHash,
|
|
||||||
"nonce": reData,
|
|
||||||
"sha3Uncles": reHash,
|
|
||||||
"receiptHash": reHash,
|
|
||||||
"transactionsRoot": reHash,
|
|
||||||
"stateRoot": reHash,
|
|
||||||
"miner": reAddress,
|
|
||||||
"difficulty": reNum,
|
|
||||||
"extraData": reData,
|
|
||||||
"gasLimit": reNum,
|
|
||||||
"gasUsed": reNum,
|
|
||||||
"timestamp": reNum,
|
|
||||||
}
|
|
||||||
|
|
||||||
j, _ := json.Marshal(u)
|
|
||||||
for k, re := range tests {
|
|
||||||
match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j))
|
|
||||||
if !match {
|
|
||||||
t.Error(fmt.Sprintf("`%s` output json does not match format %s. Source %s", k, re, j))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUncleNil(t *testing.T) {
|
|
||||||
var header *types.Header
|
|
||||||
header = nil
|
|
||||||
u := NewUncleRes(header)
|
|
||||||
j, _ := json.Marshal(u)
|
|
||||||
if string(j) != "null" {
|
|
||||||
t.Errorf("Expected null but got %v", string(j))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewLogRes(t *testing.T) {
|
|
||||||
log := makeStateLog(0)
|
|
||||||
tests := map[string]string{
|
|
||||||
"address": reAddress,
|
|
||||||
// "topics": "[.*]"
|
|
||||||
"data": reData,
|
|
||||||
"blockNumber": reNum,
|
|
||||||
// "hash": reHash,
|
|
||||||
// "logIndex": reNum,
|
|
||||||
// "blockHash": reHash,
|
|
||||||
// "transactionHash": reHash,
|
|
||||||
"transactionIndex": reNum,
|
|
||||||
}
|
|
||||||
|
|
||||||
v := NewLogRes(log)
|
|
||||||
j, _ := json.Marshal(v)
|
|
||||||
|
|
||||||
for k, re := range tests {
|
|
||||||
match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j))
|
|
||||||
if !match {
|
|
||||||
t.Error(fmt.Sprintf("`%s` output json does not match format %s. Got %s", k, re, j))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewLogsRes(t *testing.T) {
|
|
||||||
logs := make([]*state.Log, 3)
|
|
||||||
logs[0] = makeStateLog(1)
|
|
||||||
logs[1] = makeStateLog(2)
|
|
||||||
logs[2] = makeStateLog(3)
|
|
||||||
tests := map[string]string{}
|
|
||||||
|
|
||||||
v := NewLogsRes(logs)
|
|
||||||
j, _ := json.Marshal(v)
|
|
||||||
|
|
||||||
for k, re := range tests {
|
|
||||||
match, _ := regexp.MatchString(fmt.Sprintf(`[{.*"%s":%s.*}]`, k, re), string(j))
|
|
||||||
if !match {
|
|
||||||
t.Error(fmt.Sprintf("%s output json does not match format %s. Got %s", k, re, j))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeStateLog(num int) *state.Log {
|
|
||||||
address := common.HexToAddress("0x0")
|
|
||||||
data := []byte{1, 2, 3}
|
|
||||||
number := uint64(num)
|
|
||||||
topics := make([]common.Hash, 3)
|
|
||||||
topics = append(topics, common.HexToHash("0x00"))
|
|
||||||
topics = append(topics, common.HexToHash("0x10"))
|
|
||||||
topics = append(topics, common.HexToHash("0x20"))
|
|
||||||
log := state.NewLog(address, topics, data, number)
|
|
||||||
return log
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeHeader() *types.Header {
|
|
||||||
header := &types.Header{
|
|
||||||
ParentHash: common.StringToHash("0x00"),
|
|
||||||
UncleHash: common.StringToHash("0x00"),
|
|
||||||
Coinbase: common.StringToAddress("0x00"),
|
|
||||||
Root: common.StringToHash("0x00"),
|
|
||||||
TxHash: common.StringToHash("0x00"),
|
|
||||||
ReceiptHash: common.StringToHash("0x00"),
|
|
||||||
// Bloom:
|
|
||||||
Difficulty: big.NewInt(88888888),
|
|
||||||
Number: big.NewInt(16),
|
|
||||||
GasLimit: big.NewInt(70000),
|
|
||||||
GasUsed: big.NewInt(25000),
|
|
||||||
Time: 124356789,
|
|
||||||
Extra: nil,
|
|
||||||
MixDigest: common.StringToHash("0x00"),
|
|
||||||
Nonce: [8]byte{0, 1, 2, 3, 4, 5, 6, 7},
|
|
||||||
}
|
|
||||||
return header
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeBlock() *types.Block {
|
|
||||||
parentHash := common.HexToHash("0x01")
|
|
||||||
coinbase := common.HexToAddress("0x01")
|
|
||||||
root := common.HexToHash("0x01")
|
|
||||||
difficulty := common.Big1
|
|
||||||
nonce := uint64(1)
|
|
||||||
block := types.NewBlock(parentHash, coinbase, root, difficulty, nonce, nil)
|
|
||||||
|
|
||||||
txto := common.HexToAddress("0x02")
|
|
||||||
txamount := big.NewInt(1)
|
|
||||||
txgasAmount := big.NewInt(1)
|
|
||||||
txgasPrice := big.NewInt(1)
|
|
||||||
txdata := []byte{1, 2, 3}
|
|
||||||
|
|
||||||
tx := types.NewTransactionMessage(txto, txamount, txgasAmount, txgasPrice, txdata)
|
|
||||||
txs := make([]*types.Transaction, 1)
|
|
||||||
txs[0] = tx
|
|
||||||
block.SetTransactions(txs)
|
|
||||||
|
|
||||||
uncles := make([]*types.Header, 1)
|
|
||||||
uncles[0] = makeHeader()
|
|
||||||
block.SetUncles(uncles)
|
|
||||||
|
|
||||||
return block
|
|
||||||
}
|
|
381
rpc/types.go
381
rpc/types.go
@ -1,381 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of go-ethereum
|
|
||||||
|
|
||||||
go-ethereum is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
go-ethereum is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package rpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type hexdata struct {
|
|
||||||
data []byte
|
|
||||||
isNil bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *hexdata) String() string {
|
|
||||||
return "0x" + common.Bytes2Hex(d.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *hexdata) MarshalJSON() ([]byte, error) {
|
|
||||||
if d.isNil {
|
|
||||||
return json.Marshal(nil)
|
|
||||||
}
|
|
||||||
return json.Marshal(d.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHexData(input interface{}) *hexdata {
|
|
||||||
d := new(hexdata)
|
|
||||||
|
|
||||||
if input == nil {
|
|
||||||
d.isNil = true
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
switch input := input.(type) {
|
|
||||||
case []byte:
|
|
||||||
d.data = input
|
|
||||||
case common.Hash:
|
|
||||||
d.data = input.Bytes()
|
|
||||||
case *common.Hash:
|
|
||||||
if input == nil {
|
|
||||||
d.isNil = true
|
|
||||||
} else {
|
|
||||||
d.data = input.Bytes()
|
|
||||||
}
|
|
||||||
case common.Address:
|
|
||||||
d.data = input.Bytes()
|
|
||||||
case *common.Address:
|
|
||||||
if input == nil {
|
|
||||||
d.isNil = true
|
|
||||||
} else {
|
|
||||||
d.data = input.Bytes()
|
|
||||||
}
|
|
||||||
case types.Bloom:
|
|
||||||
d.data = input.Bytes()
|
|
||||||
case *types.Bloom:
|
|
||||||
if input == nil {
|
|
||||||
d.isNil = true
|
|
||||||
} else {
|
|
||||||
d.data = input.Bytes()
|
|
||||||
}
|
|
||||||
case *big.Int:
|
|
||||||
if input == nil {
|
|
||||||
d.isNil = true
|
|
||||||
} else {
|
|
||||||
d.data = input.Bytes()
|
|
||||||
}
|
|
||||||
case int64:
|
|
||||||
d.data = big.NewInt(input).Bytes()
|
|
||||||
case uint64:
|
|
||||||
buff := make([]byte, 8)
|
|
||||||
binary.BigEndian.PutUint64(buff, input)
|
|
||||||
d.data = buff
|
|
||||||
case int:
|
|
||||||
d.data = big.NewInt(int64(input)).Bytes()
|
|
||||||
case uint:
|
|
||||||
d.data = big.NewInt(int64(input)).Bytes()
|
|
||||||
case int8:
|
|
||||||
d.data = big.NewInt(int64(input)).Bytes()
|
|
||||||
case uint8:
|
|
||||||
d.data = big.NewInt(int64(input)).Bytes()
|
|
||||||
case int16:
|
|
||||||
d.data = big.NewInt(int64(input)).Bytes()
|
|
||||||
case uint16:
|
|
||||||
buff := make([]byte, 2)
|
|
||||||
binary.BigEndian.PutUint16(buff, input)
|
|
||||||
d.data = buff
|
|
||||||
case int32:
|
|
||||||
d.data = big.NewInt(int64(input)).Bytes()
|
|
||||||
case uint32:
|
|
||||||
buff := make([]byte, 4)
|
|
||||||
binary.BigEndian.PutUint32(buff, input)
|
|
||||||
d.data = buff
|
|
||||||
case string: // hexstring
|
|
||||||
// aaargh ffs TODO: avoid back-and-forth hex encodings where unneeded
|
|
||||||
bytes, err := hex.DecodeString(strings.TrimPrefix(input, "0x"))
|
|
||||||
if err != nil {
|
|
||||||
d.isNil = true
|
|
||||||
} else {
|
|
||||||
d.data = bytes
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
d.isNil = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
type hexnum struct {
|
|
||||||
data []byte
|
|
||||||
isNil bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *hexnum) String() string {
|
|
||||||
// Get hex string from bytes
|
|
||||||
out := common.Bytes2Hex(d.data)
|
|
||||||
// Trim leading 0s
|
|
||||||
out = strings.TrimLeft(out, "0")
|
|
||||||
// Output "0x0" when value is 0
|
|
||||||
if len(out) == 0 {
|
|
||||||
out = "0"
|
|
||||||
}
|
|
||||||
return "0x" + out
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *hexnum) MarshalJSON() ([]byte, error) {
|
|
||||||
if d.isNil {
|
|
||||||
return json.Marshal(nil)
|
|
||||||
}
|
|
||||||
return json.Marshal(d.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHexNum(input interface{}) *hexnum {
|
|
||||||
d := new(hexnum)
|
|
||||||
|
|
||||||
d.data = newHexData(input).data
|
|
||||||
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
type RpcConfig struct {
|
|
||||||
ListenAddress string
|
|
||||||
ListenPort uint
|
|
||||||
CorsDomain string
|
|
||||||
}
|
|
||||||
|
|
||||||
type InvalidTypeError struct {
|
|
||||||
method string
|
|
||||||
msg string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *InvalidTypeError) Error() string {
|
|
||||||
return fmt.Sprintf("invalid type on field %s: %s", e.method, e.msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewInvalidTypeError(method, msg string) *InvalidTypeError {
|
|
||||||
return &InvalidTypeError{
|
|
||||||
method: method,
|
|
||||||
msg: msg,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type InsufficientParamsError struct {
|
|
||||||
have int
|
|
||||||
want int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *InsufficientParamsError) Error() string {
|
|
||||||
return fmt.Sprintf("insufficient params, want %d have %d", e.want, e.have)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewInsufficientParamsError(have int, want int) *InsufficientParamsError {
|
|
||||||
return &InsufficientParamsError{
|
|
||||||
have: have,
|
|
||||||
want: want,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type NotImplementedError struct {
|
|
||||||
Method string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *NotImplementedError) Error() string {
|
|
||||||
return fmt.Sprintf("%s method not implemented", e.Method)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewNotImplementedError(method string) *NotImplementedError {
|
|
||||||
return &NotImplementedError{
|
|
||||||
Method: method,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type NotAvailableError struct {
|
|
||||||
Method string
|
|
||||||
Reason string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *NotAvailableError) Error() string {
|
|
||||||
return fmt.Sprintf("%s method not available: %s", e.Method, e.Reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewNotAvailableError(method string, reason string) *NotAvailableError {
|
|
||||||
return &NotAvailableError{
|
|
||||||
Method: method,
|
|
||||||
Reason: reason,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type DecodeParamError struct {
|
|
||||||
err string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *DecodeParamError) Error() string {
|
|
||||||
return fmt.Sprintf("could not decode, %s", e.err)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDecodeParamError(errstr string) error {
|
|
||||||
return &DecodeParamError{
|
|
||||||
err: errstr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ValidationError struct {
|
|
||||||
ParamName string
|
|
||||||
msg string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *ValidationError) Error() string {
|
|
||||||
return fmt.Sprintf("%s not valid, %s", e.ParamName, e.msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewValidationError(param string, msg string) error {
|
|
||||||
return &ValidationError{
|
|
||||||
ParamName: param,
|
|
||||||
msg: msg,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type RpcRequest struct {
|
|
||||||
Id interface{} `json:"id"`
|
|
||||||
Jsonrpc string `json:"jsonrpc"`
|
|
||||||
Method string `json:"method"`
|
|
||||||
Params json.RawMessage `json:"params"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RpcSuccessResponse struct {
|
|
||||||
Id interface{} `json:"id"`
|
|
||||||
Jsonrpc string `json:"jsonrpc"`
|
|
||||||
Result interface{} `json:"result"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RpcErrorResponse struct {
|
|
||||||
Id interface{} `json:"id"`
|
|
||||||
Jsonrpc string `json:"jsonrpc"`
|
|
||||||
Error *RpcErrorObject `json:"error"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RpcErrorObject struct {
|
|
||||||
Code int `json:"code"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
// Data interface{} `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type listenerHasStoppedError struct {
|
|
||||||
msg string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self listenerHasStoppedError) Error() string {
|
|
||||||
return self.msg
|
|
||||||
}
|
|
||||||
|
|
||||||
var listenerStoppedError = listenerHasStoppedError{"Listener stopped"}
|
|
||||||
|
|
||||||
// When https://github.com/golang/go/issues/4674 is fixed this could be replaced
|
|
||||||
type stoppableTCPListener struct {
|
|
||||||
*net.TCPListener
|
|
||||||
stop chan struct{} // closed when the listener must stop
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wraps the default handler and checks if the RPC service was stopped. In that case it returns an
|
|
||||||
// error indicating that the service was stopped. This will only happen for connections which are
|
|
||||||
// kept open (HTTP keep-alive) when the RPC service was shutdown.
|
|
||||||
func newStoppableHandler(h http.Handler, stop chan struct{}) http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
select {
|
|
||||||
case <-stop:
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
jsonerr := &RpcErrorObject{-32603, "RPC service stopped"}
|
|
||||||
send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
|
|
||||||
default:
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop the listener and all accepted and still active connections.
|
|
||||||
func (self *stoppableTCPListener) Stop() {
|
|
||||||
close(self.stop)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newStoppableTCPListener(addr string) (*stoppableTCPListener, error) {
|
|
||||||
wl, err := net.Listen("tcp", addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if tcpl, ok := wl.(*net.TCPListener); ok {
|
|
||||||
stop := make(chan struct{})
|
|
||||||
l := &stoppableTCPListener{tcpl, stop}
|
|
||||||
return l, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.New("Unable to create TCP listener for RPC service")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *stoppableTCPListener) Accept() (net.Conn, error) {
|
|
||||||
for {
|
|
||||||
self.SetDeadline(time.Now().Add(time.Duration(1 * time.Second)))
|
|
||||||
c, err := self.TCPListener.AcceptTCP()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-self.stop:
|
|
||||||
if c != nil { // accept timeout
|
|
||||||
c.Close()
|
|
||||||
}
|
|
||||||
self.TCPListener.Close()
|
|
||||||
return nil, listenerStoppedError
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if netErr, ok := err.(net.Error); ok && netErr.Timeout() && netErr.Temporary() {
|
|
||||||
continue // regular timeout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &closableConnection{c, self.stop}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type closableConnection struct {
|
|
||||||
*net.TCPConn
|
|
||||||
closed chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *closableConnection) Read(b []byte) (n int, err error) {
|
|
||||||
select {
|
|
||||||
case <-self.closed:
|
|
||||||
self.TCPConn.Close()
|
|
||||||
return 0, io.EOF
|
|
||||||
default:
|
|
||||||
return self.TCPConn.Read(b)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,204 +0,0 @@
|
|||||||
package rpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"math/big"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestInvalidTypeError(t *testing.T) {
|
|
||||||
err := NewInvalidTypeError("testField", "not string")
|
|
||||||
expected := "invalid type on field testField: not string"
|
|
||||||
|
|
||||||
if err.Error() != expected {
|
|
||||||
t.Error(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInsufficientParamsError(t *testing.T) {
|
|
||||||
err := NewInsufficientParamsError(0, 1)
|
|
||||||
expected := "insufficient params, want 1 have 0"
|
|
||||||
|
|
||||||
if err.Error() != expected {
|
|
||||||
t.Error(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotImplementedError(t *testing.T) {
|
|
||||||
err := NewNotImplementedError("foo")
|
|
||||||
expected := "foo method not implemented"
|
|
||||||
|
|
||||||
if err.Error() != expected {
|
|
||||||
t.Error(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecodeParamError(t *testing.T) {
|
|
||||||
err := NewDecodeParamError("foo")
|
|
||||||
expected := "could not decode, foo"
|
|
||||||
|
|
||||||
if err.Error() != expected {
|
|
||||||
t.Error(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValidationError(t *testing.T) {
|
|
||||||
err := NewValidationError("foo", "should be `bar`")
|
|
||||||
expected := "foo not valid, should be `bar`"
|
|
||||||
|
|
||||||
if err.Error() != expected {
|
|
||||||
t.Error(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexdataMarshalNil(t *testing.T) {
|
|
||||||
hd := newHexData([]byte{})
|
|
||||||
hd.isNil = true
|
|
||||||
v, _ := json.Marshal(hd)
|
|
||||||
if string(v) != "null" {
|
|
||||||
t.Errorf("Expected null, got %s", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexnumMarshalNil(t *testing.T) {
|
|
||||||
hn := newHexNum([]byte{})
|
|
||||||
hn.isNil = true
|
|
||||||
v, _ := json.Marshal(hn)
|
|
||||||
if string(v) != "null" {
|
|
||||||
t.Errorf("Expected null, got %s", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexdataNil(t *testing.T) {
|
|
||||||
v := newHexData(nil)
|
|
||||||
if v.isNil != true {
|
|
||||||
t.Errorf("Expected isNil to be true, but is %v", v.isNil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexdataPtrHash(t *testing.T) {
|
|
||||||
in := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
|
|
||||||
v := newHexData(&in)
|
|
||||||
if bytes.Compare(in.Bytes(), v.data) != 0 {
|
|
||||||
t.Errorf("Got % x expected % x", in, v.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexdataPtrHashNil(t *testing.T) {
|
|
||||||
var in *common.Hash
|
|
||||||
in = nil
|
|
||||||
v := newHexData(in)
|
|
||||||
if !v.isNil {
|
|
||||||
t.Errorf("Expect isNil to be true, but is %v", v.isNil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexdataPtrAddress(t *testing.T) {
|
|
||||||
in := common.Address{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
|
|
||||||
v := newHexData(&in)
|
|
||||||
if bytes.Compare(in.Bytes(), v.data) != 0 {
|
|
||||||
t.Errorf("Got % x expected % x", in, v.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexdataPtrAddressNil(t *testing.T) {
|
|
||||||
var in *common.Address
|
|
||||||
in = nil
|
|
||||||
v := newHexData(in)
|
|
||||||
if !v.isNil {
|
|
||||||
t.Errorf("Expect isNil to be true, but is %v", v.isNil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexdataPtrBloom(t *testing.T) {
|
|
||||||
in := types.Bloom{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
|
|
||||||
v := newHexData(&in)
|
|
||||||
if bytes.Compare(in.Bytes(), v.data) != 0 {
|
|
||||||
t.Errorf("Got % x expected % x", in, v.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexdataPtrBloomNil(t *testing.T) {
|
|
||||||
var in *types.Bloom
|
|
||||||
in = nil
|
|
||||||
v := newHexData(in)
|
|
||||||
if !v.isNil {
|
|
||||||
t.Errorf("Expect isNil to be true, but is %v", v.isNil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexdataBigintNil(t *testing.T) {
|
|
||||||
var in *big.Int
|
|
||||||
in = nil
|
|
||||||
v := newHexData(in)
|
|
||||||
if !v.isNil {
|
|
||||||
t.Errorf("Expect isNil to be true, but is %v", v.isNil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexdataUint(t *testing.T) {
|
|
||||||
var in = uint(16)
|
|
||||||
var expected = []byte{0x10}
|
|
||||||
v := newHexData(in)
|
|
||||||
if bytes.Compare(expected, v.data) != 0 {
|
|
||||||
t.Errorf("Expected % x got % x", expected, v.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexdataInt8(t *testing.T) {
|
|
||||||
var in = int8(16)
|
|
||||||
var expected = []byte{0x10}
|
|
||||||
v := newHexData(in)
|
|
||||||
if bytes.Compare(expected, v.data) != 0 {
|
|
||||||
t.Errorf("Expected % x got % x", expected, v.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexdataUint8(t *testing.T) {
|
|
||||||
var in = uint8(16)
|
|
||||||
var expected = []byte{0x10}
|
|
||||||
v := newHexData(in)
|
|
||||||
if bytes.Compare(expected, v.data) != 0 {
|
|
||||||
t.Errorf("Expected % x got % x", expected, v.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexdataInt16(t *testing.T) {
|
|
||||||
var in = int16(16)
|
|
||||||
var expected = []byte{0x10}
|
|
||||||
v := newHexData(in)
|
|
||||||
if bytes.Compare(expected, v.data) != 0 {
|
|
||||||
t.Errorf("Expected % x got % x", expected, v.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexdataUint16(t *testing.T) {
|
|
||||||
var in = uint16(16)
|
|
||||||
var expected = []byte{0x0, 0x10}
|
|
||||||
v := newHexData(in)
|
|
||||||
if bytes.Compare(expected, v.data) != 0 {
|
|
||||||
t.Errorf("Expected % x got % x", expected, v.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexdataInt32(t *testing.T) {
|
|
||||||
var in = int32(16)
|
|
||||||
var expected = []byte{0x10}
|
|
||||||
v := newHexData(in)
|
|
||||||
if bytes.Compare(expected, v.data) != 0 {
|
|
||||||
t.Errorf("Expected % x got % x", expected, v.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexdataUint32(t *testing.T) {
|
|
||||||
var in = uint32(16)
|
|
||||||
var expected = []byte{0x0, 0x0, 0x0, 0x10}
|
|
||||||
v := newHexData(in)
|
|
||||||
if bytes.Compare(expected, v.data) != 0 {
|
|
||||||
t.Errorf("Expected % x got % x", expected, v.data)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user