From 418e29f0bfbe6aefcb4744d690e36de4ff9bf606 Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 09:53:13 -0500 Subject: [PATCH 01/22] ethclient,internal/ethapi: add Nonce (null) to pending block JSON Date: 2021-03-15 09:53:13-05:00 Signed-off-by: meows --- ethclient/ethclient_test.go | 27 +++++++++++++++++++++++++++ internal/ethapi/api.go | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index 0e887d9878..ac29d8d9a6 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -315,6 +315,33 @@ func TestHeader_TxesUnclesNotEmpty(t *testing.T) { } } +func TestHeader_PendingNull(t *testing.T) { + backend, _ := newTestBackend(t) + client, _ := backend.Attach() + defer backend.Close() + defer client.Close() + + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancel() + + gotPending := make(map[string]interface{}) + err := client.CallContext(ctx, &gotPending, "eth_getBlockByNumber", "pending", false) + if err != nil { + t.Fatal(err) + } + + // iterate wanted null values, checking if they are present and set to 'null' + for _, key := range []string{"nonce"} { + val, ok := gotPending["nonce"] + if !ok { + t.Fatalf("%s: missing key", key) + } + if val != nil { + t.Fatalf("%s: non-nil value: %v", key, val) + } + } +} + func TestBalanceAt(t *testing.T) { backend, _ := newTestBackend(t) client, _ := backend.Attach() diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 6e8ebdc857..478ff34034 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1149,7 +1149,7 @@ type RPCMarshalHeaderT struct { Number *hexutil.Big `json:"number"` Hash *common.Hash `json:"hash,omitempty"` // Pending will be nil ParentHash common.Hash `json:"parentHash"` - Nonce *types.BlockNonce `json:"nonce,omitempty"` // Pending will be nil + Nonce *types.BlockNonce `json:"nonce"` // Pending will be nil MixHash common.Hash `json:"mixHash"` Sha3Uncles common.Hash `json:"sha3Uncles"` LogsBloom types.Bloom `json:"logsBloom"` From be15ecbf4a87320b4ad826c34a1fc2972e15816a Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 09:56:24 -0500 Subject: [PATCH 02/22] ethclient: add test case for expected non-null values ('number') Date: 2021-03-15 09:56:24-05:00 Signed-off-by: meows --- ethclient/ethclient_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index ac29d8d9a6..cc26d20278 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -330,6 +330,19 @@ func TestHeader_PendingNull(t *testing.T) { t.Fatal(err) } + // iterate expected non-null values, checking validity + for k, v := range map[string]interface{}{ + "number": "0x2", + } { + gotVal, ok := gotPending[k] + if !ok { + t.Fatalf("%s: missing key", k) + } + if !reflect.DeepEqual(v, gotVal) { + t.Fatalf("%s: want: %v, got: %v", k, v, gotVal) + } + } + // iterate wanted null values, checking if they are present and set to 'null' for _, key := range []string{"nonce"} { val, ok := gotPending["nonce"] From b446973cd577c2de840813b6116a6e7cb0f22504 Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 10:07:04 -0500 Subject: [PATCH 03/22] ethclient: refactor test, unifying to map of expected value (null, zero-value, and filled) Date: 2021-03-15 10:07:04-05:00 Signed-off-by: meows --- ethclient/ethclient_test.go | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index cc26d20278..df49065221 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -321,18 +321,38 @@ func TestHeader_PendingNull(t *testing.T) { defer backend.Close() defer client.Close() - ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + ctx, cancel := context.WithTimeout(context.Background(), 150*time.Millisecond) defer cancel() + // Have to sleep a little to make sure miner has time to set pending. + time.Sleep(time.Millisecond * 50) + gotPending := make(map[string]interface{}) err := client.CallContext(ctx, &gotPending, "eth_getBlockByNumber", "pending", false) if err != nil { t.Fatal(err) } - // iterate expected non-null values, checking validity + // iterate expected values, checking validity for k, v := range map[string]interface{}{ - "number": "0x2", + // nulls + "nonce": nil, + + // zero-values + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactions": []interface{}{}, + "uncles": []interface{}{}, + + // filled + "number": "0x2", + "parentHash": "0x228d7580ae75567749daa5ed31ff1fcc09803ebe001b44f64b0f364c19bff4cb", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x21a", + "stateRoot": "0x02189854bc38ea675df81794e54a2676230444d87adc7a51bbba0d4cc6519d43", + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + // "timestamp": "0x604f76f4", // incidentally nondeterministic } { gotVal, ok := gotPending[k] if !ok { @@ -342,17 +362,6 @@ func TestHeader_PendingNull(t *testing.T) { t.Fatalf("%s: want: %v, got: %v", k, v, gotVal) } } - - // iterate wanted null values, checking if they are present and set to 'null' - for _, key := range []string{"nonce"} { - val, ok := gotPending["nonce"] - if !ok { - t.Fatalf("%s: missing key", key) - } - if val != nil { - t.Fatalf("%s: non-nil value: %v", key, val) - } - } } func TestBalanceAt(t *testing.T) { From dc4e75cfb11fff74567a5a84fad14672941723bc Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 10:15:24 -0500 Subject: [PATCH 04/22] ethclient: add missing fields to test and compare diff Date: 2021-03-15 10:15:24-05:00 Signed-off-by: meows --- ethclient/ethclient_test.go | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index df49065221..40e4eebc23 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -18,6 +18,7 @@ package ethclient import ( "context" + "encoding/json" "errors" "fmt" "log" @@ -39,6 +40,7 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params/types/genesisT" + "github.com/go-test/deep" meta_schema "github.com/open-rpc/meta-schema" ) @@ -333,8 +335,11 @@ func TestHeader_PendingNull(t *testing.T) { t.Fatal(err) } + b, _ := json.MarshalIndent(gotPending, "", " ") + t.Logf("%s", string(b)) + // iterate expected values, checking validity - for k, v := range map[string]interface{}{ + want := map[string]interface{}{ // nulls "nonce": nil, @@ -346,22 +351,37 @@ func TestHeader_PendingNull(t *testing.T) { // filled "number": "0x2", + "gasLimit": "0x47d5cc", + "gasUsed": "0x0", + "difficulty": "0x20000", + "size": "0x21a", "parentHash": "0x228d7580ae75567749daa5ed31ff1fcc09803ebe001b44f64b0f364c19bff4cb", + "extraData": "0xda83010b1788436f72654765746886676f312e3135856c696e7578", + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "size": "0x21a", "stateRoot": "0x02189854bc38ea675df81794e54a2676230444d87adc7a51bbba0d4cc6519d43", - "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - // "timestamp": "0x604f76f4", // incidentally nondeterministic - } { + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "timestamp": "0x604f76f4", // incidentally nondeterministic; special case + } + for k, v := range want { gotVal, ok := gotPending[k] if !ok { t.Fatalf("%s: missing key", k) } + // special case (indeterminate time) + if k == "timestamp" { + gotVal = want["timestamp"] + gotPending["timestamp"] = gotVal + } if !reflect.DeepEqual(v, gotVal) { t.Fatalf("%s: want: %v, got: %v", k, v, gotVal) } } + if len(want) != len(gotPending) { + for _, diff := range deep.Equal(want, gotPending) { + t.Errorf("[want/got] +/-: %s", diff) + } + } } func TestBalanceAt(t *testing.T) { From f0cd46064397b8eff03e06f2691166be1193a399 Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 10:16:52 -0500 Subject: [PATCH 05/22] ethclient,internal/ethapi: add Hash (null) to pending block JSON Date: 2021-03-15 10:16:52-05:00 Signed-off-by: meows --- ethclient/ethclient_test.go | 1 + internal/ethapi/api.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index 40e4eebc23..39e05f4a41 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -342,6 +342,7 @@ func TestHeader_PendingNull(t *testing.T) { want := map[string]interface{}{ // nulls "nonce": nil, + "hash": nil, // zero-values "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 478ff34034..b9f5670fac 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1147,7 +1147,7 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} { // RPCMarshalHeaderT defines the RPC marshaling type for block headers. type RPCMarshalHeaderT struct { Number *hexutil.Big `json:"number"` - Hash *common.Hash `json:"hash,omitempty"` // Pending will be nil + Hash *common.Hash `json:"hash"` // Pending will be nil ParentHash common.Hash `json:"parentHash"` Nonce *types.BlockNonce `json:"nonce"` // Pending will be nil MixHash common.Hash `json:"mixHash"` From b3140ce5d2983bc01706b453e3be253a0dadebda Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 10:21:40 -0500 Subject: [PATCH 06/22] ethclient: assert positive generic match on special case timestamp Date: 2021-03-15 10:21:40-05:00 Signed-off-by: meows --- ethclient/ethclient_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index 39e05f4a41..94b29ba7ee 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -24,6 +24,7 @@ import ( "log" "math/big" "reflect" + "regexp" "testing" "time" @@ -371,6 +372,9 @@ func TestHeader_PendingNull(t *testing.T) { } // special case (indeterminate time) if k == "timestamp" { + if !regexp.MustCompile(fmt.Sprintf(`^0x[a-zA-Z0-9]{%d}`, len("604f76f4"))).MatchString(gotVal.(string)) { + t.Fatalf("%s: unexpected value: %v", k, gotVal) + } gotVal = want["timestamp"] gotPending["timestamp"] = gotVal } From 0e6d6562eabf7c080a364ec8f2084afbd992c3ea Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 10:44:45 -0500 Subject: [PATCH 07/22] ethclient,internal/ethapi: jsonrpc header marshaling assign totalDifficulty by calculation if DNE Date: 2021-03-15 10:44:45-05:00 Signed-off-by: meows --- ethclient/ethclient_test.go | 5 ++++- internal/ethapi/api.go | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index 94b29ba7ee..b88e51bc3c 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -41,6 +41,7 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params/types/genesisT" + "github.com/ethereum/go-ethereum/params/vars" "github.com/go-test/deep" meta_schema "github.com/open-rpc/meta-schema" ) @@ -340,6 +341,7 @@ func TestHeader_PendingNull(t *testing.T) { t.Logf("%s", string(b)) // iterate expected values, checking validity + wantBlockNumber := big.NewInt(2) want := map[string]interface{}{ // nulls "nonce": nil, @@ -352,10 +354,11 @@ func TestHeader_PendingNull(t *testing.T) { "uncles": []interface{}{}, // filled - "number": "0x2", + "number": (*hexutil.Big)(wantBlockNumber).String(), "gasLimit": "0x47d5cc", "gasUsed": "0x0", "difficulty": "0x20000", + "totalDifficulty": (*hexutil.Big)(new(big.Int).Mul(vars.MinimumDifficulty, wantBlockNumber)).String(), "size": "0x21a", "parentHash": "0x228d7580ae75567749daa5ed31ff1fcc09803ebe001b44f64b0f364c19bff4cb", "extraData": "0xda83010b1788436f72654765746886676f312e3135856c696e7578", diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index b9f5670fac..87279c8421 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1204,6 +1204,12 @@ func (s *PublicBlockChainAPI) rpcMarshalHeaderTSetTotalDifficulty(ctx context.Co hash = &c } header.TotalDifficulty = (*hexutil.Big)(s.b.GetTd(ctx, *hash)) + if header.TotalDifficulty == nil || header.TotalDifficulty.ToInt().Cmp(common.Big0) == 0 { + if header.ParentHash != (common.Hash{}) && header.Difficulty != nil { + td := (*hexutil.Big)(s.b.GetTd(ctx, header.ParentHash)) + header.TotalDifficulty = (*hexutil.Big)(td.ToInt().Add(td.ToInt(), header.Difficulty.ToInt())) + } + } } // setAsPending sets fields that must be nil for pending headers and blocks. From f11d477ba9f2e7f7b722eaba2658ba07e2587a52 Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 10:55:14 -0500 Subject: [PATCH 08/22] internal/ethapi: add Miner (null) to pending block JSON Date: 2021-03-15 10:55:14-05:00 Signed-off-by: meows --- internal/ethapi/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 87279c8421..fe48b3c7e0 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1154,7 +1154,7 @@ type RPCMarshalHeaderT struct { Sha3Uncles common.Hash `json:"sha3Uncles"` LogsBloom types.Bloom `json:"logsBloom"` StateRoot common.Hash `json:"stateRoot"` - Miner *common.Address `json:"miner,omitempty"` // Pending will be nil + Miner *common.Address `json:"miner"` // Pending will be nil Difficulty *hexutil.Big `json:"difficulty"` TotalDifficulty *hexutil.Big `json:"totalDifficulty"` ExtraData hexutil.Bytes `json:"extraData"` From 6b3f22360b145864988916784f2df6a674a805b7 Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 11:15:25 -0500 Subject: [PATCH 09/22] ethclient,internal/ethapi: wip: totalDifficulty is fucked up; either incrementing too big or nil Date: 2021-03-15 11:15:25-05:00 Signed-off-by: meows --- ethclient/ethclient_test.go | 107 ++++++++++++++++++------------------ internal/ethapi/api.go | 31 ++++++----- 2 files changed, 69 insertions(+), 69 deletions(-) diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index b88e51bc3c..755f96c567 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -18,7 +18,6 @@ package ethclient import ( "context" - "encoding/json" "errors" "fmt" "log" @@ -325,67 +324,67 @@ func TestHeader_PendingNull(t *testing.T) { defer backend.Close() defer client.Close() - ctx, cancel := context.WithTimeout(context.Background(), 150*time.Millisecond) + ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) defer cancel() // Have to sleep a little to make sure miner has time to set pending. - time.Sleep(time.Millisecond * 50) + time.Sleep(time.Millisecond * 100) - gotPending := make(map[string]interface{}) - err := client.CallContext(ctx, &gotPending, "eth_getBlockByNumber", "pending", false) - if err != nil { - t.Fatal(err) - } + for _, fullTxes := range []bool{true, false, true} { + gotPending := make(map[string]interface{}) + err := client.CallContext(ctx, &gotPending, "eth_getBlockByNumber", "pending", fullTxes) + if err != nil { + t.Fatal(err) + } - b, _ := json.MarshalIndent(gotPending, "", " ") - t.Logf("%s", string(b)) - - // iterate expected values, checking validity - wantBlockNumber := big.NewInt(2) - want := map[string]interface{}{ - // nulls - "nonce": nil, - "hash": nil, - - // zero-values - "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "transactions": []interface{}{}, - "uncles": []interface{}{}, - - // filled - "number": (*hexutil.Big)(wantBlockNumber).String(), - "gasLimit": "0x47d5cc", - "gasUsed": "0x0", - "difficulty": "0x20000", - "totalDifficulty": (*hexutil.Big)(new(big.Int).Mul(vars.MinimumDifficulty, wantBlockNumber)).String(), - "size": "0x21a", - "parentHash": "0x228d7580ae75567749daa5ed31ff1fcc09803ebe001b44f64b0f364c19bff4cb", - "extraData": "0xda83010b1788436f72654765746886676f312e3135856c696e7578", - "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "stateRoot": "0x02189854bc38ea675df81794e54a2676230444d87adc7a51bbba0d4cc6519d43", - "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "timestamp": "0x604f76f4", // incidentally nondeterministic; special case - } - for k, v := range want { - gotVal, ok := gotPending[k] - if !ok { - t.Fatalf("%s: missing key", k) + // Iterate expected values, checking validity. + wantBlockNumber := big.NewInt(2) + want := map[string]interface{}{ + // Nulls. + "nonce": nil, + "hash": nil, + "miner": nil, + + // Zero-values. + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactions": []interface{}{}, + "uncles": []interface{}{}, + + // Filled. + "number": (*hexutil.Big)(wantBlockNumber).String(), + "gasLimit": "0x47d5cc", + "gasUsed": "0x0", + "difficulty": "0x20000", + "totalDifficulty": (*hexutil.Big)(new(big.Int).Mul(vars.MinimumDifficulty, wantBlockNumber)).String(), + "size": "0x21a", + "parentHash": "0x228d7580ae75567749daa5ed31ff1fcc09803ebe001b44f64b0f364c19bff4cb", + "extraData": "0xda83010b1788436f72654765746886676f312e3135856c696e7578", + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot": "0x02189854bc38ea675df81794e54a2676230444d87adc7a51bbba0d4cc6519d43", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "timestamp": "0x604f76f4", // Incidentally nondeterministic; special case. } - // special case (indeterminate time) - if k == "timestamp" { - if !regexp.MustCompile(fmt.Sprintf(`^0x[a-zA-Z0-9]{%d}`, len("604f76f4"))).MatchString(gotVal.(string)) { - t.Fatalf("%s: unexpected value: %v", k, gotVal) + for k, v := range want { + gotVal, ok := gotPending[k] + if !ok { + t.Errorf("%s: missing key", k) + } + // Special case (indeterminate time). + if k == "timestamp" { + if !regexp.MustCompile(fmt.Sprintf(`^0x[a-zA-Z0-9]{%d}`, len("604f76f4"))).MatchString(gotVal.(string)) { + t.Errorf("%s: unexpected value: %v", k, gotVal) + } + gotVal = want["timestamp"] + gotPending["timestamp"] = gotVal + } + if !reflect.DeepEqual(v, gotVal) { + t.Errorf("%s: want: %v, got: %v", k, v, gotVal) } - gotVal = want["timestamp"] - gotPending["timestamp"] = gotVal - } - if !reflect.DeepEqual(v, gotVal) { - t.Fatalf("%s: want: %v, got: %v", k, v, gotVal) } - } - if len(want) != len(gotPending) { + + // Slightly redundant, but additionally checks the occurrence of got -> want (supplementing want -> got). for _, diff := range deep.Equal(want, gotPending) { t.Errorf("[want/got] +/-: %s", diff) } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index fe48b3c7e0..de27c08f7c 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1147,14 +1147,14 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} { // RPCMarshalHeaderT defines the RPC marshaling type for block headers. type RPCMarshalHeaderT struct { Number *hexutil.Big `json:"number"` - Hash *common.Hash `json:"hash"` // Pending will be nil + Hash *common.Hash `json:"hash"` // -- Pending will be nil -- ParentHash common.Hash `json:"parentHash"` - Nonce *types.BlockNonce `json:"nonce"` // Pending will be nil + Nonce *types.BlockNonce `json:"nonce"` // -- Pending will be nil -- MixHash common.Hash `json:"mixHash"` Sha3Uncles common.Hash `json:"sha3Uncles"` LogsBloom types.Bloom `json:"logsBloom"` StateRoot common.Hash `json:"stateRoot"` - Miner *common.Address `json:"miner"` // Pending will be nil + Miner *common.Address `json:"miner"` // -- Pending will be nil -- Difficulty *hexutil.Big `json:"difficulty"` TotalDifficulty *hexutil.Big `json:"totalDifficulty"` ExtraData hexutil.Bytes `json:"extraData"` @@ -1198,18 +1198,19 @@ func NewRPCMarshalHeaderTFromHeader(header *types.Header) *RPCMarshalHeaderT { // rpcMarshalHeaderTSetTotalDifficulty sets the total difficulty field for RPC response headers. // If the hash is unavailable (ie in Pending state), the value will be 0. func (s *PublicBlockChainAPI) rpcMarshalHeaderTSetTotalDifficulty(ctx context.Context, header *RPCMarshalHeaderT) { - hash := header.Hash - if hash == nil { - c := common.Hash{} - hash = &c - } - header.TotalDifficulty = (*hexutil.Big)(s.b.GetTd(ctx, *hash)) - if header.TotalDifficulty == nil || header.TotalDifficulty.ToInt().Cmp(common.Big0) == 0 { - if header.ParentHash != (common.Hash{}) && header.Difficulty != nil { - td := (*hexutil.Big)(s.b.GetTd(ctx, header.ParentHash)) - header.TotalDifficulty = (*hexutil.Big)(td.ToInt().Add(td.ToInt(), header.Difficulty.ToInt())) - } - } + header.TotalDifficulty = (*hexutil.Big)(s.b.GetTd(ctx, *header.Hash)) + // hash := header.Hash + // if hash == nil { + // c := common.Hash{} + // hash = &c + // } + // header.TotalDifficulty = (*hexutil.Big)(s.b.GetTd(ctx, *hash)) + // if header.TotalDifficulty == nil || header.TotalDifficulty.ToInt().Cmp(common.Big0) == 0 { + // if header.ParentHash != (common.Hash{}) && header.Difficulty != nil { + // td := (*hexutil.Big)(s.b.GetTd(ctx, header.ParentHash)) + // header.TotalDifficulty = (*hexutil.Big)(td.ToInt().Add(td.ToInt(), header.Difficulty.ToInt())) + // } + // } } // setAsPending sets fields that must be nil for pending headers and blocks. From a6b943eb8af2072429f36030049eacae03e90590 Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 13:35:41 -0500 Subject: [PATCH 10/22] ethclient: move test to 'cg' namespaced file, allows testing @ethereum/go-ethereum Date: 2021-03-15 13:35:41-05:00 Signed-off-by: meows --- ethclient/ethclient_cg_test.go | 135 +++++++++++++++++++++++++++++++++ ethclient/ethclient_test.go | 114 ---------------------------- 2 files changed, 135 insertions(+), 114 deletions(-) create mode 100644 ethclient/ethclient_cg_test.go diff --git a/ethclient/ethclient_cg_test.go b/ethclient/ethclient_cg_test.go new file mode 100644 index 0000000000..01f5fd0cad --- /dev/null +++ b/ethclient/ethclient_cg_test.go @@ -0,0 +1,135 @@ +package ethclient + +import ( + "context" + "fmt" + "log" + "math/big" + "reflect" + "regexp" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/go-test/deep" +) + +func TestHeader_TxesUnclesNotEmpty(t *testing.T) { + backend, _ := newTestBackend(t) + client, _ := backend.Attach() + defer backend.Close() + defer client.Close() + + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancel() + + res := make(map[string]interface{}) + err := client.CallContext(ctx, &res, "eth_getBlockByNumber", "latest", false) + if err != nil { + log.Fatalln(err) + } + + // Sanity check response + if v, ok := res["number"]; !ok { + t.Fatal("missing 'number' field") + } else if n, err := hexutil.DecodeBig(v.(string)); err != nil || n == nil { + t.Fatal(err) + } else if n.Cmp(big.NewInt(1)) != 0 { + t.Fatalf("unexpected 'latest' block number: %v", n) + } + // 'transactions' key should exist as [] + if v, ok := res["transactions"]; !ok { + t.Fatal("missing transactions field") + } else if len(v.([]interface{})) != 0 { + t.Fatal("'transactions' value not []") + } + // 'uncles' key should exist as [] + if v, ok := res["uncles"]; !ok { + t.Fatal("missing uncles field") + } else if len(v.([]interface{})) != 0 { + t.Fatal("'uncles' value not []'") + } +} + +func TestHeader_PendingNull(t *testing.T) { + backend, _ := newTestBackend(t) + client, _ := backend.Attach() + defer backend.Close() + defer client.Close() + + ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) + defer cancel() + + // Have to sleep a little to make sure miner has time to set pending. + time.Sleep(time.Millisecond * 100) + + // Get a reference block. + parent, err := NewClient(client).HeaderByNumber(ctx, nil) + if err != nil { + t.Fatal(err) + } + if parent == nil { + t.Fatal("bad test") + } + + for _, fullTxes := range []bool{true, false} { + gotPending := make(map[string]interface{}) + err := client.CallContext(ctx, &gotPending, "eth_getBlockByNumber", "pending", fullTxes) + if err != nil { + t.Fatal(err) + } + + // Iterate expected values, checking validity. + wantBlockNumber := big.NewInt(2) + want := map[string]interface{}{ + // Nulls. + "nonce": nil, + "hash": nil, + "miner": nil, + // "totalDifficulty": (*hexutil.Big)(new(big.Int).Mul(vars.MinimumDifficulty, wantBlockNumber)).String(), + "totalDifficulty": nil, + + // Zero-values. + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactions": []interface{}{}, + "uncles": []interface{}{}, + + // Filled. + "number": (*hexutil.Big)(wantBlockNumber).String(), + "gasLimit": "0x47d5cc", + "gasUsed": hexutil.Uint64(0).String(), + "difficulty": (*hexutil.Big)(parent.Difficulty).String(), + "size": "0x21a", + "parentHash": parent.Hash().Hex(), + "extraData": "0xda83010b1788436f72654765746886676f312e3135856c696e7578", + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot": "0x02189854bc38ea675df81794e54a2676230444d87adc7a51bbba0d4cc6519d43", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "timestamp": "0x604f76f4", // Incidentally nondeterministic; special case. + } + for k, v := range want { + gotVal, ok := gotPending[k] + if !ok { + t.Errorf("%s: missing key", k) + } + // Special case (indeterminate time). + if k == "timestamp" { + if !regexp.MustCompile(fmt.Sprintf(`^0x[a-zA-Z0-9]{%d}`, len("604f76f4"))).MatchString(gotVal.(string)) { + t.Errorf("%s: unexpected value: %v", k, gotVal) + } + gotVal = want["timestamp"] + gotPending["timestamp"] = gotVal + } + if !reflect.DeepEqual(v, gotVal) { + t.Errorf("%s: want: %v, got: %v", k, v, gotVal) + } + } + + // Slightly redundant, but additionally checks the occurrence of got -> want (supplementing want -> got). + for _, diff := range deep.Equal(want, gotPending) { + t.Errorf("[want/got] +/-: %s", diff) + } + } +} diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index 755f96c567..89afd906b2 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -20,10 +20,8 @@ import ( "context" "errors" "fmt" - "log" "math/big" "reflect" - "regexp" "testing" "time" @@ -40,8 +38,6 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params/types/genesisT" - "github.com/ethereum/go-ethereum/params/vars" - "github.com/go-test/deep" meta_schema "github.com/open-rpc/meta-schema" ) @@ -281,116 +277,6 @@ func TestHeader(t *testing.T) { } } -func TestHeader_TxesUnclesNotEmpty(t *testing.T) { - backend, _ := newTestBackend(t) - client, _ := backend.Attach() - defer backend.Close() - defer client.Close() - - ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) - defer cancel() - - res := make(map[string]interface{}) - err := client.CallContext(ctx, &res, "eth_getBlockByNumber", "latest", false) - if err != nil { - log.Fatalln(err) - } - - // Sanity check response - if v, ok := res["number"]; !ok { - t.Fatal("missing 'number' field") - } else if n, err := hexutil.DecodeBig(v.(string)); err != nil || n == nil { - t.Fatal(err) - } else if n.Cmp(big.NewInt(1)) != 0 { - t.Fatalf("unexpected 'latest' block number: %v", n) - } - // 'transactions' key should exist as [] - if v, ok := res["transactions"]; !ok { - t.Fatal("missing transactions field") - } else if len(v.([]interface{})) != 0 { - t.Fatal("'transactions' value not []") - } - // 'uncles' key should exist as [] - if v, ok := res["uncles"]; !ok { - t.Fatal("missing uncles field") - } else if len(v.([]interface{})) != 0 { - t.Fatal("'uncles' value not []'") - } -} - -func TestHeader_PendingNull(t *testing.T) { - backend, _ := newTestBackend(t) - client, _ := backend.Attach() - defer backend.Close() - defer client.Close() - - ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) - defer cancel() - - // Have to sleep a little to make sure miner has time to set pending. - time.Sleep(time.Millisecond * 100) - - for _, fullTxes := range []bool{true, false, true} { - gotPending := make(map[string]interface{}) - err := client.CallContext(ctx, &gotPending, "eth_getBlockByNumber", "pending", fullTxes) - if err != nil { - t.Fatal(err) - } - - // Iterate expected values, checking validity. - wantBlockNumber := big.NewInt(2) - want := map[string]interface{}{ - // Nulls. - "nonce": nil, - "hash": nil, - "miner": nil, - - // Zero-values. - "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "transactions": []interface{}{}, - "uncles": []interface{}{}, - - // Filled. - "number": (*hexutil.Big)(wantBlockNumber).String(), - "gasLimit": "0x47d5cc", - "gasUsed": "0x0", - "difficulty": "0x20000", - "totalDifficulty": (*hexutil.Big)(new(big.Int).Mul(vars.MinimumDifficulty, wantBlockNumber)).String(), - "size": "0x21a", - "parentHash": "0x228d7580ae75567749daa5ed31ff1fcc09803ebe001b44f64b0f364c19bff4cb", - "extraData": "0xda83010b1788436f72654765746886676f312e3135856c696e7578", - "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "stateRoot": "0x02189854bc38ea675df81794e54a2676230444d87adc7a51bbba0d4cc6519d43", - "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "timestamp": "0x604f76f4", // Incidentally nondeterministic; special case. - } - for k, v := range want { - gotVal, ok := gotPending[k] - if !ok { - t.Errorf("%s: missing key", k) - } - // Special case (indeterminate time). - if k == "timestamp" { - if !regexp.MustCompile(fmt.Sprintf(`^0x[a-zA-Z0-9]{%d}`, len("604f76f4"))).MatchString(gotVal.(string)) { - t.Errorf("%s: unexpected value: %v", k, gotVal) - } - gotVal = want["timestamp"] - gotPending["timestamp"] = gotVal - } - if !reflect.DeepEqual(v, gotVal) { - t.Errorf("%s: want: %v, got: %v", k, v, gotVal) - } - } - - // Slightly redundant, but additionally checks the occurrence of got -> want (supplementing want -> got). - for _, diff := range deep.Equal(want, gotPending) { - t.Errorf("[want/got] +/-: %s", diff) - } - } -} - func TestBalanceAt(t *testing.T) { backend, _ := newTestBackend(t) client, _ := backend.Attach() From 0b3cf0d92badedbee68c908b599a8c0327b325f1 Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 14:16:06 -0500 Subject: [PATCH 11/22] ethclient: refactor test to be generic using pattern matching Date: 2021-03-15 14:16:06-05:00 Signed-off-by: meows --- ethclient/ethclient_cg_test.go | 94 ++++++++++++++++------------------ 1 file changed, 45 insertions(+), 49 deletions(-) diff --git a/ethclient/ethclient_cg_test.go b/ethclient/ethclient_cg_test.go index 01f5fd0cad..585894f95c 100644 --- a/ethclient/ethclient_cg_test.go +++ b/ethclient/ethclient_cg_test.go @@ -2,16 +2,17 @@ package ethclient import ( "context" + "encoding/json" "fmt" "log" "math/big" - "reflect" "regexp" "testing" "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/go-test/deep" + "github.com/ethereum/go-ethereum/core/types" ) func TestHeader_TxesUnclesNotEmpty(t *testing.T) { @@ -72,64 +73,59 @@ func TestHeader_PendingNull(t *testing.T) { t.Fatal("bad test") } + reNull := regexp.MustCompile(`^null$`) + reHexAnyLen := regexp.MustCompile(`^"0x[a-zA-Z0-9]+"$`) + reHexHashLen := regexp.MustCompile(fmt.Sprintf(`^"0x[a-zA-Z0-9]{%d}"$`, common.HashLength*2)) + + cases := map[string]*regexp.Regexp{ + "nonce": reNull, + "hash": reNull, + "miner": reNull, + + "totalDifficulty": reNull, + + "mixHash": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, common.HashLength*2)), + "logsBloom": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, types.BloomByteLength*2)), + + "number": reHexAnyLen, + "difficulty": reHexAnyLen, + "gasLimit": reHexAnyLen, + "gasUsed": reHexAnyLen, + "size": reHexAnyLen, + "timestamp": reHexAnyLen, + "extraData": reHexAnyLen, + + "parentHash": reHexHashLen, + "transactionsRoot": reHexHashLen, + "stateRoot": reHexHashLen, + "receiptsRoot": reHexHashLen, + "sha3Uncles": reHexHashLen, + + "uncles": regexp.MustCompile(`^\[\]$`), + "transactions": regexp.MustCompile(`^\[\]$`), + } + for _, fullTxes := range []bool{true, false} { - gotPending := make(map[string]interface{}) + gotPending := make(map[string]json.RawMessage) err := client.CallContext(ctx, &gotPending, "eth_getBlockByNumber", "pending", fullTxes) if err != nil { t.Fatal(err) } - // Iterate expected values, checking validity. - wantBlockNumber := big.NewInt(2) - want := map[string]interface{}{ - // Nulls. - "nonce": nil, - "hash": nil, - "miner": nil, - // "totalDifficulty": (*hexutil.Big)(new(big.Int).Mul(vars.MinimumDifficulty, wantBlockNumber)).String(), - "totalDifficulty": nil, - - // Zero-values. - "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "transactions": []interface{}{}, - "uncles": []interface{}{}, - - // Filled. - "number": (*hexutil.Big)(wantBlockNumber).String(), - "gasLimit": "0x47d5cc", - "gasUsed": hexutil.Uint64(0).String(), - "difficulty": (*hexutil.Big)(parent.Difficulty).String(), - "size": "0x21a", - "parentHash": parent.Hash().Hex(), - "extraData": "0xda83010b1788436f72654765746886676f312e3135856c696e7578", - "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "stateRoot": "0x02189854bc38ea675df81794e54a2676230444d87adc7a51bbba0d4cc6519d43", - "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "timestamp": "0x604f76f4", // Incidentally nondeterministic; special case. - } - for k, v := range want { - gotVal, ok := gotPending[k] + for key, re := range cases { + gotVal, ok := gotPending[key] if !ok { - t.Errorf("%s: missing key", k) + t.Errorf("%s: missing key", key) } - // Special case (indeterminate time). - if k == "timestamp" { - if !regexp.MustCompile(fmt.Sprintf(`^0x[a-zA-Z0-9]{%d}`, len("604f76f4"))).MatchString(gotVal.(string)) { - t.Errorf("%s: unexpected value: %v", k, gotVal) - } - gotVal = want["timestamp"] - gotPending["timestamp"] = gotVal - } - if !reflect.DeepEqual(v, gotVal) { - t.Errorf("%s: want: %v, got: %v", k, v, gotVal) + if !re.Match(gotVal) { + t.Errorf("%s want: %v, got: %v", key, re, string(gotVal)) } } - // Slightly redundant, but additionally checks the occurrence of got -> want (supplementing want -> got). - for _, diff := range deep.Equal(want, gotPending) { - t.Errorf("[want/got] +/-: %s", diff) + for k, v := range gotPending { + if _, ok := cases[k]; !ok { + t.Errorf("%s: missing key (value: %v)", k, string(v)) + } } } } From 97e24db6748ad01a830db55744df7a9c88f51c00 Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 14:17:42 -0500 Subject: [PATCH 12/22] ethclient: add Latest suffix for a more descriptive test name Date: 2021-03-15 14:17:42-05:00 Signed-off-by: meows --- ethclient/ethclient_cg_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethclient/ethclient_cg_test.go b/ethclient/ethclient_cg_test.go index 585894f95c..b8312b9aeb 100644 --- a/ethclient/ethclient_cg_test.go +++ b/ethclient/ethclient_cg_test.go @@ -15,7 +15,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" ) -func TestHeader_TxesUnclesNotEmpty(t *testing.T) { +func TestHeader_TxesUnclesNotEmptyLatest(t *testing.T) { backend, _ := newTestBackend(t) client, _ := backend.Attach() defer backend.Close() From e52bcad01de808eb57907bfd21cbe909338333a3 Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 14:48:05 -0500 Subject: [PATCH 13/22] ethclient: extrapolate regex-based test to earliest,latest,pending Date: 2021-03-15 14:48:05-05:00 Signed-off-by: meows --- ethclient/ethclient_cg_test.go | 148 +++++++++++++++++++++++---------- 1 file changed, 102 insertions(+), 46 deletions(-) diff --git a/ethclient/ethclient_cg_test.go b/ethclient/ethclient_cg_test.go index b8312b9aeb..6edbbeb853 100644 --- a/ethclient/ethclient_cg_test.go +++ b/ethclient/ethclient_cg_test.go @@ -52,7 +52,7 @@ func TestHeader_TxesUnclesNotEmptyLatest(t *testing.T) { } } -func TestHeader_PendingNull(t *testing.T) { +func Test_EthGetBlockJSONResponse(t *testing.T) { backend, _ := newTestBackend(t) client, _ := backend.Attach() defer backend.Close() @@ -77,54 +77,110 @@ func TestHeader_PendingNull(t *testing.T) { reHexAnyLen := regexp.MustCompile(`^"0x[a-zA-Z0-9]+"$`) reHexHashLen := regexp.MustCompile(fmt.Sprintf(`^"0x[a-zA-Z0-9]{%d}"$`, common.HashLength*2)) - cases := map[string]*regexp.Regexp{ - "nonce": reNull, - "hash": reNull, - "miner": reNull, - - "totalDifficulty": reNull, - - "mixHash": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, common.HashLength*2)), - "logsBloom": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, types.BloomByteLength*2)), - - "number": reHexAnyLen, - "difficulty": reHexAnyLen, - "gasLimit": reHexAnyLen, - "gasUsed": reHexAnyLen, - "size": reHexAnyLen, - "timestamp": reHexAnyLen, - "extraData": reHexAnyLen, - - "parentHash": reHexHashLen, - "transactionsRoot": reHexHashLen, - "stateRoot": reHexHashLen, - "receiptsRoot": reHexHashLen, - "sha3Uncles": reHexHashLen, - - "uncles": regexp.MustCompile(`^\[\]$`), - "transactions": regexp.MustCompile(`^\[\]$`), - } - - for _, fullTxes := range []bool{true, false} { - gotPending := make(map[string]json.RawMessage) - err := client.CallContext(ctx, &gotPending, "eth_getBlockByNumber", "pending", fullTxes) - if err != nil { - t.Fatal(err) - } - - for key, re := range cases { - gotVal, ok := gotPending[key] - if !ok { - t.Errorf("%s: missing key", key) + for blockHeight, cases := range map[string]map[string]*regexp.Regexp{ + "earliest": { + "nonce": reHexAnyLen, + "hash": reHexAnyLen, + "miner": regexp.MustCompile(fmt.Sprintf(`^"0x[a-zA-Z0-9]{%d}"$`, common.AddressLength*2)), + + "totalDifficulty": reHexAnyLen, + + "mixHash": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, common.HashLength*2)), + "logsBloom": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, types.BloomByteLength*2)), + + "number": reHexAnyLen, + "difficulty": reHexAnyLen, + "gasLimit": reHexAnyLen, + "gasUsed": reHexAnyLen, + "size": reHexAnyLen, + "timestamp": reHexAnyLen, + "extraData": reHexAnyLen, + + "parentHash": reHexHashLen, + "transactionsRoot": reHexHashLen, + "stateRoot": reHexHashLen, + "receiptsRoot": reHexHashLen, + "sha3Uncles": reHexHashLen, + + "uncles": regexp.MustCompile(`^\[\]$`), + "transactions": regexp.MustCompile(`^\[\]$`), + }, + "latest": { + "nonce": reHexAnyLen, + "hash": reHexAnyLen, + "miner": regexp.MustCompile(fmt.Sprintf(`^"0x[a-zA-Z0-9]{%d}"$`, common.AddressLength*2)), + + "totalDifficulty": reHexAnyLen, + + "mixHash": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, common.HashLength*2)), + "logsBloom": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, types.BloomByteLength*2)), + + "number": reHexAnyLen, + "difficulty": reHexAnyLen, + "gasLimit": reHexAnyLen, + "gasUsed": reHexAnyLen, + "size": reHexAnyLen, + "timestamp": reHexAnyLen, + "extraData": reHexAnyLen, + + "parentHash": reHexHashLen, + "transactionsRoot": reHexHashLen, + "stateRoot": reHexHashLen, + "receiptsRoot": reHexHashLen, + "sha3Uncles": reHexHashLen, + + "uncles": regexp.MustCompile(`^\[\]$`), + "transactions": regexp.MustCompile(`^\[\]$`), + }, + "pending": { + "nonce": reNull, + "hash": reNull, + "miner": reNull, + + "totalDifficulty": reNull, + + "mixHash": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, common.HashLength*2)), + "logsBloom": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, types.BloomByteLength*2)), + + "number": reHexAnyLen, + "difficulty": reHexAnyLen, + "gasLimit": reHexAnyLen, + "gasUsed": reHexAnyLen, + "size": reHexAnyLen, + "timestamp": reHexAnyLen, + "extraData": reHexAnyLen, + + "parentHash": reHexHashLen, + "transactionsRoot": reHexHashLen, + "stateRoot": reHexHashLen, + "receiptsRoot": reHexHashLen, + "sha3Uncles": reHexHashLen, + + "uncles": regexp.MustCompile(`^\[\]$`), + "transactions": regexp.MustCompile(`^\[\]$`), + }, + } { + for _, fullTxes := range []bool{true, false} { + gotPending := make(map[string]json.RawMessage) + err := client.CallContext(ctx, &gotPending, "eth_getBlockByNumber", blockHeight, fullTxes) + if err != nil { + t.Fatal(err) } - if !re.Match(gotVal) { - t.Errorf("%s want: %v, got: %v", key, re, string(gotVal)) + + for key, re := range cases { + gotVal, ok := gotPending[key] + if !ok { + t.Errorf("%s: missing key", key) + } + if !re.Match(gotVal) { + t.Errorf("%s want: %v, got: %v", key, re, string(gotVal)) + } } - } - for k, v := range gotPending { - if _, ok := cases[k]; !ok { - t.Errorf("%s: missing key (value: %v)", k, string(v)) + for k, v := range gotPending { + if _, ok := cases[k]; !ok { + t.Errorf("%s: missing key (value: %v)", k, string(v)) + } } } } From cfcf223213cbaa49fedba25220c0786dc0b8a486 Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 14:50:05 -0500 Subject: [PATCH 14/22] ethclient: rename test to be more conventional and descriptive Date: 2021-03-15 14:50:05-05:00 Signed-off-by: meows --- ethclient/ethclient_cg_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethclient/ethclient_cg_test.go b/ethclient/ethclient_cg_test.go index 6edbbeb853..5ebafcf498 100644 --- a/ethclient/ethclient_cg_test.go +++ b/ethclient/ethclient_cg_test.go @@ -52,7 +52,7 @@ func TestHeader_TxesUnclesNotEmptyLatest(t *testing.T) { } } -func Test_EthGetBlockJSONResponse(t *testing.T) { +func TestEthGetBlock_ValidJSONResponse(t *testing.T) { backend, _ := newTestBackend(t) client, _ := backend.Attach() defer backend.Close() From 14ec0c0172af84d6ca68e4a92836632c7e46ade8 Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 14:50:19 -0500 Subject: [PATCH 15/22] internal/ethapi: remove dead wip-commented code Date: 2021-03-15 14:50:19-05:00 Signed-off-by: meows --- internal/ethapi/api.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index de27c08f7c..da8bc91e09 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1199,18 +1199,6 @@ func NewRPCMarshalHeaderTFromHeader(header *types.Header) *RPCMarshalHeaderT { // If the hash is unavailable (ie in Pending state), the value will be 0. func (s *PublicBlockChainAPI) rpcMarshalHeaderTSetTotalDifficulty(ctx context.Context, header *RPCMarshalHeaderT) { header.TotalDifficulty = (*hexutil.Big)(s.b.GetTd(ctx, *header.Hash)) - // hash := header.Hash - // if hash == nil { - // c := common.Hash{} - // hash = &c - // } - // header.TotalDifficulty = (*hexutil.Big)(s.b.GetTd(ctx, *hash)) - // if header.TotalDifficulty == nil || header.TotalDifficulty.ToInt().Cmp(common.Big0) == 0 { - // if header.ParentHash != (common.Hash{}) && header.Difficulty != nil { - // td := (*hexutil.Big)(s.b.GetTd(ctx, header.ParentHash)) - // header.TotalDifficulty = (*hexutil.Big)(td.ToInt().Add(td.ToInt(), header.Difficulty.ToInt())) - // } - // } } // setAsPending sets fields that must be nil for pending headers and blocks. From 4fd659c21a1e1af86b556c3dce08d943e06c7295 Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 15:16:47 -0500 Subject: [PATCH 16/22] .github/workflows,ethclient: init Github Actions CI workflow to test go-ethereum 1:1 cases Date: 2021-03-15 15:16:47-05:00 Signed-off-by: meows --- .github/workflows/geth_1to1.yml | 31 +++++++++++++++++++++ ethclient/ethclient_cg_test.go | 49 ++++++--------------------------- 2 files changed, 40 insertions(+), 40 deletions(-) create mode 100644 .github/workflows/geth_1to1.yml diff --git a/.github/workflows/geth_1to1.yml b/.github/workflows/geth_1to1.yml new file mode 100644 index 0000000000..788bda2923 --- /dev/null +++ b/.github/workflows/geth_1to1.yml @@ -0,0 +1,31 @@ +name: "Go-Ethereum 1:1" +on: + push: + branches: + - 'master' +jobs: + compare: + runs-on: ubuntu-latest + steps: + - name: Set up Go 1.x + uses: actions/setup-go@v2 + with: + go-version: ^1.16 + id: go + + - name: Check out ethereum/go-ethereum code into the Go module directory + uses: actions/checkout@v2 + with: + repository: 'ethereum/go-ethereum' + ref: 'v1.9.25' + submodules: 'recursive' # Not necessary, but complete. + + - name: Fetch CoreGeth + run: | + git remote add etclabscore https://github.com/etclabscore/core-geth.git + git fetch --depth 1 etclabscore master + + - name: Run Tests + run: | + git checkout etclabscore/master -- ethclient/ethclient_cg_test.go + go test ./ethclient -run _CanCompareGoEthereum diff --git a/ethclient/ethclient_cg_test.go b/ethclient/ethclient_cg_test.go index 5ebafcf498..5f243a3efe 100644 --- a/ethclient/ethclient_cg_test.go +++ b/ethclient/ethclient_cg_test.go @@ -4,55 +4,24 @@ import ( "context" "encoding/json" "fmt" - "log" - "math/big" "regexp" "testing" "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" ) -func TestHeader_TxesUnclesNotEmptyLatest(t *testing.T) { - backend, _ := newTestBackend(t) - client, _ := backend.Attach() - defer backend.Close() - defer client.Close() - - ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) - defer cancel() - - res := make(map[string]interface{}) - err := client.CallContext(ctx, &res, "eth_getBlockByNumber", "latest", false) - if err != nil { - log.Fatalln(err) - } - - // Sanity check response - if v, ok := res["number"]; !ok { - t.Fatal("missing 'number' field") - } else if n, err := hexutil.DecodeBig(v.(string)); err != nil || n == nil { - t.Fatal(err) - } else if n.Cmp(big.NewInt(1)) != 0 { - t.Fatalf("unexpected 'latest' block number: %v", n) - } - // 'transactions' key should exist as [] - if v, ok := res["transactions"]; !ok { - t.Fatal("missing transactions field") - } else if len(v.([]interface{})) != 0 { - t.Fatal("'transactions' value not []") - } - // 'uncles' key should exist as [] - if v, ok := res["uncles"]; !ok { - t.Fatal("missing uncles field") - } else if len(v.([]interface{})) != 0 { - t.Fatal("'uncles' value not []'") - } -} +/* +The '_CanCompareGoEthereum' denotes tests that can be run against ethereum/go-ethereum. +See .github/workflows/geth_1to1.yml for exemplary testing. +*/ -func TestEthGetBlock_ValidJSONResponse(t *testing.T) { +// TestEthGetBlockByNumber_ValidJSONResponse_CanCompareGoEthereum tests that +// JSON RPC API responses to eth_getBlockByNumber meet pattern-based expectations. +// These validations include the null-ness of certain fields for the 'pending' block +// as well existence of all expected keys and values. +func TestEthGetBlockByNumber_ValidJSONResponse_CanCompareGoEthereum(t *testing.T) { backend, _ := newTestBackend(t) client, _ := backend.Attach() defer backend.Close() From 3b7de793ff39d16d77450cffebc42f3c23480f16 Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 15:23:31 -0500 Subject: [PATCH 17/22] ethclient: use go subtest syntax for better descriptiveness and logging Date: 2021-03-15 15:23:31-05:00 Signed-off-by: meows --- ethclient/ethclient_cg_test.go | 162 +++++++++++++-------------------- 1 file changed, 64 insertions(+), 98 deletions(-) diff --git a/ethclient/ethclient_cg_test.go b/ethclient/ethclient_cg_test.go index 5f243a3efe..59bb08aa66 100644 --- a/ethclient/ethclient_cg_test.go +++ b/ethclient/ethclient_cg_test.go @@ -46,111 +46,77 @@ func TestEthGetBlockByNumber_ValidJSONResponse_CanCompareGoEthereum(t *testing.T reHexAnyLen := regexp.MustCompile(`^"0x[a-zA-Z0-9]+"$`) reHexHashLen := regexp.MustCompile(fmt.Sprintf(`^"0x[a-zA-Z0-9]{%d}"$`, common.HashLength*2)) + // completeBlockExpectations define expectations for 'earliest' and 'latest' blocks. + completeBlockExpectations := map[string]*regexp.Regexp{ + "nonce": reHexAnyLen, + "hash": reHexAnyLen, + "miner": regexp.MustCompile(fmt.Sprintf(`^"0x[a-zA-Z0-9]{%d}"$`, common.AddressLength*2)), + + "totalDifficulty": reHexAnyLen, + + "mixHash": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, common.HashLength*2)), + "logsBloom": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, types.BloomByteLength*2)), + + "number": reHexAnyLen, + "difficulty": reHexAnyLen, + "gasLimit": reHexAnyLen, + "gasUsed": reHexAnyLen, + "size": reHexAnyLen, + "timestamp": reHexAnyLen, + "extraData": reHexAnyLen, + + "parentHash": reHexHashLen, + "transactionsRoot": reHexHashLen, + "stateRoot": reHexHashLen, + "receiptsRoot": reHexHashLen, + "sha3Uncles": reHexHashLen, + + "uncles": regexp.MustCompile(`^\[\]$`), + "transactions": regexp.MustCompile(`^\[\]$`), + } + + // Construct the 'pending' block expectations as a copy of the concrete block + // expectations. + pendingBlockExpectations := map[string]*regexp.Regexp{} + for k, v := range completeBlockExpectations { + pendingBlockExpectations[k] = v + } + + // Make 'pending' specific adjustments. + pendingBlockExpectations["nonce"] = reNull + pendingBlockExpectations["hash"] = reNull + pendingBlockExpectations["miner"] = reNull + pendingBlockExpectations["totalDifficulty"] = reNull + for blockHeight, cases := range map[string]map[string]*regexp.Regexp{ - "earliest": { - "nonce": reHexAnyLen, - "hash": reHexAnyLen, - "miner": regexp.MustCompile(fmt.Sprintf(`^"0x[a-zA-Z0-9]{%d}"$`, common.AddressLength*2)), - - "totalDifficulty": reHexAnyLen, - - "mixHash": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, common.HashLength*2)), - "logsBloom": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, types.BloomByteLength*2)), - - "number": reHexAnyLen, - "difficulty": reHexAnyLen, - "gasLimit": reHexAnyLen, - "gasUsed": reHexAnyLen, - "size": reHexAnyLen, - "timestamp": reHexAnyLen, - "extraData": reHexAnyLen, - - "parentHash": reHexHashLen, - "transactionsRoot": reHexHashLen, - "stateRoot": reHexHashLen, - "receiptsRoot": reHexHashLen, - "sha3Uncles": reHexHashLen, - - "uncles": regexp.MustCompile(`^\[\]$`), - "transactions": regexp.MustCompile(`^\[\]$`), - }, - "latest": { - "nonce": reHexAnyLen, - "hash": reHexAnyLen, - "miner": regexp.MustCompile(fmt.Sprintf(`^"0x[a-zA-Z0-9]{%d}"$`, common.AddressLength*2)), - - "totalDifficulty": reHexAnyLen, - - "mixHash": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, common.HashLength*2)), - "logsBloom": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, types.BloomByteLength*2)), - - "number": reHexAnyLen, - "difficulty": reHexAnyLen, - "gasLimit": reHexAnyLen, - "gasUsed": reHexAnyLen, - "size": reHexAnyLen, - "timestamp": reHexAnyLen, - "extraData": reHexAnyLen, - - "parentHash": reHexHashLen, - "transactionsRoot": reHexHashLen, - "stateRoot": reHexHashLen, - "receiptsRoot": reHexHashLen, - "sha3Uncles": reHexHashLen, - - "uncles": regexp.MustCompile(`^\[\]$`), - "transactions": regexp.MustCompile(`^\[\]$`), - }, - "pending": { - "nonce": reNull, - "hash": reNull, - "miner": reNull, - - "totalDifficulty": reNull, - - "mixHash": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, common.HashLength*2)), - "logsBloom": regexp.MustCompile(fmt.Sprintf(`^"0x[0]{%d}"$`, types.BloomByteLength*2)), - - "number": reHexAnyLen, - "difficulty": reHexAnyLen, - "gasLimit": reHexAnyLen, - "gasUsed": reHexAnyLen, - "size": reHexAnyLen, - "timestamp": reHexAnyLen, - "extraData": reHexAnyLen, - - "parentHash": reHexHashLen, - "transactionsRoot": reHexHashLen, - "stateRoot": reHexHashLen, - "receiptsRoot": reHexHashLen, - "sha3Uncles": reHexHashLen, - - "uncles": regexp.MustCompile(`^\[\]$`), - "transactions": regexp.MustCompile(`^\[\]$`), - }, + "earliest": completeBlockExpectations, + "latest": completeBlockExpectations, + "pending": pendingBlockExpectations, } { for _, fullTxes := range []bool{true, false} { - gotPending := make(map[string]json.RawMessage) - err := client.CallContext(ctx, &gotPending, "eth_getBlockByNumber", blockHeight, fullTxes) - if err != nil { - t.Fatal(err) - } - - for key, re := range cases { - gotVal, ok := gotPending[key] - if !ok { - t.Errorf("%s: missing key", key) + t.Run(fmt.Sprintf("eth_getBlockByNumber-%s-%v", blockHeight, fullTxes), func(t *testing.T) { + gotPending := make(map[string]json.RawMessage) + err := client.CallContext(ctx, &gotPending, "eth_getBlockByNumber", blockHeight, fullTxes) + if err != nil { + t.Fatal(err) } - if !re.Match(gotVal) { - t.Errorf("%s want: %v, got: %v", key, re, string(gotVal)) + + for key, re := range cases { + gotVal, ok := gotPending[key] + if !ok { + t.Errorf("%s: missing key", key) + } + if !re.Match(gotVal) { + t.Errorf("%s want: %v, got: %v", key, re, string(gotVal)) + } } - } - for k, v := range gotPending { - if _, ok := cases[k]; !ok { - t.Errorf("%s: missing key (value: %v)", k, string(v)) + for k, v := range gotPending { + if _, ok := cases[k]; !ok { + t.Errorf("%s: missing key (value: %v)", k, string(v)) + } } - } + }) } } } From 904066f67c233f45da2a561306bc1b96dcebb806 Mon Sep 17 00:00:00 2001 From: meows Date: Mon, 15 Mar 2021 15:25:59 -0500 Subject: [PATCH 18/22] ethclient: additional commentary Date: 2021-03-15 15:25:59-05:00 Signed-off-by: meows --- ethclient/ethclient_cg_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ethclient/ethclient_cg_test.go b/ethclient/ethclient_cg_test.go index 59bb08aa66..cc94ac9291 100644 --- a/ethclient/ethclient_cg_test.go +++ b/ethclient/ethclient_cg_test.go @@ -14,6 +14,7 @@ import ( /* The '_CanCompareGoEthereum' denotes tests that can be run against ethereum/go-ethereum. +These tests assume reliance on a shared Go code API (both at the private (test helpers) and public-methods levels). See .github/workflows/geth_1to1.yml for exemplary testing. */ From 119b604cfadef90df4564f2f4cf0efb95a7dbe99 Mon Sep 17 00:00:00 2001 From: meows Date: Tue, 16 Mar 2021 10:33:15 -0500 Subject: [PATCH 19/22] .github/workflows,ethclient: remove go-ethereum 1:1 comparative testing This is just a little over-the-top and too ad-hoc to be worth existence. Date: 2021-03-16 10:33:15-05:00 Signed-off-by: meows --- .github/workflows/geth_1to1.yml | 31 ------------------------------- ethclient/ethclient_cg_test.go | 10 ++-------- 2 files changed, 2 insertions(+), 39 deletions(-) delete mode 100644 .github/workflows/geth_1to1.yml diff --git a/.github/workflows/geth_1to1.yml b/.github/workflows/geth_1to1.yml deleted file mode 100644 index 788bda2923..0000000000 --- a/.github/workflows/geth_1to1.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: "Go-Ethereum 1:1" -on: - push: - branches: - - 'master' -jobs: - compare: - runs-on: ubuntu-latest - steps: - - name: Set up Go 1.x - uses: actions/setup-go@v2 - with: - go-version: ^1.16 - id: go - - - name: Check out ethereum/go-ethereum code into the Go module directory - uses: actions/checkout@v2 - with: - repository: 'ethereum/go-ethereum' - ref: 'v1.9.25' - submodules: 'recursive' # Not necessary, but complete. - - - name: Fetch CoreGeth - run: | - git remote add etclabscore https://github.com/etclabscore/core-geth.git - git fetch --depth 1 etclabscore master - - - name: Run Tests - run: | - git checkout etclabscore/master -- ethclient/ethclient_cg_test.go - go test ./ethclient -run _CanCompareGoEthereum diff --git a/ethclient/ethclient_cg_test.go b/ethclient/ethclient_cg_test.go index cc94ac9291..49409dad3a 100644 --- a/ethclient/ethclient_cg_test.go +++ b/ethclient/ethclient_cg_test.go @@ -12,17 +12,11 @@ import ( "github.com/ethereum/go-ethereum/core/types" ) -/* -The '_CanCompareGoEthereum' denotes tests that can be run against ethereum/go-ethereum. -These tests assume reliance on a shared Go code API (both at the private (test helpers) and public-methods levels). -See .github/workflows/geth_1to1.yml for exemplary testing. -*/ - -// TestEthGetBlockByNumber_ValidJSONResponse_CanCompareGoEthereum tests that +// TestEthGetBlockByNumber_ValidJSONResponse tests that // JSON RPC API responses to eth_getBlockByNumber meet pattern-based expectations. // These validations include the null-ness of certain fields for the 'pending' block // as well existence of all expected keys and values. -func TestEthGetBlockByNumber_ValidJSONResponse_CanCompareGoEthereum(t *testing.T) { +func TestEthGetBlockByNumber_ValidJSONResponse(t *testing.T) { backend, _ := newTestBackend(t) client, _ := backend.Attach() defer backend.Close() From 78e602b84832f7866f0c21d1e6f65b22aa280fb7 Mon Sep 17 00:00:00 2001 From: meows Date: Tue, 16 Mar 2021 10:55:16 -0500 Subject: [PATCH 20/22] ethclient: assert hash len for block hash field Date: 2021-03-16 10:55:16-05:00 Signed-off-by: meows --- ethclient/ethclient_cg_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethclient/ethclient_cg_test.go b/ethclient/ethclient_cg_test.go index 49409dad3a..8a341e751b 100644 --- a/ethclient/ethclient_cg_test.go +++ b/ethclient/ethclient_cg_test.go @@ -44,7 +44,7 @@ func TestEthGetBlockByNumber_ValidJSONResponse(t *testing.T) { // completeBlockExpectations define expectations for 'earliest' and 'latest' blocks. completeBlockExpectations := map[string]*regexp.Regexp{ "nonce": reHexAnyLen, - "hash": reHexAnyLen, + "hash": reHexHashLen, "miner": regexp.MustCompile(fmt.Sprintf(`^"0x[a-zA-Z0-9]{%d}"$`, common.AddressLength*2)), "totalDifficulty": reHexAnyLen, From 67ca5fcf0398beba097b5ba27517d8079ad7ea58 Mon Sep 17 00:00:00 2001 From: meows Date: Wed, 17 Mar 2021 13:08:01 -0500 Subject: [PATCH 21/22] internal/ethapi: omit the transactions field when marshaling uncle blocks Create a dedicated type for this case, switching on it conditionally when inclTx is false; this parameter is false only for uncle blocks. Date: 2021-03-17 13:08:01-05:00 Signed-off-by: meows --- internal/ethapi/api.go | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index da8bc91e09..1277dc4485 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1222,7 +1222,6 @@ type RPCMarshalBlockT struct { // RPCMarshalBlockTIR is the intermediate representation of RPCMarshalBlockT. // This exists to avoid a circular reference when overriding the json marshaling interface. -// RPCMarshalHeaderT is an embedded struct. type RPCMarshalBlockTIR struct { *RPCMarshalHeaderT Transactions []interface{} `json:"transactions"` @@ -1234,6 +1233,19 @@ type RPCMarshalBlockTIR struct { fullTx bool } +// RPCMarshalUncleTIR is the intermediate representation of RPCMarshalBlockT which excludes the transactions field. +// Transactions are excluded when the API returns block information for uncle blocks. +// This exists to avoid a circular reference when overriding the json marshaling interface. +type RPCMarshalUncleTIR struct { + *RPCMarshalHeaderT + Uncles []common.Hash `json:"uncles"` + + Error string `json:"error,omitempty"` + + inclTx bool + fullTx bool +} + // MarshalJSON marshals JSON for RPCMarshalBlockT. // If an error is present on the struct, an object with only the error is returned. // This logic follows the established logic at eth/api.go's handling of bad blocks. @@ -1241,9 +1253,19 @@ func (b *RPCMarshalBlockT) MarshalJSON() ([]byte, error) { if b.Error != "" { return json.Marshal(map[string]interface{}{"error": b.Error}) } - ir := &RPCMarshalBlockTIR{ + if b.inclTx { + ir := &RPCMarshalBlockTIR{ + RPCMarshalHeaderT: b.RPCMarshalHeaderT, + Transactions: b.Transactions, + Uncles: b.Uncles, + Error: "", + inclTx: b.inclTx, + fullTx: b.fullTx, + } + return json.Marshal(ir) + } + ir := &RPCMarshalUncleTIR{ RPCMarshalHeaderT: b.RPCMarshalHeaderT, - Transactions: b.Transactions, Uncles: b.Uncles, Error: "", inclTx: b.inclTx, @@ -1258,6 +1280,8 @@ func (b *RPCMarshalBlockT) MarshalJSON() ([]byte, error) { func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool) (*RPCMarshalBlockT, error) { fields := &RPCMarshalBlockT{RPCMarshalHeaderT: NewRPCMarshalHeaderTFromHeader(block.Header())} fields.Size = hexutil.Uint64(block.Size()) + fields.inclTx = inclTx + fields.fullTx = fullTx if inclTx { formatTx := func(tx *types.Transaction) (interface{}, error) { From 136e50f1950e5eed0605f186f32f38abb9f79318 Mon Sep 17 00:00:00 2001 From: meows Date: Wed, 17 Mar 2021 13:30:37 -0500 Subject: [PATCH 22/22] ethclient: test that the transactions field is omitted for uncles responses Date: 2021-03-17 13:30:37-05:00 Signed-off-by: meows --- ethclient/ethclient_cg_test.go | 101 +++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/ethclient/ethclient_cg_test.go b/ethclient/ethclient_cg_test.go index 8a341e751b..ac102992fb 100644 --- a/ethclient/ethclient_cg_test.go +++ b/ethclient/ethclient_cg_test.go @@ -9,7 +9,15 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/params/types/genesisT" ) // TestEthGetBlockByNumber_ValidJSONResponse tests that @@ -115,3 +123,96 @@ func TestEthGetBlockByNumber_ValidJSONResponse(t *testing.T) { } } } + +// newTestBackendWithUncles duplicates the logic of newTestBackend, except that it generates a backend +// on top of a chain that has uncles. +func newTestBackendWithUncles(t *testing.T) (*node.Node, []*types.Block) { + // Generate test chain. + genesis, blocks := generateTestChainWithUncles() + // Create node + n, err := node.New(&node.Config{}) + if err != nil { + t.Fatalf("can't create new node: %v", err) + } + // Create Ethereum Service + config := ð.Config{Genesis: genesis} + config.Ethash.PowMode = ethash.ModeFake + ethservice, err := eth.New(n, config) + if err != nil { + t.Fatalf("can't create new ethereum service: %v", err) + } + // Import the test chain. + if err := n.Start(); err != nil { + t.Fatalf("can't start test node: %v", err) + } + if _, err := ethservice.BlockChain().InsertChain(blocks[1:]); err != nil { + t.Fatalf("can't import test blocks: %v", err) + } + return n, blocks +} + +// generateTestChainWithUncles is a test helper function that essentially duplicates generateTestChain, +// except that the chain generated includes block with uncles. +func generateTestChainWithUncles() (*genesisT.Genesis, []*types.Block) { + db := rawdb.NewMemoryDatabase() + config := params.AllEthashProtocolChanges + genesis := &genesisT.Genesis{ + Config: config, + Alloc: genesisT.GenesisAlloc{testAddr: {Balance: testBalance}}, + ExtraData: []byte("test genesis"), + Timestamp: 9000, + } + generate := func(i int, g *core.BlockGen) { + g.OffsetTime(5) + g.SetExtra([]byte("test")) + if i >= 6 { + b2 := g.PrevBlock(i - 3).Header() + b2.Extra = []byte("foo") + g.AddUncle(b2) + } + } + gblock := core.GenesisToBlock(genesis, db) + engine := ethash.NewFaker() + blocks, _ := core.GenerateChain(config, gblock, engine, db, 8, generate) + blocks = append([]*types.Block{gblock}, blocks...) + + return genesis, blocks +} + +// TestUncleResponseEncoding tests the correctness of the JSON encoding of uncle-type responses. +// These, different from canonical or side blocks, should NOT include the transactions field. +func TestUncleResponseEncoding(t *testing.T) { + backend, chain := newTestBackendWithUncles(t) + client, _ := backend.Attach() + defer backend.Close() + defer client.Close() + + res := make(map[string]json.RawMessage) + err := client.Call(&res, "eth_getUncleByBlockNumberAndIndex", hexutil.EncodeUint64(uint64(len(chain)-1)), "0x0") + if err != nil { + t.Fatal(err) + } + if len(res) == 0 { + t.Fatal("empty response") + } + + if v, ok := res["uncles"]; !ok { + t.Fatal("uncles: missing field") + } else if !regexp.MustCompile(`^\[\]$`).Match(v) { + t.Fatal("uncles: should be empty array") + } + + if _, ok := res["transactions"]; ok { + t.Fatal("transactions: field not omitted") + } + + // Sanity check a few other fields + reHexAnyLen := regexp.MustCompile(`^"0x[a-zA-Z0-9]+"$`) + for _, field := range []string{"number", "hash", "nonce", "parentHash", "transactionsRoot"} { + if v, ok := res[field]; !ok { + t.Fatalf("%s: missing field", field) + } else if !reHexAnyLen.Match(v) { + t.Fatalf("%s: unexpected value: %s", field, string(v)) + } + } +}