From b9c9d6d798b26942d69ec9d3c3e88a1b8f563fb6 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 2 Apr 2015 14:37:11 +0200 Subject: [PATCH 01/12] Use HashArgs for eth_getTransactionByHash --- rpc/api.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index 5e27890ce..8cc86103b 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -192,8 +192,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err *reply = br case "eth_getTransactionByHash": - // HashIndexArgs used, but only the "Hash" part we need. - args := new(HashIndexArgs) + args := new(HashArgs) if err := json.Unmarshal(req.Params, &args); err != nil { } tx, bhash, bnum, txi := api.xeth().EthTransactionByHash(args.Hash) From 172b34351aeb03d0318d241c0941c27608448cfb Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 2 Apr 2015 14:49:33 +0200 Subject: [PATCH 02/12] HashArgs fix + tests --- rpc/api.go | 1 + rpc/args_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/rpc/api.go b/rpc/api.go index 8cc86103b..ee2a6a047 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -194,6 +194,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err 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 { diff --git a/rpc/args_test.go b/rpc/args_test.go index 902f8013e..0ac8f657b 100644 --- a/rpc/args_test.go +++ b/rpc/args_test.go @@ -2090,6 +2090,51 @@ func TestHashIndexArgsInvalidIndex(t *testing.T) { } } +func TestHashArgs(t *testing.T) { + input := `["0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b"]` + expected := new(HashIndexArgs) + expected.Hash = "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b" + + args := new(HashArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Hash != args.Hash { + t.Errorf("Hash shoud be %#v but is %#v", expected.Hash, args.Hash) + } +} + +func TestHashArgsEmpty(t *testing.T) { + input := `[]` + + args := new(HashArgs) + str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestHashArgsInvalid(t *testing.T) { + input := `{}` + + args := new(HashArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestHashArgsInvalidHash(t *testing.T) { + input := `[7]` + + args := new(HashArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + func TestSubmitWorkArgs(t *testing.T) { input := `["0x0000000000000001", "0x1234567890abcdef1234567890abcdef", "0xD1GE5700000000000000000000000000"]` expected := new(SubmitWorkArgs) From 876ce0fb12d7c1eae2907dad9ea13d01e9b3ac17 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 2 Apr 2015 14:54:28 +0200 Subject: [PATCH 03/12] More nil checks --- rpc/api.go | 6 ++++++ rpc/responses.go | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index ee2a6a047..b46151cda 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -212,6 +212,9 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err block := api.xeth().EthBlockByHash(args.Hash) br := NewBlockRes(block, true) + if br == nil { + *reply = nil + } if args.Index >= int64(len(br.Transactions)) || args.Index < 0 { return NewValidationError("Index", "does not exist") @@ -225,6 +228,9 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err block := api.xeth().EthBlockByNumber(args.BlockNumber) v := NewBlockRes(block, true) + if v == nil { + *reply = nil + } if args.Index >= int64(len(v.Transactions)) || args.Index < 0 { return NewValidationError("Index", "does not exist") diff --git a/rpc/responses.go b/rpc/responses.go index 3d1687cb6..079ee8765 100644 --- a/rpc/responses.go +++ b/rpc/responses.go @@ -125,8 +125,6 @@ func (b *BlockRes) MarshalJSON() ([]byte, error) { } func NewBlockRes(block *types.Block, fullTx bool) *BlockRes { - // TODO respect fullTx flag - if block == nil { return nil } @@ -182,6 +180,10 @@ type TransactionRes struct { } 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()) From 39085905783829161738207271aa35386f6ebc72 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 2 Apr 2015 15:37:35 +0200 Subject: [PATCH 04/12] Make "To" field optional in whisper filter --- rpc/args.go | 13 ++++++++----- rpc/args_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/rpc/args.go b/rpc/args.go index 70618a01a..a8cb7dcb1 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -1021,12 +1021,15 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { return NewInsufficientParamsError(len(obj), 1) } - var argstr string - argstr, ok := obj[0].To.(string) - if !ok { - return NewInvalidTypeError("to", "is not a string") + if obj[0].To == nil { + args.To = "" + } else { + argstr, ok := obj[0].To.(string) + if !ok { + return NewInvalidTypeError("to", "is not a string") + } + args.To = argstr } - args.To = argstr t := make([]string, len(obj[0].Topics)) for i, j := range obj[0].Topics { diff --git a/rpc/args_test.go b/rpc/args_test.go index 0ac8f657b..b88bab280 100644 --- a/rpc/args_test.go +++ b/rpc/args_test.go @@ -1805,6 +1805,16 @@ func TestWhisperFilterArgsEmpty(t *testing.T) { } } +func TestWhisperFilterArgsToInt(t *testing.T) { + input := `[{"to": 2}]` + + args := new(WhisperFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) + if len(str) > 0 { + t.Error(str) + } +} + func TestWhisperFilterArgsToBool(t *testing.T) { input := `[{"topics": ["0x68656c6c6f20776f726c64"], "to": false}]` @@ -1815,6 +1825,21 @@ func TestWhisperFilterArgsToBool(t *testing.T) { } } +func TestWhisperFilterArgsToMissing(t *testing.T) { + input := `[{"topics": ["0x68656c6c6f20776f726c64"]}]` + expected := new(WhisperFilterArgs) + expected.To = "" + + args := new(WhisperFilterArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if args.To != expected.To { + t.Errorf("To shoud be %v but is %v", expected.To, args.To) + } +} + func TestWhisperFilterArgsTopicInt(t *testing.T) { input := `[{"topics": [6], "to": "0x34ag445g3455b34"}]` From aa71e27a3b0f42b980082c52232c68fb50a45052 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 2 Apr 2015 15:53:04 +0200 Subject: [PATCH 05/12] Detect non-valid strings for blockheight --- common/bytes.go | 5 +++++ rpc/args.go | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/common/bytes.go b/common/bytes.go index 2d885ac74..5bdacd810 100644 --- a/common/bytes.go +++ b/common/bytes.go @@ -127,6 +127,11 @@ func CopyBytes(b []byte) (copiedBytes []byte) { return } +func HasHexPrefix(str string) bool { + l := len(str) + return l >= 2 && str[0:2] == "0x" +} + func IsHex(str string) bool { l := len(str) return l >= 4 && l%2 == 0 && str[0:2] == "0x" diff --git a/rpc/args.go b/rpc/args.go index a8cb7dcb1..cebabf4ba 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -41,7 +41,11 @@ func blockHeight(raw interface{}, number *int64) error { case "pending": *number = -2 default: - *number = common.String2Big(str).Int64() + if common.HasHexPrefix(str) { + *number = common.String2Big(str).Int64() + } else { + return NewInvalidTypeError("blockNumber", "is not a valid string") + } } return nil From e882ba0c29a4e616113a4c16e6ea08b8a8abf06b Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 2 Apr 2015 16:11:00 +0200 Subject: [PATCH 06/12] Return nil when requested index does not exist Instead of error --- rpc/api.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index b46151cda..4ce2a98e2 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -217,9 +217,11 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err } if args.Index >= int64(len(br.Transactions)) || args.Index < 0 { - return NewValidationError("Index", "does not exist") + // return NewValidationError("Index", "does not exist") + *reply = nil + } else { + *reply = br.Transactions[args.Index] } - *reply = br.Transactions[args.Index] case "eth_getTransactionByBlockNumberAndIndex": args := new(BlockNumIndexArgs) if err := json.Unmarshal(req.Params, &args); err != nil { @@ -233,9 +235,11 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err } if args.Index >= int64(len(v.Transactions)) || args.Index < 0 { - return NewValidationError("Index", "does not exist") + // return NewValidationError("Index", "does not exist") + *reply = nil + } else { + *reply = v.Transactions[args.Index] } - *reply = v.Transactions[args.Index] case "eth_getUncleByBlockHashAndIndex": args := new(HashIndexArgs) if err := json.Unmarshal(req.Params, &args); err != nil { From 537e2cfeb64a14d9c54d5ae69e02a866b5117bd3 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 2 Apr 2015 16:12:19 +0200 Subject: [PATCH 07/12] Return nil when requested uncle index is not valid --- rpc/api.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index 4ce2a98e2..872290eef 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -253,13 +253,13 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err } if args.Index >= int64(len(br.Uncles)) || args.Index < 0 { - return NewValidationError("Index", "does not exist") + // return NewValidationError("Index", "does not exist") + *reply = nil + } else { + uhash := br.Uncles[args.Index] + uncle := NewBlockRes(api.xeth().EthBlockByHash(uhash.String()), true) + *reply = uncle } - - uhash := br.Uncles[args.Index] - uncle := NewBlockRes(api.xeth().EthBlockByHash(uhash.String()), false) - - *reply = uncle case "eth_getUncleByBlockNumberAndIndex": args := new(BlockNumIndexArgs) if err := json.Unmarshal(req.Params, &args); err != nil { @@ -275,13 +275,13 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err } if args.Index >= int64(len(v.Uncles)) || args.Index < 0 { - return NewValidationError("Index", "does not exist") + // return NewValidationError("Index", "does not exist") + *reply = nil + } else { + uhash := v.Uncles[args.Index] + uncle := NewBlockRes(api.xeth().EthBlockByHash(uhash.String()), false) + *reply = uncle } - - uhash := v.Uncles[args.Index] - uncle := NewBlockRes(api.xeth().EthBlockByHash(uhash.String()), false) - - *reply = uncle case "eth_getCompilers": c := []string{""} *reply = c From 9bdf0b655de64c0c31d81da00ff2daec79359d89 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 2 Apr 2015 16:28:42 +0200 Subject: [PATCH 08/12] Fix RLP decoding of tx metadata --- xeth/xeth.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xeth/xeth.go b/xeth/xeth.go index 6c9db16c2..ad6b09b54 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -196,7 +196,7 @@ func (self *XEth) EthTransactionByHash(hash string) (tx *types.Transaction, blha // meta var txExtra struct { BlockHash common.Hash - BlockIndex int64 + BlockIndex uint64 Index uint64 } @@ -205,8 +205,10 @@ func (self *XEth) EthTransactionByHash(hash string) (tx *types.Transaction, blha err := rlp.Decode(r, &txExtra) if err == nil { blhash = txExtra.BlockHash - blnum = big.NewInt(txExtra.BlockIndex) + blnum = big.NewInt(int64(txExtra.BlockIndex)) txi = txExtra.Index + } else { + pipelogger.Errorln(err) } return From dd1791c9fd87b3744dc0747153e41fe893cc727b Mon Sep 17 00:00:00 2001 From: zelig Date: Thu, 2 Apr 2015 15:32:41 +0100 Subject: [PATCH 09/12] proper locking to prevent close of closed channel crash when multiple mining peers compete --- blockpool/peers.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/blockpool/peers.go b/blockpool/peers.go index 615058e26..802081780 100644 --- a/blockpool/peers.go +++ b/blockpool/peers.go @@ -219,6 +219,7 @@ func (self *peers) addPeer( return } self.lock.Lock() + defer self.lock.Unlock() p, found := self.peers[id] if found { // when called on an already connected peer, it means a newBlockMsg is received @@ -243,7 +244,6 @@ func (self *peers) addPeer( plog.Debugf("addPeer: add new peer <%v> with td %v and current block %s", id, td, hex(currentBlockHash)) } - self.lock.Unlock() // check if peer's current head block is known if self.bp.hasBlock(currentBlockHash) { @@ -269,7 +269,10 @@ func (self *peers) addPeer( } else { // baseline is our own TD currentTD := self.bp.getTD() - if self.best != nil { + bestpeer := self.best + if bestpeer != nil { + bestpeer.lock.Lock() + defer bestpeer.lock.Unlock() currentTD = self.best.td } if td.Cmp(currentTD) > 0 { @@ -277,11 +280,12 @@ func (self *peers) addPeer( self.status.bestPeers[p.id]++ self.status.lock.Unlock() plog.Debugf("addPeer: peer <%v> (td: %v > current td %v) promoted best peer", id, td, currentTD) - self.bp.switchPeer(self.best, p) + self.bp.switchPeer(bestpeer, p) self.best = p best = true } } + return } From 5cb1b41440209b03a1c5ec6c639207fd336e67e3 Mon Sep 17 00:00:00 2001 From: zelig Date: Thu, 2 Apr 2015 16:30:48 +0100 Subject: [PATCH 10/12] proper locking to prevent "parent unknown" INVALID blocks due to race in peer head info update --- blockpool/peers.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/blockpool/peers.go b/blockpool/peers.go index 802081780..285fa45b1 100644 --- a/blockpool/peers.go +++ b/blockpool/peers.go @@ -133,13 +133,10 @@ func (self *peer) addError(code int, format string, params ...interface{}) { self.addToBlacklist(self.id) } +// caller must hold peer lock func (self *peer) setChainInfo(td *big.Int, c common.Hash) { - self.lock.Lock() - defer self.lock.Unlock() - self.td = td self.currentBlockHash = c - self.currentBlock = nil self.parentHash = common.Hash{} self.headSection = nil @@ -171,7 +168,7 @@ func (self *peers) requestBlocks(attempts int, hashes []common.Hash) { defer self.lock.RUnlock() peerCount := len(self.peers) // on first attempt use the best peer - if attempts == 0 { + if attempts == 0 && self.best != nil { plog.DebugDetailf("request %v missing blocks from best peer <%s>", len(hashes), self.best.id) self.best.requestBlocks(hashes) return @@ -224,6 +221,7 @@ func (self *peers) addPeer( if found { // when called on an already connected peer, it means a newBlockMsg is received // peer head info is updated + p.lock.Lock() if p.currentBlockHash != currentBlockHash { previousBlockHash = p.currentBlockHash plog.Debugf("addPeer: Update peer <%s> with td %v and current block %s (was %v)", id, td, hex(currentBlockHash), hex(previousBlockHash)) @@ -233,6 +231,7 @@ func (self *peers) addPeer( self.status.values.NewBlocks++ self.status.lock.Unlock() } + p.lock.Unlock() } else { p = self.newPeer(td, currentBlockHash, id, requestBlockHashes, requestBlocks, peerError) From 1d74086b4240f61d73e8fbf446f6234ab914ddce Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 2 Apr 2015 17:55:42 +0200 Subject: [PATCH 11/12] New UncleRes type --- rpc/api.go | 8 ++----- rpc/responses.go | 59 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index 872290eef..b8207ea0d 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -256,9 +256,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err // return NewValidationError("Index", "does not exist") *reply = nil } else { - uhash := br.Uncles[args.Index] - uncle := NewBlockRes(api.xeth().EthBlockByHash(uhash.String()), true) - *reply = uncle + *reply = br.Uncles[args.Index] } case "eth_getUncleByBlockNumberAndIndex": args := new(BlockNumIndexArgs) @@ -278,9 +276,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err // return NewValidationError("Index", "does not exist") *reply = nil } else { - uhash := v.Uncles[args.Index] - uncle := NewBlockRes(api.xeth().EthBlockByHash(uhash.String()), false) - *reply = uncle + *reply = v.Uncles[args.Index] } case "eth_getCompilers": c := []string{""} diff --git a/rpc/responses.go b/rpc/responses.go index 079ee8765..52a2f714c 100644 --- a/rpc/responses.go +++ b/rpc/responses.go @@ -28,7 +28,7 @@ type BlockRes struct { GasUsed *hexnum `json:"gasUsed"` UnixTimestamp *hexnum `json:"timestamp"` Transactions []*TransactionRes `json:"transactions"` - Uncles []*hexdata `json:"uncles"` + Uncles []*UncleRes `json:"uncles"` } func (b *BlockRes) MarshalJSON() ([]byte, error) { @@ -73,7 +73,10 @@ func (b *BlockRes) MarshalJSON() ([]byte, error) { ext.GasUsed = b.GasUsed ext.UnixTimestamp = b.UnixTimestamp ext.Transactions = b.Transactions - ext.Uncles = b.Uncles + 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 { @@ -119,7 +122,10 @@ func (b *BlockRes) MarshalJSON() ([]byte, error) { for i, tx := range b.Transactions { ext.Transactions[i] = tx.Hash } - ext.Uncles = b.Uncles + ext.Uncles = make([]*hexdata, len(b.Uncles)) + for i, u := range b.Uncles { + ext.Uncles[i] = u.BlockHash + } return json.Marshal(ext) } } @@ -157,9 +163,9 @@ func NewBlockRes(block *types.Block, fullTx bool) *BlockRes { res.Transactions[i].TxIndex = newHexNum(i) } - res.Uncles = make([]*hexdata, len(block.Uncles())) + res.Uncles = make([]*UncleRes, len(block.Uncles())) for i, uncle := range block.Uncles() { - res.Uncles[i] = newHexData(uncle.Hash()) + res.Uncles[i] = NewUncleRes(uncle) } return res @@ -200,6 +206,49 @@ func NewTransactionRes(tx *types.Transaction) *TransactionRes { 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"` From 3e042317adc99438d6ffde0cbde4f0b40ad579c1 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 2 Apr 2015 20:37:51 +0200 Subject: [PATCH 12/12] Return nil if block does not exist --- rpc/api.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rpc/api.go b/rpc/api.go index b8207ea0d..573f689a2 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -2,6 +2,7 @@ package rpc import ( "encoding/json" + // "fmt" "math/big" "sync" @@ -112,7 +113,11 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err } block := NewBlockRes(api.xeth().EthBlockByHash(args.Hash), false) - *reply = common.ToHex(big.NewInt(int64(len(block.Transactions))).Bytes()) + if block == nil { + *reply = nil + } else { + *reply = common.ToHex(big.NewInt(int64(len(block.Transactions))).Bytes()) + } case "eth_getBlockTransactionCountByNumber": args := new(BlockNumArg) if err := json.Unmarshal(req.Params, &args); err != nil {