From 1c59dfdca69b9e5fadb6f4fd2543e515ada77db9 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Wed, 30 Jan 2019 17:30:34 -0600 Subject: [PATCH 01/11] Apply go fmt changes to statediff --- cmd/utils/flags.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 998184ff0..cbad19926 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -60,8 +60,6 @@ import ( "github.com/ethereum/go-ethereum/statediff/service" whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" cli "gopkg.in/urfave/cli.v1" - "github.com/ethereum/go-ethereum/statediff/service" - "github.com/ethereum/go-ethereum/statediff" ) var ( -- 2.45.2 From 3b049aeb832309a0451f8286e374aef4d13b40fa Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Tue, 5 Feb 2019 16:10:37 -0600 Subject: [PATCH 02/11] Open a trie from the in-memory database --- statediff/builder/builder.go | 1 + 1 file changed, 1 insertion(+) diff --git a/statediff/builder/builder.go b/statediff/builder/builder.go index a106c3ad8..e53b955fd 100644 --- a/statediff/builder/builder.go +++ b/statediff/builder/builder.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" + "github.com/ethereum/go-ethereum/core" ) type Builder interface { -- 2.45.2 From 91b5f52ef7160b8b18df821306ee13daf8c829c4 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Thu, 7 Feb 2019 15:33:42 -0600 Subject: [PATCH 03/11] Use a node's LeafKey as an identifier instead of the address It was proving difficult to find look the address up from a given path with a full node (sometimes the value wouldn't exist in the disk db). So, instead, for now we are using the node's LeafKey with is a Keccak256 hash of the address, so if we know the address we can figure out which LeafKey it matches up to. --- statediff/builder/builder.go | 1 - statediff/testhelpers/test_data.go | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/statediff/builder/builder.go b/statediff/builder/builder.go index e53b955fd..a106c3ad8 100644 --- a/statediff/builder/builder.go +++ b/statediff/builder/builder.go @@ -28,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" - "github.com/ethereum/go-ethereum/core" ) type Builder interface { diff --git a/statediff/testhelpers/test_data.go b/statediff/testhelpers/test_data.go index 831bed218..f7ae64f5a 100644 --- a/statediff/testhelpers/test_data.go +++ b/statediff/testhelpers/test_data.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/statediff/builder" + "github.com/ethereum/go-ethereum/crypto" ) func AddressToLeafKey(address common.Address) common.Hash { -- 2.45.2 From 38a55abb0b6d5e2dc0f3dfdf716ad217018a3cf5 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Fri, 8 Feb 2019 15:15:36 -0600 Subject: [PATCH 04/11] Make sure that statediff has been processed before pruning --- cmd/geth/config.go | 4 ++++ core/blockchain.go | 34 +++++++++++++++++++++++++++------- core/blockchain_test.go | 12 ++++++------ eth/backend.go | 2 +- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/cmd/geth/config.go b/cmd/geth/config.go index ec97070c7..fe916d37a 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -158,6 +158,10 @@ func makeFullNode(ctx *cli.Context) *node.Node { cfg.Eth.ConstantinopleOverride = new(big.Int).SetUint64(ctx.GlobalUint64(utils.ConstantinopleOverrideFlag.Name)) } + if ctx.GlobalBool(utils.StateDiffFlag.Name) { + cfg.Eth.StateDiff = true + } + utils.RegisterEthService(stack, &cfg.Eth) if ctx.GlobalBool(utils.StateDiffFlag.Name) { diff --git a/core/blockchain.go b/core/blockchain.go index f0dba102c..912074ef8 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -95,7 +95,7 @@ type CacheConfig struct { TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk TrieDirtyDisabled bool // Whether to disable trie write caching and GC altogether (archive node) TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk - ProcessingStateDiffs bool // Whether statediffs processing should be taken into a account before a trie is pruned + ProcessStateDiffs bool // Whether statediffs processing should be taken into a account before a trie is pruned } // BlockChain represents the canonical chain given a database with a genesis @@ -937,8 +937,17 @@ func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (e } func (bc *BlockChain) AddToStateDiffProcessedCollection(hash common.Hash) { - count := bc.stateDiffsProcessed[hash] - bc.stateDiffsProcessed[hash] = count + 1 + count, ok := bc.stateDiffsProcessed[hash] + if count > 1 { + log.Error("count is too high", "count", count, "hash", hash.Hex()) + } + + if ok { + count++ + bc.stateDiffsProcessed[hash] = count + } else { + bc.stateDiffsProcessed[hash] = 1 + } } // WriteBlockWithState writes the block and all associated state to the database. @@ -1025,16 +1034,27 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. bc.triegc.Push(root, number) break } - - if bc.cacheConfig.ProcessingStateDiffs { - if !bc.allowedRootToBeDereferenced(root.(common.Hash)) { + if bc.cacheConfig.ProcessStateDiffs { + count, ok := bc.stateDiffsProcessed[root.(common.Hash)] + //if we haven't processed the statediff for a given state root and it's child, don't dereference it yet + if !ok { + log.Info("Current root NOT found root in stateDiffsProcessed", "root", root.(common.Hash).Hex()) + bc.triegc.Push(root, number) + break + } + if count < 2 { + log.Info("Current root has not yet been processed for it's child", "root", root.(common.Hash).Hex()) bc.triegc.Push(root, number) break } else { + log.Warn("Current root found in stateDiffsProcessed collection with a count of 2, okay to dereference", + "root", root.(common.Hash).Hex(), + "blockNumber", uint64(-number), + "size of stateDiffsProcessed", len(bc.stateDiffsProcessed)) delete(bc.stateDiffsProcessed, root.(common.Hash)) } } - + log.Info("DEREFERENCING", "root", root.(common.Hash).Hex()) triedb.Dereference(root.(common.Hash)) } } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 682bab8d9..22b6df734 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -1816,11 +1816,11 @@ func TestProcessingStateDiffs(t *testing.T) { defaultTrieDirtyCache := 256 defaultTrieTimeout := 60 * time.Minute cacheConfig := &CacheConfig{ - Disabled: false, - TrieCleanLimit: defaultTrieCleanCache, - TrieDirtyLimit: defaultTrieDirtyCache, - TrieTimeLimit: defaultTrieTimeout, - ProcessingStateDiffs: true, + Disabled: false, + TrieCleanLimit: defaultTrieCleanCache, + TrieDirtyLimit: defaultTrieDirtyCache, + TrieTimeLimit: defaultTrieTimeout, + ProcessStateDiffs: true, } db := ethdb.NewMemDatabase() genesis := new(Genesis).MustCommit(db) @@ -1883,7 +1883,7 @@ func TestProcessingStateDiffs(t *testing.T) { } } -func containsRootHash(collection []common.Hash, hash common.Hash) bool { +func containsRootHash(collection []common.Hash, hash common.Hash) bool{ for _, n := range collection { if n == hash { return true diff --git a/eth/backend.go b/eth/backend.go index 130cb44ef..a64d96347 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -171,7 +171,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { TrieDirtyLimit: config.TrieDirtyCache, TrieDirtyDisabled: config.NoPruning, TrieTimeLimit: config.TrieTimeout, - ProcessingStateDiffs: config.StateDiff, + ProcessStateDiffs: config.StateDiff, } ) eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve) -- 2.45.2 From 9ec5d2925682387c580f97e2ddb8d511cdbec991 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Tue, 12 Feb 2019 15:00:19 -0600 Subject: [PATCH 05/11] Clean up log lines and remove unnecessary fields from builder --- core/blockchain.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 912074ef8..3ef89d857 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1038,23 +1038,23 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. count, ok := bc.stateDiffsProcessed[root.(common.Hash)] //if we haven't processed the statediff for a given state root and it's child, don't dereference it yet if !ok { - log.Info("Current root NOT found root in stateDiffsProcessed", "root", root.(common.Hash).Hex()) + log.Debug("Current root NOT found root in stateDiffsProcessed", "root", root.(common.Hash).Hex()) bc.triegc.Push(root, number) break } if count < 2 { - log.Info("Current root has not yet been processed for it's child", "root", root.(common.Hash).Hex()) + log.Debug("Current root has not yet been processed for it's child", "root", root.(common.Hash).Hex()) bc.triegc.Push(root, number) break } else { - log.Warn("Current root found in stateDiffsProcessed collection with a count of 2, okay to dereference", + log.Debug("Current root found in stateDiffsProcessed collection with a count of 2, okay to dereference", "root", root.(common.Hash).Hex(), "blockNumber", uint64(-number), "size of stateDiffsProcessed", len(bc.stateDiffsProcessed)) delete(bc.stateDiffsProcessed, root.(common.Hash)) } } - log.Info("DEREFERENCING", "root", root.(common.Hash).Hex()) + log.Debug("Dereferencing", "root", root.(common.Hash).Hex()) triedb.Dereference(root.(common.Hash)) } } -- 2.45.2 From 9774f934d27ad176a5e8bc560936b24bc1267874 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Wed, 13 Feb 2019 09:01:31 -0600 Subject: [PATCH 06/11] Apply go fmt changes --- core/blockchain_test.go | 2 +- statediff/testhelpers/test_data.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 22b6df734..5379da1a2 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -1883,7 +1883,7 @@ func TestProcessingStateDiffs(t *testing.T) { } } -func containsRootHash(collection []common.Hash, hash common.Hash) bool{ +func containsRootHash(collection []common.Hash, hash common.Hash) bool { for _, n := range collection { if n == hash { return true diff --git a/statediff/testhelpers/test_data.go b/statediff/testhelpers/test_data.go index f7ae64f5a..831bed218 100644 --- a/statediff/testhelpers/test_data.go +++ b/statediff/testhelpers/test_data.go @@ -7,7 +7,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/statediff/builder" - "github.com/ethereum/go-ethereum/crypto" ) func AddressToLeafKey(address common.Address) common.Hash { -- 2.45.2 From 8fc2cab837a63fe76b10b5727f50388ad22ccad5 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Thu, 14 Feb 2019 15:46:03 -0600 Subject: [PATCH 07/11] Address PR comments --- cmd/geth/config.go | 4 ---- core/blockchain.go | 13 +------------ 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/cmd/geth/config.go b/cmd/geth/config.go index fe916d37a..ec97070c7 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -158,10 +158,6 @@ func makeFullNode(ctx *cli.Context) *node.Node { cfg.Eth.ConstantinopleOverride = new(big.Int).SetUint64(ctx.GlobalUint64(utils.ConstantinopleOverrideFlag.Name)) } - if ctx.GlobalBool(utils.StateDiffFlag.Name) { - cfg.Eth.StateDiff = true - } - utils.RegisterEthService(stack, &cfg.Eth) if ctx.GlobalBool(utils.StateDiffFlag.Name) { diff --git a/core/blockchain.go b/core/blockchain.go index 3ef89d857..e1bb482ea 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -938,9 +938,6 @@ func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (e func (bc *BlockChain) AddToStateDiffProcessedCollection(hash common.Hash) { count, ok := bc.stateDiffsProcessed[hash] - if count > 1 { - log.Error("count is too high", "count", count, "hash", hash.Hex()) - } if ok { count++ @@ -1035,15 +1032,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. break } if bc.cacheConfig.ProcessStateDiffs { - count, ok := bc.stateDiffsProcessed[root.(common.Hash)] - //if we haven't processed the statediff for a given state root and it's child, don't dereference it yet - if !ok { - log.Debug("Current root NOT found root in stateDiffsProcessed", "root", root.(common.Hash).Hex()) - bc.triegc.Push(root, number) - break - } - if count < 2 { - log.Debug("Current root has not yet been processed for it's child", "root", root.(common.Hash).Hex()) + if !bc.allowedRootToBeDereferenced(root.(common.Hash)) { bc.triegc.Push(root, number) break } else { -- 2.45.2 From 22d78f23cd5139a9744efcdb9739315cc1414c96 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Wed, 20 Feb 2019 09:37:08 -0600 Subject: [PATCH 08/11] Address PR comments --- core/blockchain.go | 10 ++-------- core/blockchain_test.go | 10 +++++----- eth/backend.go | 2 +- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index e1bb482ea..5eebed9f4 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -937,14 +937,8 @@ func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (e } func (bc *BlockChain) AddToStateDiffProcessedCollection(hash common.Hash) { - count, ok := bc.stateDiffsProcessed[hash] - - if ok { - count++ - bc.stateDiffsProcessed[hash] = count - } else { - bc.stateDiffsProcessed[hash] = 1 - } + count := bc.stateDiffsProcessed[hash] + bc.stateDiffsProcessed[hash] = count + 1 } // WriteBlockWithState writes the block and all associated state to the database. diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 5379da1a2..682bab8d9 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -1816,11 +1816,11 @@ func TestProcessingStateDiffs(t *testing.T) { defaultTrieDirtyCache := 256 defaultTrieTimeout := 60 * time.Minute cacheConfig := &CacheConfig{ - Disabled: false, - TrieCleanLimit: defaultTrieCleanCache, - TrieDirtyLimit: defaultTrieDirtyCache, - TrieTimeLimit: defaultTrieTimeout, - ProcessStateDiffs: true, + Disabled: false, + TrieCleanLimit: defaultTrieCleanCache, + TrieDirtyLimit: defaultTrieDirtyCache, + TrieTimeLimit: defaultTrieTimeout, + ProcessingStateDiffs: true, } db := ethdb.NewMemDatabase() genesis := new(Genesis).MustCommit(db) diff --git a/eth/backend.go b/eth/backend.go index a64d96347..130cb44ef 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -171,7 +171,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { TrieDirtyLimit: config.TrieDirtyCache, TrieDirtyDisabled: config.NoPruning, TrieTimeLimit: config.TrieTimeout, - ProcessStateDiffs: config.StateDiff, + ProcessingStateDiffs: config.StateDiff, } ) eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve) -- 2.45.2 From 131bd1d81d89fde498785699b3d5d63ff937e0ec Mon Sep 17 00:00:00 2001 From: Ian Norden Date: Wed, 10 Apr 2019 13:07:26 -0500 Subject: [PATCH 09/11] websocket stream of block + state diff data for seed node --- cmd/geth/main.go | 2 - cmd/geth/usage.go | 2 - cmd/utils/flags.go | 35 +-- statediff/builder/builder_test.go | 16 ++ statediff/builder/struct.go | 25 ++ statediff/config.go | 93 -------- statediff/config_test.go | 24 -- statediff/extractor/extractor.go | 51 ----- statediff/extractor/extractor_test.go | 123 ---------- statediff/publisher/csv.go | 130 ----------- statediff/publisher/publisher.go | 48 ---- statediff/publisher/publisher_test.go | 316 -------------------------- statediff/service/api.go | 95 ++++++++ statediff/service/service.go | 144 +++++++++--- statediff/service/service_test.go | 2 - 15 files changed, 251 insertions(+), 855 deletions(-) delete mode 100644 statediff/config.go delete mode 100644 statediff/config_test.go delete mode 100644 statediff/extractor/extractor.go delete mode 100644 statediff/extractor/extractor_test.go delete mode 100644 statediff/publisher/csv.go delete mode 100644 statediff/publisher/publisher.go delete mode 100644 statediff/publisher/publisher_test.go create mode 100644 statediff/service/api.go diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 746ec9ecc..39375a2b1 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -143,8 +143,6 @@ var ( utils.EWASMInterpreterFlag, utils.EVMInterpreterFlag, utils.StateDiffFlag, - utils.StateDiffModeFlag, - utils.StateDiffPathFlag, configFileFlag, } diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 616cddb53..e7a9e1fb8 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -249,8 +249,6 @@ var AppHelpFlagGroups = []flagGroup{ Name: "STATE DIFF", Flags: []cli.Flag{ utils.StateDiffFlag, - utils.StateDiffModeFlag, - utils.StateDiffPathFlag, }, }, { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index cbad19926..7815244b2 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -56,7 +56,6 @@ import ( "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/statediff" "github.com/ethereum/go-ethereum/statediff/service" whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" cli "gopkg.in/urfave/cli.v1" @@ -714,18 +713,6 @@ var ( Name: "statediff", Usage: "Enables the calculation of state diffs between each block, persists these state diffs the configured persistence mode.", } - - StateDiffModeFlag = cli.StringFlag{ - Name: "statediff.mode", - Usage: "Enables the user to determine which persistence mode they'd like to store the state diffs in.", - Value: "csv", - } - - StateDiffPathFlag = cli.StringFlag{ - Name: "statediff.path", - Usage: "Enables the user to determine where to persist the state diffs.", - Value: ".", - } ) // MakeDataDir retrieves the currently requested data directory, terminating @@ -937,6 +924,9 @@ func setWS(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalIsSet(WSApiFlag.Name) { cfg.WSModules = splitAndTrim(ctx.GlobalString(WSApiFlag.Name)) } + if ctx.GlobalBool(StateDiffFlag.Name) { + cfg.WSModules = append(cfg.WSModules, "statediff") + } } // setIPC creates an IPC path configuration from the set command line flags, @@ -1535,29 +1525,14 @@ func RegisterEthStatsService(stack *node.Node, url string) { } } +// RegisterStateDiffService configures and registers a service to stream state diff data over RPC func RegisterStateDiffService(stack *node.Node, ctx *cli.Context) { - //based on the context, if path and mode are set, update the config here - //otherwise pass in an empty config - - modeFlag := ctx.GlobalString(StateDiffModeFlag.Name) - mode, err := statediff.NewMode(modeFlag) - if err != nil { - Fatalf("Failed to register State Diff Service", err) - } - - path := ctx.GlobalString(StateDiffPathFlag.Name) - - config := statediff.Config{ - Mode: mode, - Path: path, - } - if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { var ethServ *eth.Ethereum ctx.Service(ðServ) chainDb := ethServ.ChainDb() blockChain := ethServ.BlockChain() - return service.NewStateDiffService(chainDb, blockChain, config) + return service.NewStateDiffService(chainDb, blockChain) }); err != nil { Fatalf("Failed to register State Diff Service", err) } diff --git a/statediff/builder/builder_test.go b/statediff/builder/builder_test.go index 4f8b7c468..7b3307f30 100644 --- a/statediff/builder/builder_test.go +++ b/statediff/builder/builder_test.go @@ -1,3 +1,19 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + package builder_test import ( diff --git a/statediff/builder/struct.go b/statediff/builder/struct.go index 416d8e816..f0fd43b75 100644 --- a/statediff/builder/struct.go +++ b/statediff/builder/struct.go @@ -27,6 +27,7 @@ import ( ) type AccountDiffsMap map[common.Hash]AccountDiff + type StateDiff struct { BlockNumber int64 `json:"blockNumber" gencodec:"required"` BlockHash common.Hash `json:"blockHash" gencodec:"required"` @@ -77,3 +78,27 @@ type DiffUint64 struct { type DiffBigInt struct { Value *big.Int `json:"value" gencodec:"optional"` } + +/* +// State trie leaf is just a short node, below +// that has an rlp encoded account as the value + + +// SO each account diffs map is reall a map of shortnode keys to values +// Flatten to a slice of short nodes? + +// Need to coerce into: + +type TrieNode struct { + // leaf, extension or branch + nodeKind string + + // If leaf or extension: [0] is key, [1] is val. + // If branch: [0] - [16] are children. + elements []interface{} + + // IPLD block information + cid *cid.Cid + rawdata []byte +} +*/ diff --git a/statediff/config.go b/statediff/config.go deleted file mode 100644 index 7f5ec3c35..000000000 --- a/statediff/config.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains a batch of utility type declarations used by the tests. As the node -// operates on unique types, a lot of them are needed to check various features. - -package statediff - -import "fmt" - -type Config struct { - Mode StateDiffMode // Mode for storing diffs - Path string // Path for storing diffs -} - -type StateDiffMode int - -const ( - CSV StateDiffMode = iota - IPLD - LDB - SQL -) - -func (mode StateDiffMode) IsValid() bool { - return mode >= IPLD && mode <= SQL -} - -// String implements the stringer interface. -func (mode StateDiffMode) String() string { - switch mode { - case CSV: - return "csv" - case IPLD: - return "ipfs" - case LDB: - return "ldb" - case SQL: - return "sql" - default: - return "unknown" - } -} - -func NewMode(mode string) (StateDiffMode, error) { - stateDiffMode := StateDiffMode(0) - err := stateDiffMode.UnmarshalText([]byte(mode)) - return stateDiffMode, err -} - -func (mode StateDiffMode) MarshalText() ([]byte, error) { - switch mode { - case CSV: - return []byte("ipfs"), nil - case IPLD: - return []byte("ipfs"), nil - case LDB: - return []byte("ldb"), nil - case SQL: - return []byte("sql"), nil - default: - return nil, fmt.Errorf("unknown state diff storage mode %d", mode) - } -} - -func (mode *StateDiffMode) UnmarshalText(text []byte) error { - switch string(text) { - case "csv": - *mode = CSV - case "ipfs": - *mode = IPLD - case "ldb": - *mode = LDB - case "sql": - *mode = SQL - default: - return fmt.Errorf(`unknown state diff storage mode %q, want "ipfs", "ldb" or "sql"`, text) - } - return nil -} diff --git a/statediff/config_test.go b/statediff/config_test.go deleted file mode 100644 index 5246d1cd1..000000000 --- a/statediff/config_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package statediff_test - -import ( - "testing" - - "github.com/ethereum/go-ethereum/statediff" - "github.com/ethereum/go-ethereum/statediff/testhelpers" -) - -func TestNewMode(t *testing.T) { - mode, err := statediff.NewMode("csv") - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - - if mode != statediff.CSV { - t.Error() - } - - _, err = statediff.NewMode("not a real mode") - if err == nil { - t.Error("Expected an error, and got nil.") - } -} diff --git a/statediff/extractor/extractor.go b/statediff/extractor/extractor.go deleted file mode 100644 index 770973c8d..000000000 --- a/statediff/extractor/extractor.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains a batch of utility type declarations used by the tests. As the node -// operates on unique types, a lot of them are needed to check various features. - -package extractor - -import ( - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/statediff/builder" - "github.com/ethereum/go-ethereum/statediff/publisher" -) - -type Extractor interface { - ExtractStateDiff(parent, current types.Block) (string, error) -} - -type extractor struct { - Builder builder.Builder // Interface for building state diff objects from two blocks - Publisher publisher.Publisher // Interface for publishing state diff objects to a datastore (e.g. IPFS) -} - -func NewExtractor(builder builder.Builder, publisher publisher.Publisher) *extractor { - return &extractor{ - Builder: builder, - Publisher: publisher, - } -} - -func (e *extractor) ExtractStateDiff(parent, current types.Block) (string, error) { - stateDiff, err := e.Builder.BuildStateDiff(parent.Root(), current.Root(), current.Number().Int64(), current.Hash()) - if err != nil { - return "", err - } - - return e.Publisher.PublishStateDiff(stateDiff) -} diff --git a/statediff/extractor/extractor_test.go b/statediff/extractor/extractor_test.go deleted file mode 100644 index 0ed036c12..000000000 --- a/statediff/extractor/extractor_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package extractor_test - -import ( - "bytes" - "math/big" - "math/rand" - "reflect" - "testing" - - "github.com/ethereum/go-ethereum/core/types" - b "github.com/ethereum/go-ethereum/statediff/builder" - e "github.com/ethereum/go-ethereum/statediff/extractor" - "github.com/ethereum/go-ethereum/statediff/testhelpers/mocks" -) - -var publisher mocks.Publisher -var builder mocks.Builder -var currentBlockNumber *big.Int -var parentBlock, currentBlock *types.Block -var expectedStateDiff b.StateDiff -var extractor e.Extractor -var err error - -func TestExtractor(t *testing.T) { - publisher = mocks.Publisher{} - builder = mocks.Builder{} - extractor = e.NewExtractor(&builder, &publisher) - if err != nil { - t.Error(err) - } - - blockNumber := rand.Int63() - parentBlockNumber := big.NewInt(blockNumber - int64(1)) - currentBlockNumber = big.NewInt(blockNumber) - parentBlock = types.NewBlock(&types.Header{Number: parentBlockNumber}, nil, nil, nil) - currentBlock = types.NewBlock(&types.Header{Number: currentBlockNumber}, nil, nil, nil) - - expectedStateDiff = b.StateDiff{ - BlockNumber: blockNumber, - BlockHash: currentBlock.Hash(), - CreatedAccounts: nil, - DeletedAccounts: nil, - UpdatedAccounts: nil, - } - - testBuildStateDiffStruct(t) - testBuildStateDiffErrorHandling(t) - testPublishingStateDiff(t) - testPublisherErrorHandling(t) -} - -func testBuildStateDiffStruct(t *testing.T) { - builder.SetStateDiffToBuild(&expectedStateDiff) - - _, err = extractor.ExtractStateDiff(*parentBlock, *currentBlock) - if err != nil { - t.Error(err) - } - - if !equals(builder.OldStateRoot, parentBlock.Root()) { - t.Error() - } - if !equals(builder.NewStateRoot, currentBlock.Root()) { - t.Error() - } - if !equals(builder.BlockNumber, currentBlockNumber.Int64()) { - t.Error() - } - if !equals(builder.BlockHash, currentBlock.Hash()) { - t.Error() - } -} - -func testBuildStateDiffErrorHandling(t *testing.T) { - builder.SetBuilderError(mocks.Error) - - _, err = extractor.ExtractStateDiff(*parentBlock, *currentBlock) - if err == nil { - t.Error(err) - } - - if !equals(err, mocks.Error) { - t.Error() - } - builder.SetBuilderError(nil) -} - -func testPublishingStateDiff(t *testing.T) { - builder.SetStateDiffToBuild(&expectedStateDiff) - - _, err = extractor.ExtractStateDiff(*parentBlock, *currentBlock) - if err != nil { - t.Error(err) - } - - if !equals(publisher.StateDiff, &expectedStateDiff) { - t.Error() - } -} - -func testPublisherErrorHandling(t *testing.T) { - publisher.SetPublisherError(mocks.Error) - - _, err = extractor.ExtractStateDiff(*parentBlock, *currentBlock) - if err == nil { - t.Error("Expected an error, but it didn't occur.") - } - if !equals(err, mocks.Error) { - t.Error() - } - - publisher.SetPublisherError(nil) -} - -func equals(actual, expected interface{}) (success bool) { - if actualByteSlice, ok := actual.([]byte); ok { - if expectedByteSlice, ok := expected.([]byte); ok { - return bytes.Equal(actualByteSlice, expectedByteSlice) - } - } - - return reflect.DeepEqual(actual, expected) -} diff --git a/statediff/publisher/csv.go b/statediff/publisher/csv.go deleted file mode 100644 index 13971a5c8..000000000 --- a/statediff/publisher/csv.go +++ /dev/null @@ -1,130 +0,0 @@ -package publisher - -import ( - "encoding/csv" - "os" - "path/filepath" - "strconv" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/statediff/builder" -) - -var ( - Headers = []string{ - "blockNumber", "blockHash", "accountAction", "codeHash", - "nonceValue", "balanceValue", "contractRoot", "storageDiffPaths", - "accountLeafKey", "storageKey", "storageValue", - } - - timeStampFormat = "20060102150405.00000" - deletedAccountAction = "deleted" - createdAccountAction = "created" - updatedAccountAction = "updated" -) - -func createCSVFilePath(path, blockNumber string) string { - now := time.Now() - timeStamp := now.Format(timeStampFormat) - suffix := timeStamp + "-" + blockNumber - filePath := filepath.Join(path, suffix) - filePath = filePath + ".csv" - return filePath -} - -func (p *publisher) publishStateDiffToCSV(sd builder.StateDiff) (string, error) { - filePath := createCSVFilePath(p.Config.Path, strconv.FormatInt(sd.BlockNumber, 10)) - - file, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - return "", err - } - defer file.Close() - - writer := csv.NewWriter(file) - defer writer.Flush() - - var data [][]string - data = append(data, Headers) - data = append(data, accumulateAccountRows(sd)...) - for _, value := range data { - err := writer.Write(value) - if err != nil { - return "", err - } - } - - return filePath, nil -} - -func accumulateAccountRows(sd builder.StateDiff) [][]string { - var accountRows [][]string - for accountAddr, accountDiff := range sd.CreatedAccounts { - formattedAccountData := formatAccountData(accountAddr, accountDiff, sd, createdAccountAction) - - accountRows = append(accountRows, formattedAccountData...) - } - - for accountAddr, accountDiff := range sd.UpdatedAccounts { - formattedAccountData := formatAccountData(accountAddr, accountDiff, sd, updatedAccountAction) - - accountRows = append(accountRows, formattedAccountData...) - } - - for accountAddr, accountDiff := range sd.DeletedAccounts { - formattedAccountData := formatAccountData(accountAddr, accountDiff, sd, deletedAccountAction) - - accountRows = append(accountRows, formattedAccountData...) - } - - return accountRows -} - -func formatAccountData(accountAddr common.Hash, accountDiff builder.AccountDiff, sd builder.StateDiff, accountAction string) [][]string { - blockNumberString := strconv.FormatInt(sd.BlockNumber, 10) - blockHash := sd.BlockHash.String() - codeHash := accountDiff.CodeHash - nonce := strconv.FormatUint(*accountDiff.Nonce.Value, 10) - balance := accountDiff.Balance.Value.String() - newContractRoot := accountDiff.ContractRoot.Value - address := accountAddr.String() - var result [][]string - - if len(accountDiff.Storage) > 0 { - for storagePath, storage := range accountDiff.Storage { - formattedAccountData := []string{ - blockNumberString, - blockHash, - accountAction, - codeHash, - nonce, - balance, - *newContractRoot, - storagePath, - address, - *storage.Key, - *storage.Value, - } - - result = append(result, formattedAccountData) - } - } else { - formattedAccountData := []string{ - blockNumberString, - blockHash, - accountAction, - codeHash, - nonce, - balance, - *newContractRoot, - "", - address, - "", - "", - } - result = append(result, formattedAccountData) - } - - return result -} diff --git a/statediff/publisher/publisher.go b/statediff/publisher/publisher.go deleted file mode 100644 index ff1925513..000000000 --- a/statediff/publisher/publisher.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains a batch of utility type declarations used by the tests. As the node -// operates on unique types, a lot of them are needed to check various features. - -package publisher - -import ( - "github.com/ethereum/go-ethereum/statediff" - "github.com/ethereum/go-ethereum/statediff/builder" -) - -type Publisher interface { - PublishStateDiff(sd *builder.StateDiff) (string, error) -} - -type publisher struct { - Config statediff.Config -} - -func NewPublisher(config statediff.Config) (*publisher, error) { - return &publisher{ - Config: config, - }, nil -} - -func (p *publisher) PublishStateDiff(sd *builder.StateDiff) (string, error) { - switch p.Config.Mode { - case statediff.CSV: - return p.publishStateDiffToCSV(*sd) - default: - return p.publishStateDiffToCSV(*sd) - } -} diff --git a/statediff/publisher/publisher_test.go b/statediff/publisher/publisher_test.go deleted file mode 100644 index 76aaf961e..000000000 --- a/statediff/publisher/publisher_test.go +++ /dev/null @@ -1,316 +0,0 @@ -package publisher_test - -import ( - "bytes" - "encoding/csv" - "io/ioutil" - "os" - "path/filepath" - "reflect" - "strconv" - "strings" - "testing" - - "github.com/ethereum/go-ethereum/statediff" - "github.com/ethereum/go-ethereum/statediff/builder" - p "github.com/ethereum/go-ethereum/statediff/publisher" - "github.com/ethereum/go-ethereum/statediff/testhelpers" - "github.com/pkg/errors" -) - -var ( - tempDir = os.TempDir() - testFilePrefix = "test-statediff" - publisher p.Publisher - dir string - err error -) - -var expectedCreatedAccountRow = []string{ - strconv.FormatInt(testhelpers.BlockNumber, 10), - testhelpers.BlockHash, - "created", - testhelpers.CodeHash, - strconv.FormatUint(testhelpers.NewNonceValue, 10), - strconv.FormatInt(testhelpers.NewBalanceValue, 10), - testhelpers.ContractRoot, - testhelpers.StoragePath, - testhelpers.ContractLeafKey.Hex(), - "0000000000000000000000000000000000000000000000000000000000000001", - testhelpers.StorageValue, -} - -var expectedCreatedAccountWithoutStorageUpdateRow = []string{ - strconv.FormatInt(testhelpers.BlockNumber, 10), - testhelpers.BlockHash, - "created", - testhelpers.CodeHash, - strconv.FormatUint(testhelpers.NewNonceValue, 10), - strconv.FormatInt(testhelpers.NewBalanceValue, 10), - testhelpers.ContractRoot, - "", - testhelpers.AnotherContractLeafKey.Hex(), - "", - "", -} - -var expectedUpdatedAccountRow = []string{ - strconv.FormatInt(testhelpers.BlockNumber, 10), - testhelpers.BlockHash, - "updated", - testhelpers.CodeHash, - strconv.FormatUint(testhelpers.NewNonceValue, 10), - strconv.FormatInt(testhelpers.NewBalanceValue, 10), - testhelpers.ContractRoot, - testhelpers.StoragePath, - testhelpers.ContractLeafKey.Hex(), - "0000000000000000000000000000000000000000000000000000000000000001", - testhelpers.StorageValue, -} - -var expectedDeletedAccountRow = []string{ - strconv.FormatInt(testhelpers.BlockNumber, 10), - testhelpers.BlockHash, - "deleted", - testhelpers.CodeHash, - strconv.FormatUint(testhelpers.NewNonceValue, 10), - strconv.FormatInt(testhelpers.NewBalanceValue, 10), - testhelpers.ContractRoot, - testhelpers.StoragePath, - testhelpers.ContractLeafKey.Hex(), - "0000000000000000000000000000000000000000000000000000000000000001", - testhelpers.StorageValue, -} - -func TestPublisher(t *testing.T) { - dir, err = ioutil.TempDir(tempDir, testFilePrefix) - if err != nil { - t.Error(err) - } - config := statediff.Config{ - Path: dir, - Mode: statediff.CSV, - } - publisher, err = p.NewPublisher(config) - if err != nil { - t.Error(err) - } - - type Test func(t *testing.T) - - var tests = []Test{ - testFileName, - testColumnHeaders, - testAccountDiffs, - testWhenNoDiff, - testDefaultPublisher, - testDefaultDirectory, - } - - for _, test := range tests { - test(t) - err := removeFilesFromDir(dir) - if err != nil { - t.Errorf("Error removing files from temp dir: %s", dir) - } - } -} - -func removeFilesFromDir(dir string) error { - files, err := filepath.Glob(filepath.Join(dir, "*")) - if err != nil { - return err - } - - for _, file := range files { - err = os.RemoveAll(file) - if err != nil { - return err - } - } - return nil -} - -func testFileName(t *testing.T) { - fileName, err := publisher.PublishStateDiff(&testhelpers.TestStateDiff) - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - - if !strings.HasPrefix(fileName, dir) { - t.Errorf(testhelpers.TestFailureFormatString, t.Name(), dir, fileName) - } - blockNumberWithFileExt := strconv.FormatInt(testhelpers.BlockNumber, 10) + ".csv" - if !strings.HasSuffix(fileName, blockNumberWithFileExt) { - t.Errorf(testhelpers.TestFailureFormatString, t.Name(), blockNumberWithFileExt, fileName) - } -} - -func testColumnHeaders(t *testing.T) { - _, err = publisher.PublishStateDiff(&testhelpers.TestStateDiff) - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - - file, err := getTestDiffFile(dir) - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - - lines, err := csv.NewReader(file).ReadAll() - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - if len(lines) < 1 { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - if !equals(lines[0], p.Headers) { - t.Error() - } -} - -func testAccountDiffs(t *testing.T) { - // it persists the created, updated and deleted account diffs to a CSV file - _, err = publisher.PublishStateDiff(&testhelpers.TestStateDiff) - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - - file, err := getTestDiffFile(dir) - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - - lines, err := csv.NewReader(file).ReadAll() - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - if len(lines) <= 3 { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - if !equals(lines[1], expectedCreatedAccountRow) { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - if !equals(lines[2], expectedCreatedAccountWithoutStorageUpdateRow) { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - if !equals(lines[3], expectedUpdatedAccountRow) { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - if !equals(lines[4], expectedDeletedAccountRow) { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } -} - -func testWhenNoDiff(t *testing.T) { - //it creates an empty CSV when there is no diff - emptyDiff := builder.StateDiff{} - _, err = publisher.PublishStateDiff(&emptyDiff) - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - - file, err := getTestDiffFile(dir) - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - - lines, err := csv.NewReader(file).ReadAll() - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - - if !equals(len(lines), 1) { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } -} - -func testDefaultPublisher(t *testing.T) { - //it defaults to publishing state diffs to a CSV file when no mode is configured - config := statediff.Config{Path: dir} - publisher, err = p.NewPublisher(config) - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - - _, err = publisher.PublishStateDiff(&testhelpers.TestStateDiff) - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - - file, err := getTestDiffFile(dir) - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - - lines, err := csv.NewReader(file).ReadAll() - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - if !equals(len(lines), 5) { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - if !equals(lines[0], p.Headers) { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } -} - -func testDefaultDirectory(t *testing.T) { - //it defaults to publishing CSV files in the current directory when no path is configured - config := statediff.Config{} - publisher, err = p.NewPublisher(config) - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - - err := os.Chdir(dir) - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - - _, err = publisher.PublishStateDiff(&testhelpers.TestStateDiff) - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - - file, err := getTestDiffFile(dir) - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - - lines, err := csv.NewReader(file).ReadAll() - if err != nil { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - if !equals(len(lines), 5) { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } - if !equals(lines[0], p.Headers) { - t.Errorf(testhelpers.ErrorFormatString, t.Name(), err) - } -} - -func getTestDiffFile(dir string) (*os.File, error) { - files, err := ioutil.ReadDir(dir) - if err != nil { - return nil, err - } - if len(files) == 0 { - return nil, errors.New("There are 0 files.") - } - - fileName := files[0].Name() - filePath := filepath.Join(dir, fileName) - - return os.Open(filePath) -} - -func equals(actual, expected interface{}) (success bool) { - if actualByteSlice, ok := actual.([]byte); ok { - if expectedByteSlice, ok := expected.([]byte); ok { - return bytes.Equal(actualByteSlice, expectedByteSlice) - } - } - - return reflect.DeepEqual(actual, expected) -} diff --git a/statediff/service/api.go b/statediff/service/api.go new file mode 100644 index 000000000..79ea0c02d --- /dev/null +++ b/statediff/service/api.go @@ -0,0 +1,95 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package service + +import ( + "context" + "sync" + "time" + + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rpc" +) + +const APIName = "statediff" +const APIVersion = "0.0.1" + +// PublicStateDiffAPI provides the a websocket service +// that can be used to stream out state diffs as they +// are produced by a full node +type PublicStateDiffAPI struct { + sds *StateDiffService + + mu sync.Mutex + lastUsed map[string]time.Time // keeps track when a filter was polled for the last time. +} + +// NewPublicStateDiffAPI create a new state diff websocket streaming service. +func NewPublicStateDiffAPI(sds *StateDiffService) *PublicStateDiffAPI { + return &PublicStateDiffAPI{ + sds: sds, + lastUsed: make(map[string]time.Time), + mu: sync.Mutex{}, + } +} + +// StreamData set up a subscription that fires off state-diffs when they are created +func (api *PublicStateDiffAPI) StreamData(ctx context.Context) (*rpc.Subscription, error) { + // ensure that the RPC connection supports subscriptions + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return nil, rpc.ErrNotificationsUnsupported + } + + // create subscription and start waiting for statediff events + rpcSub := notifier.CreateSubscription() + id := rpcSub.ID + + go func() { + // subscribe to events from the state diff service + payloadChannel := make(chan StateDiffPayload) + quitChan := make(chan bool) + api.sds.Subscribe(id, payloadChannel, quitChan) + + // loop and await state diff payloads and relay them to the subscriber with then notifier + for { + select { + case packet := <-payloadChannel: + if err := notifier.Notify(id, packet); err != nil { + log.Error("Failed to send state diff packet", "err", err) + } + case <-rpcSub.Err(): + err := api.sds.Unsubscribe(id) + if err != nil { + log.Error("Failed to unsubscribe from the state diff service", err) + } + return + case <-notifier.Closed(): + err := api.sds.Unsubscribe(id) + if err != nil { + log.Error("Failed to unsubscribe from the state diff service", err) + } + return + case <-quitChan: + // don't need to unsubscribe, statediff service does so before sending the quit signal + return + } + } + }() + + return rpcSub, nil +} diff --git a/statediff/service/service.go b/statediff/service/service.go index 19ea0c644..1dc68dcb9 100644 --- a/statediff/service/service.go +++ b/statediff/service/service.go @@ -1,19 +1,19 @@ package service import ( - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/statediff" - b "github.com/ethereum/go-ethereum/statediff/builder" - e "github.com/ethereum/go-ethereum/statediff/extractor" - p "github.com/ethereum/go-ethereum/statediff/publisher" + "bytes" + "fmt" + "sync" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/statediff/builder" ) type BlockChain interface { @@ -23,31 +23,49 @@ type BlockChain interface { } type StateDiffService struct { - Builder *b.Builder - Extractor e.Extractor - BlockChain BlockChain + sync.Mutex + Builder builder.Builder + BlockChain BlockChain + QuitChan chan bool + Subscriptions Subscriptions } -func NewStateDiffService(db ethdb.Database, blockChain *core.BlockChain, config statediff.Config) (*StateDiffService, error) { - builder := b.NewBuilder(db, blockChain) - publisher, err := p.NewPublisher(config) - if err != nil { - return nil, err - } +type Subscriptions map[rpc.ID]subscription - extractor := e.NewExtractor(builder, publisher) +type subscription struct { + PayloadChan chan<- StateDiffPayload + QuitChan chan<- bool +} + +type StateDiffPayload struct { + BlockRlp []byte `json:"block"` + StateDiff builder.StateDiff `json:"state_diff"` + Err error `json:"error"` +} + +func NewStateDiffService(db ethdb.Database, blockChain *core.BlockChain) (*StateDiffService, error) { return &StateDiffService{ - BlockChain: blockChain, - Extractor: extractor, + BlockChain: blockChain, + Builder: builder.NewBuilder(db, blockChain), + QuitChan: make(chan bool), + Subscriptions: make(Subscriptions), }, nil } -func (StateDiffService) Protocols() []p2p.Protocol { +func (sds *StateDiffService) Protocols() []p2p.Protocol { return []p2p.Protocol{} } -func (StateDiffService) APIs() []rpc.API { - return []rpc.API{} +// APIs returns the RPC descriptors the Whisper implementation offers +func (sds *StateDiffService) APIs() []rpc.API { + return []rpc.API{ + { + Namespace: APIName, + Version: APIVersion, + Service: NewPublicStateDiffAPI(sds), + Public: true, + }, + } } func (sds *StateDiffService) Loop(chainEventCh chan core.ChainEvent) { @@ -56,7 +74,6 @@ func (sds *StateDiffService) Loop(chainEventCh chan core.ChainEvent) { blocksCh := make(chan *types.Block, 10) errCh := chainEventSub.Err() - quitCh := make(chan struct{}) go func() { HandleChainEventChLoop: @@ -66,13 +83,15 @@ func (sds *StateDiffService) Loop(chainEventCh chan core.ChainEvent) { case chainEvent := <-chainEventCh: log.Debug("Event received from chainEventCh", "event", chainEvent) blocksCh <- chainEvent.Block - //if node stopped + //if node stopped case err := <-errCh: log.Warn("Error from chain event subscription, breaking loop.", "error", err) + close(sds.QuitChan) + break HandleChainEventChLoop + case <-sds.QuitChan: break HandleChainEventChLoop } } - close(quitCh) }() //loop through chain events until no more @@ -90,21 +109,77 @@ HandleBlockChLoop: break HandleBlockChLoop } - stateDiffLocation, err := sds.Extractor.ExtractStateDiff(*parentBlock, *currentBlock) + stateDiff, err := sds.Builder.BuildStateDiff(parentBlock.Root(), currentBlock.Root(), currentBlock.Number().Int64(), currentBlock.Hash()) if err != nil { - log.Error("Error extracting statediff", "block number", currentBlock.Number(), "error", err) - } else { - log.Info("Statediff extracted", "block number", currentBlock.Number(), "location", stateDiffLocation) - sds.BlockChain.AddToStateDiffProcessedCollection(parentBlock.Root()) - sds.BlockChain.AddToStateDiffProcessedCollection(currentBlock.Root()) + log.Error("Error building statediff", "block number", currentBlock.Number(), "error", err) } - case <-quitCh: + rlpBuff := new(bytes.Buffer) + currentBlock.EncodeRLP(rlpBuff) + blockRlp := rlpBuff.Bytes() + payload := StateDiffPayload{ + BlockRlp: blockRlp, + StateDiff: *stateDiff, + Err: err, + } + // If we have any websocket subscription listening in, send the data to them + sds.Send(payload) + case <-sds.QuitChan: log.Debug("Quitting the statediff block channel") + sds.Close() return } } } +func (sds *StateDiffService) Subscribe(id rpc.ID, sub chan<- StateDiffPayload, quitChan chan<- bool) { + log.Info("Subscribing to the statediff service") + sds.Lock() + sds.Subscriptions[id] = subscription{ + PayloadChan: sub, + QuitChan: quitChan, + } + sds.Unlock() +} + +func (sds *StateDiffService) Unsubscribe(id rpc.ID) error { + log.Info("Unsubscribing from the statediff service") + sds.Lock() + _, ok := sds.Subscriptions[id] + if !ok { + return fmt.Errorf("cannot unsubscribe; subscription for id %s does not exist", id) + } + delete(sds.Subscriptions, id) + sds.Unlock() + return nil +} + +func (sds *StateDiffService) Send(payload StateDiffPayload) { + sds.Lock() + for id, sub := range sds.Subscriptions { + select { + case sub.PayloadChan <- payload: + log.Info("sending state diff payload to subscription %s", id) + default: + log.Info("unable to send payload to subscription %s; channel has no receiver", id) + } + } + sds.Unlock() +} + +func (sds *StateDiffService) Close() { + sds.Lock() + for id, sub := range sds.Subscriptions { + select { + case sub.QuitChan <- true: + delete(sds.Subscriptions, id) + log.Info("closing subscription %s", id) + default: + log.Info("unable to close subscription %s; channel has no receiver", id) + } + } + sds.Unlock() +} + func (sds *StateDiffService) Start(server *p2p.Server) error { log.Info("Starting statediff service") @@ -114,7 +189,8 @@ func (sds *StateDiffService) Start(server *p2p.Server) error { return nil } -func (StateDiffService) Stop() error { +func (sds *StateDiffService) Stop() error { log.Info("Stopping statediff service") + close(sds.QuitChan) return nil } diff --git a/statediff/service/service_test.go b/statediff/service/service_test.go index daf3445c3..55219e337 100644 --- a/statediff/service/service_test.go +++ b/statediff/service/service_test.go @@ -50,7 +50,6 @@ func testErrorInChainEventLoop(t *testing.T) { blockChain := mocks.BlockChain{} service := s.StateDiffService{ Builder: nil, - Extractor: &extractor, BlockChain: &blockChain, } @@ -85,7 +84,6 @@ func testErrorInBlockLoop(t *testing.T) { blockChain := mocks.BlockChain{} service := s.StateDiffService{ Builder: nil, - Extractor: &extractor, BlockChain: &blockChain, } -- 2.45.2 From cc58d94cc362b8974d57fa886e5a343062c59057 Mon Sep 17 00:00:00 2001 From: Ian Norden Date: Sun, 14 Apr 2019 16:59:18 -0500 Subject: [PATCH 10/11] adjust state diffs to form closer to the eth-ipld objects we need to publish + pass their proofs and paths along with them; adjust tests --- statediff/{service => }/api.go | 8 +- statediff/{builder => }/builder.go | 112 +++--- statediff/builder/builder_test.go | 381 ------------------ statediff/builder_test.go | 467 ++++++++++++++++++++++ statediff/{builder => }/helpers.go | 7 +- statediff/{service => }/service.go | 78 ++-- statediff/service/service_test.go | 105 ----- statediff/service_test.go | 129 ++++++ statediff/testhelpers/helpers.go | 16 + statediff/testhelpers/mocks/blockchain.go | 16 + statediff/testhelpers/mocks/builder.go | 24 +- statediff/testhelpers/mocks/error.go | 16 + statediff/testhelpers/mocks/extractor.go | 20 - statediff/testhelpers/mocks/publisher.go | 22 +- statediff/testhelpers/mocks/service.go | 162 ++++++++ statediff/testhelpers/test_data.go | 87 ++-- statediff/{builder/struct.go => types.go} | 54 +-- 17 files changed, 1045 insertions(+), 659 deletions(-) rename statediff/{service => }/api.go (93%) rename statediff/{builder => }/builder.go (78%) delete mode 100644 statediff/builder/builder_test.go create mode 100644 statediff/builder_test.go rename statediff/{builder => }/helpers.go (96%) rename statediff/{service => }/service.go (63%) delete mode 100644 statediff/service/service_test.go create mode 100644 statediff/service_test.go delete mode 100644 statediff/testhelpers/mocks/extractor.go create mode 100644 statediff/testhelpers/mocks/service.go rename statediff/{builder/struct.go => types.go} (63%) diff --git a/statediff/service/api.go b/statediff/api.go similarity index 93% rename from statediff/service/api.go rename to statediff/api.go index 79ea0c02d..c4267c1f5 100644 --- a/statediff/service/api.go +++ b/statediff/api.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package service +package statediff import ( "context" @@ -32,14 +32,14 @@ const APIVersion = "0.0.1" // that can be used to stream out state diffs as they // are produced by a full node type PublicStateDiffAPI struct { - sds *StateDiffService + sds SDS mu sync.Mutex lastUsed map[string]time.Time // keeps track when a filter was polled for the last time. } // NewPublicStateDiffAPI create a new state diff websocket streaming service. -func NewPublicStateDiffAPI(sds *StateDiffService) *PublicStateDiffAPI { +func NewPublicStateDiffAPI(sds SDS) *PublicStateDiffAPI { return &PublicStateDiffAPI{ sds: sds, lastUsed: make(map[string]time.Time), @@ -48,7 +48,7 @@ func NewPublicStateDiffAPI(sds *StateDiffService) *PublicStateDiffAPI { } // StreamData set up a subscription that fires off state-diffs when they are created -func (api *PublicStateDiffAPI) StreamData(ctx context.Context) (*rpc.Subscription, error) { +func (api *PublicStateDiffAPI) StreamStateDiffs(ctx context.Context) (*rpc.Subscription, error) { // ensure that the RPC connection supports subscriptions notifier, supported := rpc.NotifierFromContext(ctx) if !supported { diff --git a/statediff/builder/builder.go b/statediff/builder.go similarity index 78% rename from statediff/builder/builder.go rename to statediff/builder.go index a106c3ad8..0a37fafe6 100644 --- a/statediff/builder/builder.go +++ b/statediff/builder.go @@ -17,7 +17,7 @@ // Contains a batch of utility type declarations used by the tests. As the node // operates on unique types, a lot of them are needed to check various features. -package builder +package statediff import ( "github.com/ethereum/go-ethereum/common" @@ -31,7 +31,7 @@ import ( ) type Builder interface { - BuildStateDiff(oldStateRoot, newStateRoot common.Hash, blockNumber int64, blockHash common.Hash) (*StateDiff, error) + BuildStateDiff(oldStateRoot, newStateRoot common.Hash, blockNumber int64, blockHash common.Hash) (StateDiff, error) } type builder struct { @@ -39,8 +39,6 @@ type builder struct { blockChain *core.BlockChain } -type AccountsMap map[common.Hash]*state.Account - func NewBuilder(db ethdb.Database, blockChain *core.BlockChain) *builder { return &builder{ chainDB: db, @@ -48,18 +46,18 @@ func NewBuilder(db ethdb.Database, blockChain *core.BlockChain) *builder { } } -func (sdb *builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, blockNumber int64, blockHash common.Hash) (*StateDiff, error) { +func (sdb *builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, blockNumber int64, blockHash common.Hash) (StateDiff, error) { // Generate tries for old and new states stateCache := sdb.blockChain.StateCache() oldTrie, err := stateCache.OpenTrie(oldStateRoot) if err != nil { log.Error("Error creating trie for oldStateRoot", "error", err) - return nil, err + return StateDiff{}, err } newTrie, err := stateCache.OpenTrie(newStateRoot) if err != nil { log.Error("Error creating trie for newStateRoot", "error", err) - return nil, err + return StateDiff{}, err } // Find created accounts @@ -68,7 +66,7 @@ func (sdb *builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, block creations, err := sdb.collectDiffNodes(oldIt, newIt) if err != nil { log.Error("Error collecting creation diff nodes", "error", err) - return nil, err + return StateDiff{}, err } // Find deleted accounts @@ -77,7 +75,7 @@ func (sdb *builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, block deletions, err := sdb.collectDiffNodes(newIt, oldIt) if err != nil { log.Error("Error collecting deletion diff nodes", "error", err) - return nil, err + return StateDiff{}, err } // Find all the diffed keys @@ -89,20 +87,20 @@ func (sdb *builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, block updatedAccounts, err := sdb.buildDiffIncremental(creations, deletions, updatedKeys) if err != nil { log.Error("Error building diff for updated accounts", "error", err) - return nil, err + return StateDiff{}, err } createdAccounts, err := sdb.buildDiffEventual(creations) if err != nil { log.Error("Error building diff for created accounts", "error", err) - return nil, err + return StateDiff{}, err } deletedAccounts, err := sdb.buildDiffEventual(deletions) if err != nil { log.Error("Error building diff for deleted accounts", "error", err) - return nil, err + return StateDiff{}, err } - return &StateDiff{ + return StateDiff{ BlockNumber: blockNumber, BlockHash: blockHash, CreatedAccounts: createdAccounts, @@ -118,20 +116,31 @@ func (sdb *builder) collectDiffNodes(a, b trie.NodeIterator) (AccountsMap, error for { log.Debug("Current Path and Hash", "path", pathToStr(it), "hashold", it.Hash()) if it.Leaf() { + leafProof := make([][]byte, len(it.LeafProof())) + copy(leafProof, it.LeafProof()) + leafPath := make([]byte, len(it.Path())) + copy(leafPath, it.Path()) leafKey := make([]byte, len(it.LeafKey())) copy(leafKey, it.LeafKey()) leafKeyHash := common.BytesToHash(leafKey) - + leafValue := make([]byte, len(it.LeafBlob())) + copy(leafValue, it.LeafBlob()) // lookup account state var account state.Account - if err := rlp.DecodeBytes(it.LeafBlob(), &account); err != nil { + if err := rlp.DecodeBytes(leafValue, &account); err != nil { log.Error("Error looking up account via address", "address", leafKeyHash, "error", err) return nil, err } - + aw := accountWrapper{ + Account: account, + RawKey: leafKey, + RawValue: leafValue, + Proof: leafProof, + Path: leafPath, + } // record account to diffs (creation if we are looking at new - old; deletion if old - new) log.Debug("Account lookup successful", "address", leafKeyHash, "account", account) - diffAccounts[leafKeyHash] = &account + diffAccounts[leafKeyHash] = &aw } cont := it.Next(true) if !cont { @@ -145,24 +154,18 @@ func (sdb *builder) collectDiffNodes(a, b trie.NodeIterator) (AccountsMap, error func (sdb *builder) buildDiffEventual(accounts AccountsMap) (AccountDiffsMap, error) { accountDiffs := make(AccountDiffsMap) for addr, val := range accounts { - sr := val.Root + sr := val.Account.Root storageDiffs, err := sdb.buildStorageDiffsEventual(sr) if err != nil { log.Error("Failed building eventual storage diffs", "Address", addr, "error", err) return nil, err } - - codeHash := hexutil.Encode(val.CodeHash) - hexRoot := val.Root.Hex() - nonce := DiffUint64{Value: &val.Nonce} - balance := DiffBigInt{Value: val.Balance} - contractRoot := DiffString{Value: &hexRoot} accountDiffs[addr] = AccountDiff{ - Nonce: nonce, - Balance: balance, - CodeHash: codeHash, - ContractRoot: contractRoot, - Storage: storageDiffs, + Key: val.RawKey, + Value: val.RawValue, + Proof: val.Proof, + Path: val.Path, + Storage: storageDiffs, } } @@ -174,25 +177,18 @@ func (sdb *builder) buildDiffIncremental(creations AccountsMap, deletions Accoun for _, val := range updatedKeys { createdAcc := creations[common.HexToHash(val)] deletedAcc := deletions[common.HexToHash(val)] - oldSR := deletedAcc.Root - newSR := createdAcc.Root + oldSR := deletedAcc.Account.Root + newSR := createdAcc.Account.Root if storageDiffs, err := sdb.buildStorageDiffsIncremental(oldSR, newSR); err != nil { log.Error("Failed building storage diffs", "Address", val, "error", err) return nil, err } else { - nonce := DiffUint64{Value: &createdAcc.Nonce} - balance := DiffBigInt{Value: createdAcc.Balance} - codeHash := hexutil.Encode(createdAcc.CodeHash) - - nHexRoot := createdAcc.Root.Hex() - contractRoot := DiffString{Value: &nHexRoot} - updatedAccounts[common.HexToHash(val)] = AccountDiff{ - Nonce: nonce, - Balance: balance, - CodeHash: codeHash, - ContractRoot: contractRoot, - Storage: storageDiffs, + Key: createdAcc.RawKey, + Value: createdAcc.RawValue, + Proof: createdAcc.Proof, + Path: createdAcc.Path, + Storage: storageDiffs, } delete(creations, common.HexToHash(val)) delete(deletions, common.HexToHash(val)) @@ -201,7 +197,7 @@ func (sdb *builder) buildDiffIncremental(creations AccountsMap, deletions Accoun return updatedAccounts, nil } -func (sdb *builder) buildStorageDiffsEventual(sr common.Hash) (map[string]DiffStorage, error) { +func (sdb *builder) buildStorageDiffsEventual(sr common.Hash) ([]StorageDiff, error) { log.Debug("Storage Root For Eventual Diff", "root", sr.Hex()) stateCache := sdb.blockChain.StateCache() sTrie, err := stateCache.OpenTrie(sr) @@ -214,7 +210,7 @@ func (sdb *builder) buildStorageDiffsEventual(sr common.Hash) (map[string]DiffSt return storageDiffs, nil } -func (sdb *builder) buildStorageDiffsIncremental(oldSR common.Hash, newSR common.Hash) (map[string]DiffStorage, error) { +func (sdb *builder) buildStorageDiffsIncremental(oldSR common.Hash, newSR common.Hash) ([]StorageDiff, error) { log.Debug("Storage Roots for Incremental Diff", "old", oldSR.Hex(), "new", newSR.Hex()) stateCache := sdb.blockChain.StateCache() @@ -235,21 +231,27 @@ func (sdb *builder) buildStorageDiffsIncremental(oldSR common.Hash, newSR common return storageDiffs, nil } -func buildStorageDiffsFromTrie(it trie.NodeIterator) map[string]DiffStorage { - storageDiffs := make(map[string]DiffStorage) +func buildStorageDiffsFromTrie(it trie.NodeIterator) []StorageDiff { + storageDiffs := make([]StorageDiff, 0) for { log.Debug("Iterating over state at path ", "path", pathToStr(it)) if it.Leaf() { log.Debug("Found leaf in storage", "path", pathToStr(it)) - path := pathToStr(it) - storageKey := hexutil.Encode(it.LeafKey()) - storageValue := hexutil.Encode(it.LeafBlob()) - storageDiffs[path] = DiffStorage{ - Key: &storageKey, - Value: &storageValue, - } + leafProof := make([][]byte, len(it.LeafProof())) + copy(leafProof, it.LeafProof()) + leafPath := make([]byte, len(it.Path())) + copy(leafPath, it.Path()) + leafKey := make([]byte, len(it.LeafKey())) + copy(leafKey, it.LeafKey()) + leafValue := make([]byte, len(it.LeafBlob())) + copy(leafValue, it.LeafBlob()) + storageDiffs = append(storageDiffs, StorageDiff{ + Key: leafKey, + Value: leafValue, + Path: leafPath, + Proof: leafProof, + }) } - cont := it.Next(true) if !cont { break diff --git a/statediff/builder/builder_test.go b/statediff/builder/builder_test.go deleted file mode 100644 index 7b3307f30..000000000 --- a/statediff/builder/builder_test.go +++ /dev/null @@ -1,381 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package builder_test - -import ( - "bytes" - "math/big" - "reflect" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core" - "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/ethdb" - "github.com/ethereum/go-ethereum/params" - b "github.com/ethereum/go-ethereum/statediff/builder" - "github.com/ethereum/go-ethereum/statediff/testhelpers" -) - -var ( - testdb = ethdb.NewMemDatabase() - - testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) //0x71562b71999873DB5b286dF957af199Ec94617F7 - bankLeafKey = testhelpers.AddressToLeafKey(testBankAddress) - testBankFunds = big.NewInt(100000000) - genesis = core.GenesisBlockForTesting(testdb, testBankAddress, testBankFunds) - - account1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") - account2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") - account1Addr = crypto.PubkeyToAddress(account1Key.PublicKey) //0x703c4b2bD70c169f5717101CaeE543299Fc946C7 - account1LeafKey = testhelpers.AddressToLeafKey(account1Addr) - account2Addr = crypto.PubkeyToAddress(account2Key.PublicKey) //0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e - account2LeafKey = testhelpers.AddressToLeafKey(account2Addr) - contractCode = common.Hex2Bytes("608060405234801561001057600080fd5b50602060405190810160405280600160ff16815250600090600161003592919061003b565b506100a5565b826064810192821561006f579160200282015b8281111561006e578251829060ff1690559160200191906001019061004e565b5b50905061007c9190610080565b5090565b6100a291905b8082111561009e576000816000905550600101610086565b5090565b90565b610124806100b46000396000f3fe6080604052348015600f57600080fd5b5060043610604f576000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146054578063c16431b9146093575b600080fd5b607d60048036036020811015606857600080fd5b810190808035906020019092919050505060c8565b6040518082815260200191505060405180910390f35b60c66004803603604081101560a757600080fd5b81019080803590602001909291908035906020019092919050505060e0565b005b6000808260648110151560d757fe5b01549050919050565b8060008360648110151560ef57fe5b0181905550505056fea165627a7a7230582064e918c3140a117bf3aa65865a9b9e83fae21ad1720506e7933b2a9f54bb40260029") - contractAddr common.Address - contractLeafKey common.Hash - emptyAccountDiffEventualMap = make(b.AccountDiffsMap) - emptyAccountDiffIncrementalMap = make(b.AccountDiffsMap) - block0Hash, block1Hash, block2Hash, block3Hash common.Hash - block0, block1, block2, block3 *types.Block - builder b.Builder - miningReward = int64(2000000000000000000) - burnAddress = common.HexToAddress("0x0") - burnLeafKey = testhelpers.AddressToLeafKey(burnAddress) -) - -func TestBuilder(t *testing.T) { - _, blockMap, chain := makeChain(3, genesis) - contractLeafKey = testhelpers.AddressToLeafKey(contractAddr) - defer chain.Stop() - block0Hash = common.HexToHash("0xd1721cfd0b29c36fd7a68f25c128e86413fb666a6e1d68e89b875bd299262661") - block1Hash = common.HexToHash("0xbbe88de60ba33a3f18c0caa37d827bfb70252e19e40a07cd34041696c35ecb1a") - block2Hash = common.HexToHash("0xde75663f36a8497b4bdda2a4b52bd9540b705a2728c7391c59b8cb2cde5a2feb") - block3Hash = common.HexToHash("0x76c6d0e39285cee40d5e5fadc6141ca88c8ab8bd1a15d46717205af2efbb4a3c") - - block0 = blockMap[block0Hash] - block1 = blockMap[block1Hash] - block2 = blockMap[block2Hash] - block3 = blockMap[block3Hash] - builder = b.NewBuilder(testdb, chain) - - type arguments struct { - oldStateRoot common.Hash - newStateRoot common.Hash - blockNumber int64 - blockHash common.Hash - } - - var ( - balanceChange10000 = int64(10000) - balanceChange1000 = int64(1000) - block1BankBalance = int64(99990000) - block1Account1Balance = int64(10000) - block2Account2Balance = int64(1000) - nonce0 = uint64(0) - nonce1 = uint64(1) - nonce2 = uint64(2) - nonce3 = uint64(3) - originalContractRoot = "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" - contractContractRoot = "0x821e2556a290c86405f8160a2d662042a431ba456b9db265c79bb837c04be5f0" - newContractRoot = "0x71e0d14b2b93e5c7f9748e69e1fe5f17498a1c3ac3cec29f96af13d7f8a4e070" - originalStorageLocation = common.HexToHash("0") - originalStorageKey = crypto.Keccak256Hash(originalStorageLocation[:]).String() - updatedStorageLocation = common.HexToHash("2") - updatedStorageKey = crypto.Keccak256Hash(updatedStorageLocation[:]).String() - originalStorageValue = "0x01" - updatedStorageValue = "0x03" - ) - - var tests = []struct { - name string - startingArguments arguments - expected *b.StateDiff - }{ - { - "testEmptyDiff", - arguments{ - oldStateRoot: block0.Root(), - newStateRoot: block0.Root(), - blockNumber: block0.Number().Int64(), - blockHash: block0Hash, - }, - &b.StateDiff{ - BlockNumber: block0.Number().Int64(), - BlockHash: block0Hash, - CreatedAccounts: emptyAccountDiffEventualMap, - DeletedAccounts: emptyAccountDiffEventualMap, - UpdatedAccounts: emptyAccountDiffIncrementalMap, - }, - }, - { - "testBlock1", - //10000 transferred from testBankAddress to account1Addr - arguments{ - oldStateRoot: block0.Root(), - newStateRoot: block1.Root(), - blockNumber: block1.Number().Int64(), - blockHash: block1Hash, - }, - &b.StateDiff{ - BlockNumber: block1.Number().Int64(), - BlockHash: block1.Hash(), - CreatedAccounts: b.AccountDiffsMap{ - account1LeafKey: { - Nonce: b.DiffUint64{Value: &nonce0}, - Balance: b.DiffBigInt{Value: big.NewInt(balanceChange10000)}, - CodeHash: "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", - ContractRoot: b.DiffString{Value: &originalContractRoot}, - Storage: map[string]b.DiffStorage{}, - }, - burnLeafKey: { - Nonce: b.DiffUint64{Value: &nonce0}, - Balance: b.DiffBigInt{Value: big.NewInt(miningReward)}, - CodeHash: "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", - ContractRoot: b.DiffString{Value: &originalContractRoot}, - Storage: map[string]b.DiffStorage{}, - }, - }, - DeletedAccounts: emptyAccountDiffEventualMap, - UpdatedAccounts: b.AccountDiffsMap{ - bankLeafKey: { - Nonce: b.DiffUint64{Value: &nonce1}, - Balance: b.DiffBigInt{Value: big.NewInt(testBankFunds.Int64() - balanceChange10000)}, - CodeHash: "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", - ContractRoot: b.DiffString{Value: &originalContractRoot}, - Storage: map[string]b.DiffStorage{}, - }, - }, - }, - }, - { - "testBlock2", - //1000 transferred from testBankAddress to account1Addr - //1000 transferred from account1Addr to account2Addr - arguments{ - oldStateRoot: block1.Root(), - newStateRoot: block2.Root(), - blockNumber: block2.Number().Int64(), - blockHash: block2Hash, - }, - &b.StateDiff{ - BlockNumber: block2.Number().Int64(), - BlockHash: block2.Hash(), - CreatedAccounts: b.AccountDiffsMap{ - account2LeafKey: { - Nonce: b.DiffUint64{Value: &nonce0}, - Balance: b.DiffBigInt{Value: big.NewInt(balanceChange1000)}, - CodeHash: "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", - ContractRoot: b.DiffString{Value: &originalContractRoot}, - Storage: map[string]b.DiffStorage{}, - }, - contractLeafKey: { - Nonce: b.DiffUint64{Value: &nonce1}, - Balance: b.DiffBigInt{Value: big.NewInt(0)}, - CodeHash: "0x753f98a8d4328b15636e46f66f2cb4bc860100aa17967cc145fcd17d1d4710ea", - ContractRoot: b.DiffString{Value: &contractContractRoot}, - Storage: map[string]b.DiffStorage{ - originalStorageKey: { - Key: &originalStorageKey, - Value: &originalStorageValue}, - }, - }, - }, - DeletedAccounts: emptyAccountDiffEventualMap, - UpdatedAccounts: b.AccountDiffsMap{ - bankLeafKey: { - Nonce: b.DiffUint64{Value: &nonce2}, - Balance: b.DiffBigInt{Value: big.NewInt(block1BankBalance - balanceChange1000)}, - CodeHash: "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", - ContractRoot: b.DiffString{Value: &originalContractRoot}, - Storage: map[string]b.DiffStorage{}, - }, - account1LeafKey: { - Nonce: b.DiffUint64{Value: &nonce2}, - Balance: b.DiffBigInt{Value: big.NewInt(block1Account1Balance - balanceChange1000 + balanceChange1000)}, - CodeHash: "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", - ContractRoot: b.DiffString{Value: &originalContractRoot}, - Storage: map[string]b.DiffStorage{}, - }, - burnLeafKey: { - Nonce: b.DiffUint64{Value: &nonce0}, - Balance: b.DiffBigInt{Value: big.NewInt(miningReward + miningReward)}, - CodeHash: "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", - ContractRoot: b.DiffString{Value: &originalContractRoot}, - Storage: map[string]b.DiffStorage{}, - }, - }, - }, - }, - { - "testBlock3", - //the contract's storage is changed - //and the block is mined by account 2 - arguments{ - oldStateRoot: block2.Root(), - newStateRoot: block3.Root(), - blockNumber: block3.Number().Int64(), - blockHash: block3.Hash(), - }, - &b.StateDiff{ - BlockNumber: block3.Number().Int64(), - BlockHash: block3.Hash(), - CreatedAccounts: b.AccountDiffsMap{}, - DeletedAccounts: emptyAccountDiffEventualMap, - UpdatedAccounts: b.AccountDiffsMap{ - account2LeafKey: { - Nonce: b.DiffUint64{Value: &nonce0}, - Balance: b.DiffBigInt{Value: big.NewInt(block2Account2Balance + miningReward)}, - CodeHash: "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", - ContractRoot: b.DiffString{Value: &originalContractRoot}, - Storage: map[string]b.DiffStorage{}, - }, - contractLeafKey: { - Nonce: b.DiffUint64{Value: &nonce1}, - Balance: b.DiffBigInt{Value: big.NewInt(0)}, - CodeHash: "0x753f98a8d4328b15636e46f66f2cb4bc860100aa17967cc145fcd17d1d4710ea", - ContractRoot: b.DiffString{Value: &newContractRoot}, - Storage: map[string]b.DiffStorage{ - "0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace": { - Key: &updatedStorageKey, - Value: &updatedStorageValue}, - }, - }, - bankLeafKey: { - Nonce: b.DiffUint64{Value: &nonce3}, - Balance: b.DiffBigInt{Value: big.NewInt(99989000)}, - CodeHash: "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", - ContractRoot: b.DiffString{Value: &originalContractRoot}, - Storage: map[string]b.DiffStorage{}, - }, - }, - }, - }, - } - - for _, test := range tests { - arguments := test.startingArguments - diff, err := builder.BuildStateDiff(arguments.oldStateRoot, arguments.newStateRoot, arguments.blockNumber, arguments.blockHash) - if err != nil { - t.Error(err) - } - fields := []string{"BlockNumber", "BlockHash", "DeletedAccounts", "UpdatedAccounts", "CreatedAccounts"} - - for _, field := range fields { - reflectionOfDiff := reflect.ValueOf(diff) - diffValue := reflect.Indirect(reflectionOfDiff).FieldByName(field) - - reflectionOfExpected := reflect.ValueOf(test.expected) - expectedValue := reflect.Indirect(reflectionOfExpected).FieldByName(field) - - diffValueInterface := diffValue.Interface() - expectedValueInterface := expectedValue.Interface() - - if !equals(diffValueInterface, expectedValueInterface) { - t.Logf("Test failed: %s", test.name) - t.Errorf("field: %+v\nactual: %+v\nexpected: %+v", field, diffValueInterface, expectedValueInterface) - } - } - } -} - -func equals(actual, expected interface{}) (success bool) { - if actualByteSlice, ok := actual.([]byte); ok { - if expectedByteSlice, ok := expected.([]byte); ok { - return bytes.Equal(actualByteSlice, expectedByteSlice) - } - } - - return reflect.DeepEqual(actual, expected) -} - -// makeChain creates a chain of n blocks starting at and including parent. -// the returned hash chain is ordered head->parent. In addition, every 3rd block -// contains a transaction and every 5th an uncle to allow testing correct block -// reassembly. -func makeChain(n int, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block, *core.BlockChain) { - blocks, _ := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testdb, n, testChainGen) - headers := make([]*types.Header, len(blocks)) - for i, block := range blocks { - headers[i] = block.Header() - } - chain, _ := core.NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil) - - hashes := make([]common.Hash, n+1) - hashes[len(hashes)-1] = parent.Hash() - blockm := make(map[common.Hash]*types.Block, n+1) - blockm[parent.Hash()] = parent - for i, b := range blocks { - hashes[len(hashes)-i-2] = b.Hash() - blockm[b.Hash()] = b - } - return hashes, blockm, chain -} - -func testChainGen(i int, block *core.BlockGen) { - signer := types.HomesteadSigner{} - switch i { - case 0: - // In block 1, the test bank sends account #1 some ether. - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), account1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) - block.AddTx(tx) - case 1: - // In block 2, the test bank sends some more ether to account #1. - // account1Addr passes it on to account #2. - // account1Addr creates a test contract. - tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), account1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey) - nonce := block.TxNonce(account1Addr) - tx2, _ := types.SignTx(types.NewTransaction(nonce, account2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, account1Key) - nonce++ - tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, big.NewInt(0), contractCode), signer, account1Key) - contractAddr = crypto.CreateAddress(account1Addr, nonce) //0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476592 - block.AddTx(tx1) - block.AddTx(tx2) - block.AddTx(tx3) - case 2: - // Block 3 is empty but was mined by account #2. - block.SetCoinbase(account2Addr) - //get function: 60cd2685 - //put function: c16431b9 - data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), contractAddr, big.NewInt(0), 100000, nil, data), signer, testBankKey) - block.AddTx(tx) - } -} - -/* -contract test { - - uint256[100] data; - - constructor() public { - data = [1]; - } - - function Put(uint256 addr, uint256 value) { - data[addr] = value; - } - - function Get(uint256 addr) constant returns (uint256 value) { - return data[addr]; - } -} -*/ diff --git a/statediff/builder_test.go b/statediff/builder_test.go new file mode 100644 index 000000000..d74a285dc --- /dev/null +++ b/statediff/builder_test.go @@ -0,0 +1,467 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package statediff_test + +import ( + "bytes" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/rlp" + "math/big" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core" + "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/ethdb" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/statediff" + "github.com/ethereum/go-ethereum/statediff/testhelpers" +) + +var ( + testdb = ethdb.NewMemDatabase() + + testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) //0x71562b71999873DB5b286dF957af199Ec94617F7 + bankLeafKey = testhelpers.AddressToLeafKey(testBankAddress) + testBankFunds = big.NewInt(100000000) + genesis = core.GenesisBlockForTesting(testdb, testBankAddress, testBankFunds) + + account1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + account2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + account1Addr = crypto.PubkeyToAddress(account1Key.PublicKey) //0x703c4b2bD70c169f5717101CaeE543299Fc946C7 + account1LeafKey = testhelpers.AddressToLeafKey(account1Addr) + account2Addr = crypto.PubkeyToAddress(account2Key.PublicKey) //0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e + account2LeafKey = testhelpers.AddressToLeafKey(account2Addr) + contractCode = common.Hex2Bytes("608060405234801561001057600080fd5b50602060405190810160405280600160ff16815250600090600161003592919061003b565b506100a5565b826064810192821561006f579160200282015b8281111561006e578251829060ff1690559160200191906001019061004e565b5b50905061007c9190610080565b5090565b6100a291905b8082111561009e576000816000905550600101610086565b5090565b90565b610124806100b46000396000f3fe6080604052348015600f57600080fd5b5060043610604f576000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146054578063c16431b9146093575b600080fd5b607d60048036036020811015606857600080fd5b810190808035906020019092919050505060c8565b6040518082815260200191505060405180910390f35b60c66004803603604081101560a757600080fd5b81019080803590602001909291908035906020019092919050505060e0565b005b6000808260648110151560d757fe5b01549050919050565b8060008360648110151560ef57fe5b0181905550505056fea165627a7a7230582064e918c3140a117bf3aa65865a9b9e83fae21ad1720506e7933b2a9f54bb40260029") + contractAddr common.Address + contractLeafKey common.Hash + emptyAccountDiffEventualMap = make(statediff.AccountDiffsMap) + emptyAccountDiffIncrementalMap = make(statediff.AccountDiffsMap) + block0Hash, block1Hash, block2Hash, block3Hash common.Hash + block0, block1, block2, block3 *types.Block + builder statediff.Builder + miningReward = int64(2000000000000000000) + burnAddress = common.HexToAddress("0x0") + burnLeafKey = testhelpers.AddressToLeafKey(burnAddress) +) + +func TestBuilder(t *testing.T) { + _, blockMap, chain := makeChain(3, genesis) + contractLeafKey = testhelpers.AddressToLeafKey(contractAddr) + defer chain.Stop() + block0Hash = common.HexToHash("0xd1721cfd0b29c36fd7a68f25c128e86413fb666a6e1d68e89b875bd299262661") + block1Hash = common.HexToHash("0xbbe88de60ba33a3f18c0caa37d827bfb70252e19e40a07cd34041696c35ecb1a") + block2Hash = common.HexToHash("0xde75663f36a8497b4bdda2a4b52bd9540b705a2728c7391c59b8cb2cde5a2feb") + block3Hash = common.HexToHash("0x76c6d0e39285cee40d5e5fadc6141ca88c8ab8bd1a15d46717205af2efbb4a3c") + + block0 = blockMap[block0Hash] + block1 = blockMap[block1Hash] + block2 = blockMap[block2Hash] + block3 = blockMap[block3Hash] + builder = statediff.NewBuilder(testdb, chain) + + type arguments struct { + oldStateRoot common.Hash + newStateRoot common.Hash + blockNumber int64 + blockHash common.Hash + } + + var ( + balanceChange10000 = int64(10000) + balanceChange1000 = int64(1000) + block1BankBalance = int64(99990000) + block1Account1Balance = int64(10000) + block2Account2Balance = int64(1000) + nonce0 = uint64(0) + nonce1 = uint64(1) + nonce2 = uint64(2) + nonce3 = uint64(3) + originalContractRoot = "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" + contractContractRoot = "0x821e2556a290c86405f8160a2d662042a431ba456b9db265c79bb837c04be5f0" + newContractRoot = "0x71e0d14b2b93e5c7f9748e69e1fe5f17498a1c3ac3cec29f96af13d7f8a4e070" + originalStorageLocation = common.HexToHash("0") + originalStorageKey = crypto.Keccak256Hash(originalStorageLocation[:]).Bytes() + updatedStorageLocation = common.HexToHash("2") + updatedStorageKey = crypto.Keccak256Hash(updatedStorageLocation[:]).Bytes() + originalStorageValue = common.Hex2Bytes("01") + updatedStorageValue = common.Hex2Bytes("03") + account1, _ = rlp.EncodeToBytes(state.Account{ + Nonce: nonce0, + Balance: big.NewInt(balanceChange10000), + CodeHash: common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(), + Root: common.HexToHash(originalContractRoot), + }) + burnAccount1, _ = rlp.EncodeToBytes(state.Account{ + Nonce: nonce0, + Balance: big.NewInt(miningReward), + CodeHash: common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(), + Root: common.HexToHash(originalContractRoot), + }) + bankAccount1, _ = rlp.EncodeToBytes(state.Account{ + Nonce: nonce1, + Balance: big.NewInt(testBankFunds.Int64() - balanceChange10000), + CodeHash: common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(), + Root: common.HexToHash(originalContractRoot), + }) + account2, _ = rlp.EncodeToBytes(state.Account{ + Nonce: nonce0, + Balance: big.NewInt(balanceChange1000), + CodeHash: common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(), + Root: common.HexToHash(originalContractRoot), + }) + contractAccount, _ = rlp.EncodeToBytes(state.Account{ + Nonce: nonce1, + Balance: big.NewInt(0), + CodeHash: common.HexToHash("0x753f98a8d4328b15636e46f66f2cb4bc860100aa17967cc145fcd17d1d4710ea").Bytes(), + Root: common.HexToHash(contractContractRoot), + }) + bankAccount2, _ = rlp.EncodeToBytes(state.Account{ + Nonce: nonce2, + Balance: big.NewInt(block1BankBalance - balanceChange1000), + CodeHash: common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(), + Root: common.HexToHash(originalContractRoot), + }) + account3, _ = rlp.EncodeToBytes(state.Account{ + Nonce: nonce2, + Balance: big.NewInt(block1Account1Balance - balanceChange1000 + balanceChange1000), + CodeHash: common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(), + Root: common.HexToHash(originalContractRoot), + }) + burnAccount2, _ = rlp.EncodeToBytes(state.Account{ + Nonce: nonce0, + Balance: big.NewInt(miningReward + miningReward), + CodeHash: common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(), + Root: common.HexToHash(originalContractRoot), + }) + account4, _ = rlp.EncodeToBytes(state.Account{ + Nonce: nonce0, + Balance: big.NewInt(block2Account2Balance + miningReward), + CodeHash: common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(), + Root: common.HexToHash(originalContractRoot), + }) + contractAccount2, _ = rlp.EncodeToBytes(state.Account{ + Nonce: nonce1, + Balance: big.NewInt(0), + CodeHash: common.HexToHash("0x753f98a8d4328b15636e46f66f2cb4bc860100aa17967cc145fcd17d1d4710ea").Bytes(), + Root: common.HexToHash(newContractRoot), + }) + bankAccount3, _ = rlp.EncodeToBytes(state.Account{ + Nonce: nonce3, + Balance: big.NewInt(99989000), + CodeHash: common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(), + Root: common.HexToHash(originalContractRoot), + }) + ) + + var tests = []struct { + name string + startingArguments arguments + expected *statediff.StateDiff + }{ + { + "testEmptyDiff", + arguments{ + oldStateRoot: block0.Root(), + newStateRoot: block0.Root(), + blockNumber: block0.Number().Int64(), + blockHash: block0Hash, + }, + &statediff.StateDiff{ + BlockNumber: block0.Number().Int64(), + BlockHash: block0Hash, + CreatedAccounts: emptyAccountDiffEventualMap, + DeletedAccounts: emptyAccountDiffEventualMap, + UpdatedAccounts: emptyAccountDiffIncrementalMap, + }, + }, + { + "testBlock1", + //10000 transferred from testBankAddress to account1Addr + arguments{ + oldStateRoot: block0.Root(), + newStateRoot: block1.Root(), + blockNumber: block1.Number().Int64(), + blockHash: block1Hash, + }, + &statediff.StateDiff{ + BlockNumber: block1.Number().Int64(), + BlockHash: block1.Hash(), + CreatedAccounts: statediff.AccountDiffsMap{ + account1LeafKey: { + Key: account1LeafKey.Bytes(), + Value: account1, + Proof: [][]byte{{248, 113, 160, 87, 118, 82, 182, 37, 183, 123, 219, 91, 247, 123, 196, 63, 49, 37, 202, 215, 70, 77, 103, 157, 21, 117, 86, 82, 119, 211, 97, 27, 128, 83, 231, 128, 128, 128, 128, 160, 254, 136, 159, 16, 229, 219, 143, 44, 43, 243, 85, 146, 129, 82, 161, 127, 110, 59, 185, 154, 146, 65, 172, 109, 132, 199, 126, 98, 100, 80, 156, 121, 128, 128, 128, 128, 128, 128, 128, 128, 160, 17, 219, 12, 218, 52, 168, 150, 218, 190, 182, 131, 155, 176, 106, 56, 244, 149, 20, 207, 164, 134, 67, 89, 132, 235, 1, 59, 125, 249, 238, 133, 197, 128, 128}, + {248, 107, 160, 57, 38, 219, 105, 170, 206, 213, 24, 233, 185, 240, 244, 52, 164, 115, 231, 23, 65, 9, 201, 67, 84, 139, 184, 242, 59, 228, 28, 167, 109, 154, 210, 184, 72, 248, 70, 128, 130, 39, 16, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, + Path: []byte{14, 9, 2, 6, 13, 11, 6, 9, 10, 10, 12, 14, 13, 5, 1, 8, 14, 9, 11, 9, 15, 0, 15, 4, 3, 4, 10, 4, 7, 3, 14, 7, 1, 7, 4, 1, 0, 9, 12, 9, 4, 3, 5, 4, 8, 11, 11, 8, 15, 2, 3, 11, 14, 4, 1, 12, 10, 7, 6, 13, 9, 10, 13, 2, 16}, + Storage: []statediff.StorageDiff{}, + }, + burnLeafKey: { + Key: burnLeafKey.Bytes(), + Value: burnAccount1, + Proof: [][]byte{{248, 113, 160, 87, 118, 82, 182, 37, 183, 123, 219, 91, 247, 123, 196, 63, 49, 37, 202, 215, 70, 77, 103, 157, 21, 117, 86, 82, 119, 211, 97, 27, 128, 83, 231, 128, 128, 128, 128, 160, 254, 136, 159, 16, 229, 219, 143, 44, 43, 243, 85, 146, 129, 82, 161, 127, 110, 59, 185, 154, 146, 65, 172, 109, 132, 199, 126, 98, 100, 80, 156, 121, 128, 128, 128, 128, 128, 128, 128, 128, 160, 17, 219, 12, 218, 52, 168, 150, 218, 190, 182, 131, 155, 176, 106, 56, 244, 149, 20, 207, 164, 134, 67, 89, 132, 235, 1, 59, 125, 249, 238, 133, 197, 128, 128}, + {248, 113, 160, 51, 128, 199, 183, 174, 129, 165, 142, 185, 141, 156, 120, 222, 74, 31, 215, 253, 149, 53, 252, 149, 62, 210, 190, 96, 45, 170, 164, 23, 103, 49, 42, 184, 78, 248, 76, 128, 136, 27, 193, 109, 103, 78, 200, 0, 0, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, + Path: []byte{5, 3, 8, 0, 12, 7, 11, 7, 10, 14, 8, 1, 10, 5, 8, 14, 11, 9, 8, 13, 9, 12, 7, 8, 13, 14, 4, 10, 1, 15, 13, 7, 15, 13, 9, 5, 3, 5, 15, 12, 9, 5, 3, 14, 13, 2, 11, 14, 6, 0, 2, 13, 10, 10, 10, 4, 1, 7, 6, 7, 3, 1, 2, 10, 16}, + Storage: []statediff.StorageDiff{}, + }, + }, + DeletedAccounts: emptyAccountDiffEventualMap, + UpdatedAccounts: statediff.AccountDiffsMap{ + bankLeafKey: { + Key: bankLeafKey.Bytes(), + Value: bankAccount1, + Proof: [][]byte{{248, 113, 160, 87, 118, 82, 182, 37, 183, 123, 219, 91, 247, 123, 196, 63, 49, 37, 202, 215, 70, 77, 103, 157, 21, 117, 86, 82, 119, 211, 97, 27, 128, 83, 231, 128, 128, 128, 128, 160, 254, 136, 159, 16, 229, 219, 143, 44, 43, 243, 85, 146, 129, 82, 161, 127, 110, 59, 185, 154, 146, 65, 172, 109, 132, 199, 126, 98, 100, 80, 156, 121, 128, 128, 128, 128, 128, 128, 128, 128, 160, 17, 219, 12, 218, 52, 168, 150, 218, 190, 182, 131, 155, 176, 106, 56, 244, 149, 20, 207, 164, 134, 67, 89, 132, 235, 1, 59, 125, 249, 238, 133, 197, 128, 128}, + {248, 109, 160, 48, 191, 73, 244, 64, 161, 205, 5, 39, 228, 208, 110, 39, 101, 101, 76, 15, 86, 69, 34, 87, 81, 109, 121, 58, 155, 141, 96, 77, 207, 223, 42, 184, 74, 248, 72, 1, 132, 5, 245, 185, 240, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, + Path: []byte{0, 0, 11, 15, 4, 9, 15, 4, 4, 0, 10, 1, 12, 13, 0, 5, 2, 7, 14, 4, 13, 0, 6, 14, 2, 7, 6, 5, 6, 5, 4, 12, 0, 15, 5, 6, 4, 5, 2, 2, 5, 7, 5, 1, 6, 13, 7, 9, 3, 10, 9, 11, 8, 13, 6, 0, 4, 13, 12, 15, 13, 15, 2, 10, 16}, + Storage: []statediff.StorageDiff{}, + }, + }, + }, + }, + { + "testBlock2", + //1000 transferred from testBankAddress to account1Addr + //1000 transferred from account1Addr to account2Addr + arguments{ + oldStateRoot: block1.Root(), + newStateRoot: block2.Root(), + blockNumber: block2.Number().Int64(), + blockHash: block2Hash, + }, + &statediff.StateDiff{ + BlockNumber: block2.Number().Int64(), + BlockHash: block2.Hash(), + CreatedAccounts: statediff.AccountDiffsMap{ + account2LeafKey: { + Key: account2LeafKey.Bytes(), + Value: account2, + Proof: [][]byte{{248, 177, 160, 177, 155, 238, 178, 242, 47, 83, 2, 49, 141, 155, 92, 149, 175, 245, 120, 233, 177, 101, 67, 46, 200, 23, 250, 41, 74, 135, 94, 61, 133, 51, 162, 128, 128, 128, 128, 160, 179, 86, 53, 29, 96, 188, 152, 148, 207, 31, 29, 108, 182, 140, 129, 95, 1, 49, 213, 15, 29, 168, 60, 64, 35, 160, 158, 200, 85, 207, 255, 145, 160, 114, 57, 32, 11, 115, 232, 140, 238, 165, 222, 121, 226, 208, 2, 192, 216, 67, 198, 179, 31, 181, 27, 208, 243, 99, 202, 48, 148, 207, 107, 106, 177, 128, 128, 128, 128, 128, 160, 10, 173, 165, 125, 110, 240, 77, 112, 149, 100, 135, 237, 25, 228, 116, 7, 195, 9, 210, 166, 208, 148, 101, 23, 244, 238, 84, 84, 211, 249, 138, 137, 128, 160, 255, 115, 147, 190, 57, 135, 174, 188, 86, 51, 227, 70, 22, 253, 237, 49, 24, 19, 149, 199, 142, 195, 186, 244, 70, 51, 138, 0, 146, 148, 117, 60, 128, 128}, + {248, 107, 160, 57, 87, 243, 226, 240, 74, 7, 100, 195, 160, 73, 27, 23, 95, 105, 146, 109, 166, 30, 251, 204, 143, 97, 250, 20, 85, 253, 45, 43, 76, 221, 69, 184, 72, 248, 70, 128, 130, 3, 232, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, + Path: []byte{12, 9, 5, 7, 15, 3, 14, 2, 15, 0, 4, 10, 0, 7, 6, 4, 12, 3, 10, 0, 4, 9, 1, 11, 1, 7, 5, 15, 6, 9, 9, 2, 6, 13, 10, 6, 1, 14, 15, 11, 12, 12, 8, 15, 6, 1, 15, 10, 1, 4, 5, 5, 15, 13, 2, 13, 2, 11, 4, 12, 13, 13, 4, 5, 16}, + Storage: []statediff.StorageDiff{}, + }, + contractLeafKey: { + Key: contractLeafKey.Bytes(), + Value: contractAccount, + Proof: [][]byte{{248, 177, 160, 177, 155, 238, 178, 242, 47, 83, 2, 49, 141, 155, 92, 149, 175, 245, 120, 233, 177, 101, 67, 46, 200, 23, 250, 41, 74, 135, 94, 61, 133, 51, 162, 128, 128, 128, 128, 160, 179, 86, 53, 29, 96, 188, 152, 148, 207, 31, 29, 108, 182, 140, 129, 95, 1, 49, 213, 15, 29, 168, 60, 64, 35, 160, 158, 200, 85, 207, 255, 145, 160, 114, 57, 32, 11, 115, 232, 140, 238, 165, 222, 121, 226, 208, 2, 192, 216, 67, 198, 179, 31, 181, 27, 208, 243, 99, 202, 48, 148, 207, 107, 106, 177, 128, 128, 128, 128, 128, 160, 10, 173, 165, 125, 110, 240, 77, 112, 149, 100, 135, 237, 25, 228, 116, 7, 195, 9, 210, 166, 208, 148, 101, 23, 244, 238, 84, 84, 211, 249, 138, 137, 128, 160, 255, 115, 147, 190, 57, 135, 174, 188, 86, 51, 227, 70, 22, 253, 237, 49, 24, 19, 149, 199, 142, 195, 186, 244, 70, 51, 138, 0, 146, 148, 117, 60, 128, 128}, + {248, 105, 160, 49, 20, 101, 138, 116, 217, 204, 159, 122, 207, 44, 92, 214, 150, 195, 73, 77, 124, 52, 77, 120, 191, 236, 58, 221, 13, 145, 236, 78, 141, 28, 69, 184, 70, 248, 68, 1, 128, 160, 130, 30, 37, 86, 162, 144, 200, 100, 5, 248, 22, 10, 45, 102, 32, 66, 164, 49, 186, 69, 107, 157, 178, 101, 199, 155, 184, 55, 192, 75, 229, 240, 160, 117, 63, 152, 168, 212, 50, 139, 21, 99, 110, 70, 246, 111, 44, 180, 188, 134, 1, 0, 170, 23, 150, 124, 193, 69, 252, 209, 125, 29, 71, 16, 234}}, + Path: []byte{6, 1, 1, 4, 6, 5, 8, 10, 7, 4, 13, 9, 12, 12, 9, 15, 7, 10, 12, 15, 2, 12, 5, 12, 13, 6, 9, 6, 12, 3, 4, 9, 4, 13, 7, 12, 3, 4, 4, 13, 7, 8, 11, 15, 14, 12, 3, 10, 13, 13, 0, 13, 9, 1, 14, 12, 4, 14, 8, 13, 1, 12, 4, 5, 16}, + Storage: []statediff.StorageDiff{ + { + Key: originalStorageKey, + Value: originalStorageValue, + Proof: [][]byte{{227, 161, 32, 41, 13, 236, 217, 84, 139, 98, 168, 214, 3, 69, 169, 136, 56, 111, 200, 75, 166, 188, 149, 72, 64, 8, 246, 54, 47, 147, 22, 14, 243, 229, 99, 1}}, + Path: []byte{2, 9, 0, 13, 14, 12, 13, 9, 5, 4, 8, 11, 6, 2, 10, 8, 13, 6, 0, 3, 4, 5, 10, 9, 8, 8, 3, 8, 6, 15, 12, 8, 4, 11, 10, 6, 11, 12, 9, 5, 4, 8, 4, 0, 0, 8, 15, 6, 3, 6, 2, 15, 9, 3, 1, 6, 0, 14, 15, 3, 14, 5, 6, 3, 16}, + }, + }, + }, + }, + DeletedAccounts: emptyAccountDiffEventualMap, + UpdatedAccounts: statediff.AccountDiffsMap{ + bankLeafKey: { + Key: bankLeafKey.Bytes(), + Value: bankAccount2, + Proof: [][]byte{{248, 177, 160, 177, 155, 238, 178, 242, 47, 83, 2, 49, 141, 155, 92, 149, 175, 245, 120, 233, 177, 101, 67, 46, 200, 23, 250, 41, 74, 135, 94, 61, 133, 51, 162, 128, 128, 128, 128, 160, 179, 86, 53, 29, 96, 188, 152, 148, 207, 31, 29, 108, 182, 140, 129, 95, 1, 49, 213, 15, 29, 168, 60, 64, 35, 160, 158, 200, 85, 207, 255, 145, 160, 114, 57, 32, 11, 115, 232, 140, 238, 165, 222, 121, 226, 208, 2, 192, 216, 67, 198, 179, 31, 181, 27, 208, 243, 99, 202, 48, 148, 207, 107, 106, 177, 128, 128, 128, 128, 128, 160, 10, 173, 165, 125, 110, 240, 77, 112, 149, 100, 135, 237, 25, 228, 116, 7, 195, 9, 210, 166, 208, 148, 101, 23, 244, 238, 84, 84, 211, 249, 138, 137, 128, 160, 255, 115, 147, 190, 57, 135, 174, 188, 86, 51, 227, 70, 22, 253, 237, 49, 24, 19, 149, 199, 142, 195, 186, 244, 70, 51, 138, 0, 146, 148, 117, 60, 128, 128}, + {248, 109, 160, 48, 191, 73, 244, 64, 161, 205, 5, 39, 228, 208, 110, 39, 101, 101, 76, 15, 86, 69, 34, 87, 81, 109, 121, 58, 155, 141, 96, 77, 207, 223, 42, 184, 74, 248, 72, 2, 132, 5, 245, 182, 8, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, + Path: []byte{0, 0, 11, 15, 4, 9, 15, 4, 4, 0, 10, 1, 12, 13, 0, 5, 2, 7, 14, 4, 13, 0, 6, 14, 2, 7, 6, 5, 6, 5, 4, 12, 0, 15, 5, 6, 4, 5, 2, 2, 5, 7, 5, 1, 6, 13, 7, 9, 3, 10, 9, 11, 8, 13, 6, 0, 4, 13, 12, 15, 13, 15, 2, 10, 16}, + Storage: []statediff.StorageDiff{}, + }, + account1LeafKey: { + Key: account1LeafKey.Bytes(), + Value: account3, + Proof: [][]byte{{248, 177, 160, 177, 155, 238, 178, 242, 47, 83, 2, 49, 141, 155, 92, 149, 175, 245, 120, 233, 177, 101, 67, 46, 200, 23, 250, 41, 74, 135, 94, 61, 133, 51, 162, 128, 128, 128, 128, 160, 179, 86, 53, 29, 96, 188, 152, 148, 207, 31, 29, 108, 182, 140, 129, 95, 1, 49, 213, 15, 29, 168, 60, 64, 35, 160, 158, 200, 85, 207, 255, 145, 160, 114, 57, 32, 11, 115, 232, 140, 238, 165, 222, 121, 226, 208, 2, 192, 216, 67, 198, 179, 31, 181, 27, 208, 243, 99, 202, 48, 148, 207, 107, 106, 177, 128, 128, 128, 128, 128, 160, 10, 173, 165, 125, 110, 240, 77, 112, 149, 100, 135, 237, 25, 228, 116, 7, 195, 9, 210, 166, 208, 148, 101, 23, 244, 238, 84, 84, 211, 249, 138, 137, 128, 160, 255, 115, 147, 190, 57, 135, 174, 188, 86, 51, 227, 70, 22, 253, 237, 49, 24, 19, 149, 199, 142, 195, 186, 244, 70, 51, 138, 0, 146, 148, 117, 60, 128, 128}, + {248, 107, 160, 57, 38, 219, 105, 170, 206, 213, 24, 233, 185, 240, 244, 52, 164, 115, 231, 23, 65, 9, 201, 67, 84, 139, 184, 242, 59, 228, 28, 167, 109, 154, 210, 184, 72, 248, 70, 2, 130, 39, 16, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, + Path: []byte{14, 9, 2, 6, 13, 11, 6, 9, 10, 10, 12, 14, 13, 5, 1, 8, 14, 9, 11, 9, 15, 0, 15, 4, 3, 4, 10, 4, 7, 3, 14, 7, 1, 7, 4, 1, 0, 9, 12, 9, 4, 3, 5, 4, 8, 11, 11, 8, 15, 2, 3, 11, 14, 4, 1, 12, 10, 7, 6, 13, 9, 10, 13, 2, 16}, + Storage: []statediff.StorageDiff{}, + }, + burnLeafKey: { + Key: burnLeafKey.Bytes(), + Value: burnAccount2, + Proof: [][]byte{{248, 177, 160, 177, 155, 238, 178, 242, 47, 83, 2, 49, 141, 155, 92, 149, 175, 245, 120, 233, 177, 101, 67, 46, 200, 23, 250, 41, 74, 135, 94, 61, 133, 51, 162, 128, 128, 128, 128, 160, 179, 86, 53, 29, 96, 188, 152, 148, 207, 31, 29, 108, 182, 140, 129, 95, 1, 49, 213, 15, 29, 168, 60, 64, 35, 160, 158, 200, 85, 207, 255, 145, 160, 114, 57, 32, 11, 115, 232, 140, 238, 165, 222, 121, 226, 208, 2, 192, 216, 67, 198, 179, 31, 181, 27, 208, 243, 99, 202, 48, 148, 207, 107, 106, 177, 128, 128, 128, 128, 128, 160, 10, 173, 165, 125, 110, 240, 77, 112, 149, 100, 135, 237, 25, 228, 116, 7, 195, 9, 210, 166, 208, 148, 101, 23, 244, 238, 84, 84, 211, 249, 138, 137, 128, 160, 255, 115, 147, 190, 57, 135, 174, 188, 86, 51, 227, 70, 22, 253, 237, 49, 24, 19, 149, 199, 142, 195, 186, 244, 70, 51, 138, 0, 146, 148, 117, 60, 128, 128}, + {248, 113, 160, 51, 128, 199, 183, 174, 129, 165, 142, 185, 141, 156, 120, 222, 74, 31, 215, 253, 149, 53, 252, 149, 62, 210, 190, 96, 45, 170, 164, 23, 103, 49, 42, 184, 78, 248, 76, 128, 136, 55, 130, 218, 206, 157, 144, 0, 0, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, + Path: []byte{5, 3, 8, 0, 12, 7, 11, 7, 10, 14, 8, 1, 10, 5, 8, 14, 11, 9, 8, 13, 9, 12, 7, 8, 13, 14, 4, 10, 1, 15, 13, 7, 15, 13, 9, 5, 3, 5, 15, 12, 9, 5, 3, 14, 13, 2, 11, 14, 6, 0, 2, 13, 10, 10, 10, 4, 1, 7, 6, 7, 3, 1, 2, 10, 16}, + Storage: []statediff.StorageDiff{}, + }, + }, + }, + }, + { + "testBlock3", + //the contract's storage is changed + //and the block is mined by account 2 + arguments{ + oldStateRoot: block2.Root(), + newStateRoot: block3.Root(), + blockNumber: block3.Number().Int64(), + blockHash: block3.Hash(), + }, + &statediff.StateDiff{ + BlockNumber: block3.Number().Int64(), + BlockHash: block3.Hash(), + CreatedAccounts: statediff.AccountDiffsMap{}, + DeletedAccounts: emptyAccountDiffEventualMap, + UpdatedAccounts: statediff.AccountDiffsMap{ + account2LeafKey: { + Key: account2LeafKey.Bytes(), + Value: account4, + Proof: [][]byte{{248, 177, 160, 101, 223, 138, 81, 34, 40, 229, 170, 198, 188, 136, 99, 7, 55, 33, 112, 160, 111, 181, 131, 167, 201, 131, 24, 201, 211, 177, 30, 159, 229, 246, 6, 128, 128, 128, 128, 160, 179, 86, 53, 29, 96, 188, 152, 148, 207, 31, 29, 108, 182, 140, 129, 95, 1, 49, 213, 15, 29, 168, 60, 64, 35, 160, 158, 200, 85, 207, 255, 145, 160, 32, 135, 108, 213, 150, 150, 110, 44, 170, 65, 75, 154, 74, 249, 94, 65, 74, 107, 100, 115, 39, 5, 3, 26, 22, 238, 138, 114, 254, 21, 6, 171, 128, 128, 128, 128, 128, 160, 4, 228, 121, 222, 255, 218, 60, 247, 15, 0, 34, 198, 28, 229, 180, 129, 109, 157, 68, 181, 248, 229, 200, 123, 29, 81, 145, 114, 90, 209, 205, 210, 128, 160, 255, 115, 147, 190, 57, 135, 174, 188, 86, 51, 227, 70, 22, 253, 237, 49, 24, 19, 149, 199, 142, 195, 186, 244, 70, 51, 138, 0, 146, 148, 117, 60, 128, 128}, + {248, 113, 160, 57, 87, 243, 226, 240, 74, 7, 100, 195, 160, 73, 27, 23, 95, 105, 146, 109, 166, 30, 251, 204, 143, 97, 250, 20, 85, 253, 45, 43, 76, 221, 69, 184, 78, 248, 76, 128, 136, 27, 193, 109, 103, 78, 200, 3, 232, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, + Path: []byte{12, 9, 5, 7, 15, 3, 14, 2, 15, 0, 4, 10, 0, 7, 6, 4, 12, 3, 10, 0, 4, 9, 1, 11, 1, 7, 5, 15, 6, 9, 9, 2, 6, 13, 10, 6, 1, 14, 15, 11, 12, 12, 8, 15, 6, 1, 15, 10, 1, 4, 5, 5, 15, 13, 2, 13, 2, 11, 4, 12, 13, 13, 4, 5, 16}, + Storage: []statediff.StorageDiff{}, + }, + contractLeafKey: { + Key: contractLeafKey.Bytes(), + Value: contractAccount2, + Proof: [][]byte{{248, 177, 160, 101, 223, 138, 81, 34, 40, 229, 170, 198, 188, 136, 99, 7, 55, 33, 112, 160, 111, 181, 131, 167, 201, 131, 24, 201, 211, 177, 30, 159, 229, 246, 6, 128, 128, 128, 128, 160, 179, 86, 53, 29, 96, 188, 152, 148, 207, 31, 29, 108, 182, 140, 129, 95, 1, 49, 213, 15, 29, 168, 60, 64, 35, 160, 158, 200, 85, 207, 255, 145, 160, 32, 135, 108, 213, 150, 150, 110, 44, 170, 65, 75, 154, 74, 249, 94, 65, 74, 107, 100, 115, 39, 5, 3, 26, 22, 238, 138, 114, 254, 21, 6, 171, 128, 128, 128, 128, 128, 160, 4, 228, 121, 222, 255, 218, 60, 247, 15, 0, 34, 198, 28, 229, 180, 129, 109, 157, 68, 181, 248, 229, 200, 123, 29, 81, 145, 114, 90, 209, 205, 210, 128, 160, 255, 115, 147, 190, 57, 135, 174, 188, 86, 51, 227, 70, 22, 253, 237, 49, 24, 19, 149, 199, 142, 195, 186, 244, 70, 51, 138, 0, 146, 148, 117, 60, 128, 128}, + {248, 105, 160, 49, 20, 101, 138, 116, 217, 204, 159, 122, 207, 44, 92, 214, 150, 195, 73, 77, 124, 52, 77, 120, 191, 236, 58, 221, 13, 145, 236, 78, 141, 28, 69, 184, 70, 248, 68, 1, 128, 160, 113, 224, 209, 75, 43, 147, 229, 199, 249, 116, 142, 105, 225, 254, 95, 23, 73, 138, 28, 58, 195, 206, 194, 159, 150, 175, 19, 215, 248, 164, 224, 112, 160, 117, 63, 152, 168, 212, 50, 139, 21, 99, 110, 70, 246, 111, 44, 180, 188, 134, 1, 0, 170, 23, 150, 124, 193, 69, 252, 209, 125, 29, 71, 16, 234}}, + Path: []byte{6, 1, 1, 4, 6, 5, 8, 10, 7, 4, 13, 9, 12, 12, 9, 15, 7, 10, 12, 15, 2, 12, 5, 12, 13, 6, 9, 6, 12, 3, 4, 9, 4, 13, 7, 12, 3, 4, 4, 13, 7, 8, 11, 15, 14, 12, 3, 10, 13, 13, 0, 13, 9, 1, 14, 12, 4, 14, 8, 13, 1, 12, 4, 5, 16}, + Storage: []statediff.StorageDiff{ + { + Key: updatedStorageKey, + Value: updatedStorageValue, + Proof: [][]byte{{248, 81, 128, 128, 160, 79, 197, 241, 58, 178, 249, 186, 12, 45, 168, 139, 1, 81, 171, 14, 124, 244, 216, 93, 8, 204, 164, 92, 205, 146, 60, 106, 183, 99, 35, 235, 40, 128, 160, 205, 69, 114, 89, 105, 97, 21, 35, 94, 100, 199, 130, 35, 52, 214, 33, 41, 226, 241, 96, 68, 37, 167, 218, 100, 148, 243, 95, 196, 91, 229, 24, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}, + {226, 160, 48, 87, 135, 250, 18, 168, 35, 224, 242, 183, 99, 28, 196, 27, 59, 168, 130, 139, 51, 33, 202, 129, 17, 17, 250, 117, 205, 58, 163, 187, 90, 206, 3}}, + Path: []byte{4, 0, 5, 7, 8, 7, 15, 10, 1, 2, 10, 8, 2, 3, 14, 0, 15, 2, 11, 7, 6, 3, 1, 12, 12, 4, 1, 11, 3, 11, 10, 8, 8, 2, 8, 11, 3, 3, 2, 1, 12, 10, 8, 1, 1, 1, 1, 1, 15, 10, 7, 5, 12, 13, 3, 10, 10, 3, 11, 11, 5, 10, 12, 14, 16}, + }, + }, + }, + bankLeafKey: { + Key: bankLeafKey.Bytes(), + Value: bankAccount3, + Proof: [][]byte{{248, 177, 160, 101, 223, 138, 81, 34, 40, 229, 170, 198, 188, 136, 99, 7, 55, 33, 112, 160, 111, 181, 131, 167, 201, 131, 24, 201, 211, 177, 30, 159, 229, 246, 6, 128, 128, 128, 128, 160, 179, 86, 53, 29, 96, 188, 152, 148, 207, 31, 29, 108, 182, 140, 129, 95, 1, 49, 213, 15, 29, 168, 60, 64, 35, 160, 158, 200, 85, 207, 255, 145, 160, 32, 135, 108, 213, 150, 150, 110, 44, 170, 65, 75, 154, 74, 249, 94, 65, 74, 107, 100, 115, 39, 5, 3, 26, 22, 238, 138, 114, 254, 21, 6, 171, 128, 128, 128, 128, 128, 160, 4, 228, 121, 222, 255, 218, 60, 247, 15, 0, 34, 198, 28, 229, 180, 129, 109, 157, 68, 181, 248, 229, 200, 123, 29, 81, 145, 114, 90, 209, 205, 210, 128, 160, 255, 115, 147, 190, 57, 135, 174, 188, 86, 51, 227, 70, 22, 253, 237, 49, 24, 19, 149, 199, 142, 195, 186, 244, 70, 51, 138, 0, 146, 148, 117, 60, 128, 128}, + {248, 109, 160, 48, 191, 73, 244, 64, 161, 205, 5, 39, 228, 208, 110, 39, 101, 101, 76, 15, 86, 69, 34, 87, 81, 109, 121, 58, 155, 141, 96, 77, 207, 223, 42, 184, 74, 248, 72, 3, 132, 5, 245, 182, 8, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, + Path: []byte{0, 0, 11, 15, 4, 9, 15, 4, 4, 0, 10, 1, 12, 13, 0, 5, 2, 7, 14, 4, 13, 0, 6, 14, 2, 7, 6, 5, 6, 5, 4, 12, 0, 15, 5, 6, 4, 5, 2, 2, 5, 7, 5, 1, 6, 13, 7, 9, 3, 10, 9, 11, 8, 13, 6, 0, 4, 13, 12, 15, 13, 15, 2, 10, 16}, + Storage: []statediff.StorageDiff{}, + }, + }, + }, + }, + } + + for _, test := range tests { + arguments := test.startingArguments + diff, err := builder.BuildStateDiff(arguments.oldStateRoot, arguments.newStateRoot, arguments.blockNumber, arguments.blockHash) + if err != nil { + t.Error(err) + } + fields := []string{"BlockNumber", "BlockHash", "DeletedAccounts", "UpdatedAccounts", "CreatedAccounts"} + + for _, field := range fields { + reflectionOfDiff := reflect.ValueOf(diff) + diffValue := reflect.Indirect(reflectionOfDiff).FieldByName(field) + + reflectionOfExpected := reflect.ValueOf(test.expected) + expectedValue := reflect.Indirect(reflectionOfExpected).FieldByName(field) + + diffValueInterface := diffValue.Interface() + expectedValueInterface := expectedValue.Interface() + + if !equals(diffValueInterface, expectedValueInterface) { + t.Logf("Test failed: %s", test.name) + t.Errorf("field: %+v\nactual: %+v\nexpected: %+v", field, diffValueInterface, expectedValueInterface) + } + } + } +} + +func equals(actual, expected interface{}) (success bool) { + if actualByteSlice, ok := actual.([]byte); ok { + if expectedByteSlice, ok := expected.([]byte); ok { + return bytes.Equal(actualByteSlice, expectedByteSlice) + } + } + + return reflect.DeepEqual(actual, expected) +} + +// makeChain creates a chain of n blocks starting at and including parent. +// the returned hash chain is ordered head->parent. In addition, every 3rd block +// contains a transaction and every 5th an uncle to allow testing correct block +// reassembly. +func makeChain(n int, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block, *core.BlockChain) { + blocks, _ := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testdb, n, testChainGen) + headers := make([]*types.Header, len(blocks)) + for i, block := range blocks { + headers[i] = block.Header() + } + chain, _ := core.NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil) + + hashes := make([]common.Hash, n+1) + hashes[len(hashes)-1] = parent.Hash() + blockm := make(map[common.Hash]*types.Block, n+1) + blockm[parent.Hash()] = parent + for i, b := range blocks { + hashes[len(hashes)-i-2] = b.Hash() + blockm[b.Hash()] = b + } + return hashes, blockm, chain +} + +func testChainGen(i int, block *core.BlockGen) { + signer := types.HomesteadSigner{} + switch i { + case 0: + // In block 1, the test bank sends account #1 some ether. + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), account1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) + block.AddTx(tx) + case 1: + // In block 2, the test bank sends some more ether to account #1. + // account1Addr passes it on to account #2. + // account1Addr creates a test contract. + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), account1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey) + nonce := block.TxNonce(account1Addr) + tx2, _ := types.SignTx(types.NewTransaction(nonce, account2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, account1Key) + nonce++ + tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, big.NewInt(0), contractCode), signer, account1Key) + contractAddr = crypto.CreateAddress(account1Addr, nonce) //0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476592 + block.AddTx(tx1) + block.AddTx(tx2) + block.AddTx(tx3) + case 2: + // Block 3 is empty but was mined by account #2. + block.SetCoinbase(account2Addr) + //get function: 60cd2685 + //put function: c16431b9 + data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), contractAddr, big.NewInt(0), 100000, nil, data), signer, testBankKey) + block.AddTx(tx) + } +} + +/* +contract test { + + uint256[100] data; + + constructor() public { + data = [1]; + } + + function Put(uint256 addr, uint256 value) { + data[addr] = value; + } + + function Get(uint256 addr) constant returns (uint256 value) { + return data[addr]; + } +} +*/ diff --git a/statediff/builder/helpers.go b/statediff/helpers.go similarity index 96% rename from statediff/builder/helpers.go rename to statediff/helpers.go index 339454776..e1a989144 100644 --- a/statediff/builder/helpers.go +++ b/statediff/helpers.go @@ -17,7 +17,7 @@ // Contains a batch of utility type declarations used by the tests. As the node // operates on unique types, a lot of them are needed to check various features. -package builder +package statediff import ( "sort" @@ -73,7 +73,10 @@ func findIntersection(a, b []string) []string { } func pathToStr(it trie.NodeIterator) string { - path := it.Path() + return BytesToNiblePath(it.Path()) +} + +func BytesToNiblePath(path []byte) string { if hasTerm(path) { path = path[:len(path)-1] } diff --git a/statediff/service/service.go b/statediff/service.go similarity index 63% rename from statediff/service/service.go rename to statediff/service.go index 1dc68dcb9..0609861d5 100644 --- a/statediff/service/service.go +++ b/statediff/service.go @@ -1,8 +1,25 @@ -package service +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package statediff import ( "bytes" "fmt" + "github.com/ethereum/go-ethereum/node" "sync" "github.com/ethereum/go-ethereum/common" @@ -13,7 +30,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/statediff/builder" ) type BlockChain interface { @@ -22,42 +38,54 @@ type BlockChain interface { AddToStateDiffProcessedCollection(hash common.Hash) } -type StateDiffService struct { +type SDS interface { + // APIs(), Protocols(), Start() and Stop() + node.Service + // Main event loop for processing state diffs + Loop(chainEventCh chan core.ChainEvent) + // Method to subscribe to receive state diff processing output + Subscribe(id rpc.ID, sub chan<- StateDiffPayload, quitChan chan<- bool) + // Method to unsubscribe from state diff processing + Unsubscribe(id rpc.ID) error +} + +type StateDiffingService struct { sync.Mutex - Builder builder.Builder + Builder Builder BlockChain BlockChain QuitChan chan bool Subscriptions Subscriptions } -type Subscriptions map[rpc.ID]subscription +type Subscriptions map[rpc.ID]Subscription -type subscription struct { +type Subscription struct { PayloadChan chan<- StateDiffPayload QuitChan chan<- bool } type StateDiffPayload struct { - BlockRlp []byte `json:"block"` - StateDiff builder.StateDiff `json:"state_diff"` - Err error `json:"error"` + BlockRlp []byte `json:"block"` + StateDiff StateDiff `json:"state_diff"` + Err error `json:"error"` } -func NewStateDiffService(db ethdb.Database, blockChain *core.BlockChain) (*StateDiffService, error) { - return &StateDiffService{ +func NewStateDiffService(db ethdb.Database, blockChain *core.BlockChain) (*StateDiffingService, error) { + return &StateDiffingService{ + Mutex: sync.Mutex{}, BlockChain: blockChain, - Builder: builder.NewBuilder(db, blockChain), + Builder: NewBuilder(db, blockChain), QuitChan: make(chan bool), Subscriptions: make(Subscriptions), }, nil } -func (sds *StateDiffService) Protocols() []p2p.Protocol { +func (sds *StateDiffingService) Protocols() []p2p.Protocol { return []p2p.Protocol{} } // APIs returns the RPC descriptors the Whisper implementation offers -func (sds *StateDiffService) APIs() []rpc.API { +func (sds *StateDiffingService) APIs() []rpc.API { return []rpc.API{ { Namespace: APIName, @@ -68,7 +96,7 @@ func (sds *StateDiffService) APIs() []rpc.API { } } -func (sds *StateDiffService) Loop(chainEventCh chan core.ChainEvent) { +func (sds *StateDiffingService) Loop(chainEventCh chan core.ChainEvent) { chainEventSub := sds.BlockChain.SubscribeChainEvent(chainEventCh) defer chainEventSub.Unsubscribe() @@ -118,30 +146,30 @@ HandleBlockChLoop: blockRlp := rlpBuff.Bytes() payload := StateDiffPayload{ BlockRlp: blockRlp, - StateDiff: *stateDiff, + StateDiff: stateDiff, Err: err, } // If we have any websocket subscription listening in, send the data to them - sds.Send(payload) + sds.send(payload) case <-sds.QuitChan: log.Debug("Quitting the statediff block channel") - sds.Close() + sds.close() return } } } -func (sds *StateDiffService) Subscribe(id rpc.ID, sub chan<- StateDiffPayload, quitChan chan<- bool) { +func (sds *StateDiffingService) Subscribe(id rpc.ID, sub chan<- StateDiffPayload, quitChan chan<- bool) { log.Info("Subscribing to the statediff service") sds.Lock() - sds.Subscriptions[id] = subscription{ + sds.Subscriptions[id] = Subscription{ PayloadChan: sub, QuitChan: quitChan, } sds.Unlock() } -func (sds *StateDiffService) Unsubscribe(id rpc.ID) error { +func (sds *StateDiffingService) Unsubscribe(id rpc.ID) error { log.Info("Unsubscribing from the statediff service") sds.Lock() _, ok := sds.Subscriptions[id] @@ -153,7 +181,7 @@ func (sds *StateDiffService) Unsubscribe(id rpc.ID) error { return nil } -func (sds *StateDiffService) Send(payload StateDiffPayload) { +func (sds *StateDiffingService) send(payload StateDiffPayload) { sds.Lock() for id, sub := range sds.Subscriptions { select { @@ -166,7 +194,7 @@ func (sds *StateDiffService) Send(payload StateDiffPayload) { sds.Unlock() } -func (sds *StateDiffService) Close() { +func (sds *StateDiffingService) close() { sds.Lock() for id, sub := range sds.Subscriptions { select { @@ -180,7 +208,7 @@ func (sds *StateDiffService) Close() { sds.Unlock() } -func (sds *StateDiffService) Start(server *p2p.Server) error { +func (sds *StateDiffingService) Start(server *p2p.Server) error { log.Info("Starting statediff service") chainEventCh := make(chan core.ChainEvent, 10) @@ -189,7 +217,7 @@ func (sds *StateDiffService) Start(server *p2p.Server) error { return nil } -func (sds *StateDiffService) Stop() error { +func (sds *StateDiffingService) Stop() error { log.Info("Stopping statediff service") close(sds.QuitChan) return nil diff --git a/statediff/service/service_test.go b/statediff/service/service_test.go deleted file mode 100644 index 55219e337..000000000 --- a/statediff/service/service_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package service_test - -import ( - "math/big" - "math/rand" - "reflect" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - s "github.com/ethereum/go-ethereum/statediff/service" - "github.com/ethereum/go-ethereum/statediff/testhelpers/mocks" -) - -func TestServiceLoop(t *testing.T) { - testErrorInChainEventLoop(t) - testErrorInBlockLoop(t) -} - -var ( - eventsChannel = make(chan core.ChainEvent, 1) - - parentHeader1 = types.Header{Number: big.NewInt(rand.Int63())} - parentHeader2 = types.Header{Number: big.NewInt(rand.Int63())} - - parentBlock1 = types.NewBlock(&parentHeader1, nil, nil, nil) - parentBlock2 = types.NewBlock(&parentHeader2, nil, nil, nil) - - parentHash1 = parentBlock1.Hash() - parentHash2 = parentBlock2.Hash() - - header1 = types.Header{ParentHash: parentHash1} - header2 = types.Header{ParentHash: parentHash2} - header3 = types.Header{ParentHash: common.HexToHash("parent hash")} - - block1 = types.NewBlock(&header1, nil, nil, nil) - block2 = types.NewBlock(&header2, nil, nil, nil) - block3 = types.NewBlock(&header3, nil, nil, nil) - - event1 = core.ChainEvent{Block: block1} - event2 = core.ChainEvent{Block: block2} - event3 = core.ChainEvent{Block: block3} -) - -func testErrorInChainEventLoop(t *testing.T) { - //the first chain event causes and error (in blockchain mock) - extractor := mocks.Extractor{} - - blockChain := mocks.BlockChain{} - service := s.StateDiffService{ - Builder: nil, - BlockChain: &blockChain, - } - - blockChain.SetParentBlocksToReturn([]*types.Block{parentBlock1, parentBlock2}) - blockChain.SetChainEvents([]core.ChainEvent{event1, event2, event3}) - service.Loop(eventsChannel) - - //parent and current blocks are passed to the extractor - expectedCurrentBlocks := []types.Block{*block1, *block2} - if !reflect.DeepEqual(extractor.CurrentBlocks, expectedCurrentBlocks) { - t.Error("Test failure:", t.Name()) - t.Logf("Actual does not equal expected.\nactual:%+v\nexpected: %+v", extractor.CurrentBlocks, expectedCurrentBlocks) - } - expectedParentBlocks := []types.Block{*parentBlock1, *parentBlock2} - if !reflect.DeepEqual(extractor.ParentBlocks, expectedParentBlocks) { - t.Error("Test failure:", t.Name()) - t.Logf("Actual does not equal expected.\nactual:%+v\nexpected: %+v", extractor.CurrentBlocks, expectedParentBlocks) - } - - //look up the parent block from its hash - expectedHashes := []common.Hash{block1.ParentHash(), block2.ParentHash()} - if !reflect.DeepEqual(blockChain.ParentHashesLookedUp, expectedHashes) { - t.Error("Test failure:", t.Name()) - t.Logf("Actual does not equal expected.\nactual:%+v\nexpected: %+v", blockChain.ParentHashesLookedUp, expectedHashes) - } -} - -func testErrorInBlockLoop(t *testing.T) { - //second block's parent block can't be found - extractor := mocks.Extractor{} - - blockChain := mocks.BlockChain{} - service := s.StateDiffService{ - Builder: nil, - BlockChain: &blockChain, - } - - blockChain.SetParentBlocksToReturn([]*types.Block{parentBlock1, nil}) - blockChain.SetChainEvents([]core.ChainEvent{event1, event2}) - service.Loop(eventsChannel) - - //only the first current block (and it's parent) are passed to the extractor - expectedCurrentBlocks := []types.Block{*block1} - if !reflect.DeepEqual(extractor.CurrentBlocks, expectedCurrentBlocks) { - t.Error("Test failure:", t.Name()) - t.Logf("Actual does not equal expected.\nactual:%+v\nexpected: %+v", extractor.CurrentBlocks, expectedCurrentBlocks) - } - expectedParentBlocks := []types.Block{*parentBlock1} - if !reflect.DeepEqual(extractor.ParentBlocks, expectedParentBlocks) { - t.Error("Test failure:", t.Name()) - t.Logf("Actual does not equal expected.\nactual:%+v\nexpected: %+v", extractor.CurrentBlocks, expectedParentBlocks) - } -} diff --git a/statediff/service_test.go b/statediff/service_test.go new file mode 100644 index 000000000..2221e2e66 --- /dev/null +++ b/statediff/service_test.go @@ -0,0 +1,129 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package statediff_test + +import ( + "bytes" + "math/big" + "math/rand" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/statediff" + "github.com/ethereum/go-ethereum/statediff/testhelpers/mocks" +) + +func TestServiceLoop(t *testing.T) { + testErrorInChainEventLoop(t) + testErrorInBlockLoop(t) +} + +var ( + eventsChannel = make(chan core.ChainEvent, 1) + + parentRoot1 = common.HexToHash("0x01") + parentRoot2 = common.HexToHash("0x02") + parentHeader1 = types.Header{Number: big.NewInt(rand.Int63()), Root: parentRoot1} + parentHeader2 = types.Header{Number: big.NewInt(rand.Int63()), Root: parentRoot2} + + parentBlock1 = types.NewBlock(&parentHeader1, nil, nil, nil) + parentBlock2 = types.NewBlock(&parentHeader2, nil, nil, nil) + + parentHash1 = parentBlock1.Hash() + parentHash2 = parentBlock2.Hash() + + testRoot1 = common.HexToHash("0x03") + testRoot2 = common.HexToHash("0x04") + testRoot3 = common.HexToHash("0x04") + header1 = types.Header{ParentHash: parentHash1, Root: testRoot1} + header2 = types.Header{ParentHash: parentHash2, Root: testRoot2} + header3 = types.Header{ParentHash: common.HexToHash("parent hash"), Root: testRoot3} + + testBlock1 = types.NewBlock(&header1, nil, nil, nil) + testBlock2 = types.NewBlock(&header2, nil, nil, nil) + testBlock3 = types.NewBlock(&header3, nil, nil, nil) + + event1 = core.ChainEvent{Block: testBlock1} + event2 = core.ChainEvent{Block: testBlock2} + event3 = core.ChainEvent{Block: testBlock3} +) + +func testErrorInChainEventLoop(t *testing.T) { + //the first chain event causes and error (in blockchain mock) + builder := mocks.Builder{} + blockChain := mocks.BlockChain{} + service := statediff.StateDiffingService{ + Builder: &builder, + BlockChain: &blockChain, + QuitChan: make(chan bool), + Subscriptions: make(statediff.Subscriptions), + } + testRoot2 = common.HexToHash("0xTestRoot2") + blockChain.SetParentBlocksToReturn([]*types.Block{parentBlock1, parentBlock2}) + blockChain.SetChainEvents([]core.ChainEvent{event1, event2, event3}) + service.Loop(eventsChannel) + if !reflect.DeepEqual(builder.BlockHash, testBlock2.Hash()) { + t.Error("Test failure:", t.Name()) + t.Logf("Actual does not equal expected.\nactual:%+v\nexpected: %+v", builder.BlockHash, testBlock2.Hash()) + } + if !bytes.Equal(builder.OldStateRoot.Bytes(), parentBlock2.Root().Bytes()) { + t.Error("Test failure:", t.Name()) + t.Logf("Actual does not equal expected.\nactual:%+v\nexpected: %+v", builder.OldStateRoot, parentBlock2.Root()) + } + if !bytes.Equal(builder.NewStateRoot.Bytes(), testBlock2.Root().Bytes()) { + t.Error("Test failure:", t.Name()) + t.Logf("Actual does not equal expected.\nactual:%+v\nexpected: %+v", builder.NewStateRoot, testBlock2.Root()) + } + //look up the parent block from its hash + expectedHashes := []common.Hash{testBlock1.ParentHash(), testBlock2.ParentHash()} + if !reflect.DeepEqual(blockChain.ParentHashesLookedUp, expectedHashes) { + t.Error("Test failure:", t.Name()) + t.Logf("Actual does not equal expected.\nactual:%+v\nexpected: %+v", blockChain.ParentHashesLookedUp, expectedHashes) + } +} + +func testErrorInBlockLoop(t *testing.T) { + //second block's parent block can't be found + builder := mocks.Builder{} + blockChain := mocks.BlockChain{} + service := statediff.StateDiffingService{ + Builder: &builder, + BlockChain: &blockChain, + QuitChan: make(chan bool), + Subscriptions: make(statediff.Subscriptions), + } + + blockChain.SetParentBlocksToReturn([]*types.Block{parentBlock1, nil}) + blockChain.SetChainEvents([]core.ChainEvent{event1, event2}) + service.Loop(eventsChannel) + + if !bytes.Equal(builder.BlockHash.Bytes(), testBlock1.Hash().Bytes()) { + t.Error("Test failure:", t.Name()) + t.Logf("Actual does not equal expected.\nactual:%+v\nexpected: %+v", builder.BlockHash, testBlock1.Hash()) + } + if !bytes.Equal(builder.OldStateRoot.Bytes(), parentBlock1.Root().Bytes()) { + t.Error("Test failure:", t.Name()) + t.Logf("Actual does not equal expected.\nactual:%+v\nexpected: %+v", builder.OldStateRoot, parentBlock1.Root()) + } + if !bytes.Equal(builder.NewStateRoot.Bytes(), testBlock1.Root().Bytes()) { + t.Error("Test failure:", t.Name()) + t.Logf("Actual does not equal expected.\nactual:%+v\nexpected: %+v", builder.NewStateRoot, testBlock1.Root()) + } +} diff --git a/statediff/testhelpers/helpers.go b/statediff/testhelpers/helpers.go index 0bfc53e5e..86b851f89 100644 --- a/statediff/testhelpers/helpers.go +++ b/statediff/testhelpers/helpers.go @@ -1,3 +1,19 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + package testhelpers var ErrorFormatString = "Error: %s, %+v" diff --git a/statediff/testhelpers/mocks/blockchain.go b/statediff/testhelpers/mocks/blockchain.go index 7b0d74c59..ed16c3471 100644 --- a/statediff/testhelpers/mocks/blockchain.go +++ b/statediff/testhelpers/mocks/blockchain.go @@ -1,3 +1,19 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + package mocks import ( diff --git a/statediff/testhelpers/mocks/builder.go b/statediff/testhelpers/mocks/builder.go index ae9ff5ced..d3252fe4b 100644 --- a/statediff/testhelpers/mocks/builder.go +++ b/statediff/testhelpers/mocks/builder.go @@ -1,8 +1,24 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + package mocks import ( "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/statediff/builder" + "github.com/ethereum/go-ethereum/statediff" ) type Builder struct { @@ -10,11 +26,11 @@ type Builder struct { NewStateRoot common.Hash BlockNumber int64 BlockHash common.Hash - stateDiff *builder.StateDiff + stateDiff statediff.StateDiff builderError error } -func (builder *Builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, blockNumber int64, blockHash common.Hash) (*builder.StateDiff, error) { +func (builder *Builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, blockNumber int64, blockHash common.Hash) (statediff.StateDiff, error) { builder.OldStateRoot = oldStateRoot builder.NewStateRoot = newStateRoot builder.BlockNumber = blockNumber @@ -23,7 +39,7 @@ func (builder *Builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, b return builder.stateDiff, builder.builderError } -func (builder *Builder) SetStateDiffToBuild(stateDiff *builder.StateDiff) { +func (builder *Builder) SetStateDiffToBuild(stateDiff statediff.StateDiff) { builder.stateDiff = stateDiff } diff --git a/statediff/testhelpers/mocks/error.go b/statediff/testhelpers/mocks/error.go index 7c40452ae..dafb0371e 100644 --- a/statediff/testhelpers/mocks/error.go +++ b/statediff/testhelpers/mocks/error.go @@ -1,3 +1,19 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + package mocks import "errors" diff --git a/statediff/testhelpers/mocks/extractor.go b/statediff/testhelpers/mocks/extractor.go deleted file mode 100644 index 067497646..000000000 --- a/statediff/testhelpers/mocks/extractor.go +++ /dev/null @@ -1,20 +0,0 @@ -package mocks - -import "github.com/ethereum/go-ethereum/core/types" - -type Extractor struct { - ParentBlocks []types.Block - CurrentBlocks []types.Block - extractError error -} - -func (me *Extractor) ExtractStateDiff(parent, current types.Block) (string, error) { - me.ParentBlocks = append(me.ParentBlocks, parent) - me.CurrentBlocks = append(me.CurrentBlocks, current) - - return "", me.extractError -} - -func (me *Extractor) SetExtractError(err error) { - me.extractError = err -} diff --git a/statediff/testhelpers/mocks/publisher.go b/statediff/testhelpers/mocks/publisher.go index efbe4e2ab..01f4c5997 100644 --- a/statediff/testhelpers/mocks/publisher.go +++ b/statediff/testhelpers/mocks/publisher.go @@ -1,13 +1,29 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + package mocks -import "github.com/ethereum/go-ethereum/statediff/builder" +import "github.com/ethereum/go-ethereum/statediff" type Publisher struct { - StateDiff *builder.StateDiff + StateDiff *statediff.StateDiff publisherError error } -func (publisher *Publisher) PublishStateDiff(sd *builder.StateDiff) (string, error) { +func (publisher *Publisher) PublishStateDiff(sd *statediff.StateDiff) (string, error) { publisher.StateDiff = sd return "", publisher.publisherError } diff --git a/statediff/testhelpers/mocks/service.go b/statediff/testhelpers/mocks/service.go new file mode 100644 index 000000000..d948091a1 --- /dev/null +++ b/statediff/testhelpers/mocks/service.go @@ -0,0 +1,162 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package mocks + +import ( + "bytes" + "errors" + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/statediff" +) + +type MockStateDiffService struct { + sync.Mutex + Builder statediff.Builder + ReturnProtocol []p2p.Protocol + ReturnAPIs []rpc.API + MockBlockChan chan *types.Block + MockParentBlockChan chan *types.Block + QuitChan chan bool + Subscriptions statediff.Subscriptions +} + +func (sds *MockStateDiffService) Protocols() []p2p.Protocol { + return []p2p.Protocol{} +} + +// APIs returns the RPC descriptors the Whisper implementation offers +func (sds *MockStateDiffService) APIs() []rpc.API { + return []rpc.API{ + { + Namespace: statediff.APIName, + Version: statediff.APIVersion, + Service: statediff.NewPublicStateDiffAPI(sds), + Public: true, + }, + } +} + +func (sds *MockStateDiffService) Loop(chan core.ChainEvent) { + //loop through chain events until no more +HandleBlockChLoop: + for { + select { + case block := <-sds.MockBlockChan: + currentBlock := block + parentBlock := <-sds.MockParentBlockChan + parentHash := parentBlock.Hash() + if parentBlock == nil { + log.Error("Parent block is nil, skipping this block", + "parent block hash", parentHash.String(), + "current block number", currentBlock.Number()) + break HandleBlockChLoop + } + + stateDiff, err := sds.Builder.BuildStateDiff(parentBlock.Root(), currentBlock.Root(), currentBlock.Number().Int64(), currentBlock.Hash()) + if err != nil { + log.Error("Error building statediff", "block number", currentBlock.Number(), "error", err) + } + rlpBuff := new(bytes.Buffer) + currentBlock.EncodeRLP(rlpBuff) + blockRlp := rlpBuff.Bytes() + payload := statediff.StateDiffPayload{ + BlockRlp: blockRlp, + StateDiff: stateDiff, + Err: err, + } + // If we have any websocket subscription listening in, send the data to them + sds.send(payload) + case <-sds.QuitChan: + log.Debug("Quitting the statediff block channel") + sds.close() + return + } + } +} + +func (sds *MockStateDiffService) Subscribe(id rpc.ID, sub chan<- statediff.StateDiffPayload, quitChan chan<- bool) { + log.Info("Subscribing to the statediff service") + sds.Lock() + sds.Subscriptions[id] = statediff.Subscription{ + PayloadChan: sub, + QuitChan: quitChan, + } + sds.Unlock() +} + +func (sds *MockStateDiffService) Unsubscribe(id rpc.ID) error { + log.Info("Unsubscribing from the statediff service") + sds.Lock() + _, ok := sds.Subscriptions[id] + if !ok { + return fmt.Errorf("cannot unsubscribe; subscription for id %s does not exist", id) + } + delete(sds.Subscriptions, id) + sds.Unlock() + return nil +} + +func (sds *MockStateDiffService) send(payload statediff.StateDiffPayload) { + sds.Lock() + for id, sub := range sds.Subscriptions { + select { + case sub.PayloadChan <- payload: + log.Info("sending state diff payload to subscription %s", id) + default: + log.Info("unable to send payload to subscription %s; channel has no receiver", id) + } + } + sds.Unlock() +} + +func (sds *MockStateDiffService) close() { + sds.Lock() + for id, sub := range sds.Subscriptions { + select { + case sub.QuitChan <- true: + delete(sds.Subscriptions, id) + log.Info("closing subscription %s", id) + default: + log.Info("unable to close subscription %s; channel has no receiver", id) + } + } + sds.Unlock() +} + +func (sds *MockStateDiffService) Start(server *p2p.Server) error { + log.Info("Starting statediff service") + if sds.MockParentBlockChan == nil || sds.MockBlockChan == nil { + return errors.New("mock StateDiffingService requires preconfiguration with a MockParentBlockChan and MockBlockChan") + } + chainEventCh := make(chan core.ChainEvent, 10) + go sds.Loop(chainEventCh) + + return nil +} + +func (sds *MockStateDiffService) Stop() error { + log.Info("Stopping statediff service") + close(sds.QuitChan) + return nil +} diff --git a/statediff/testhelpers/test_data.go b/statediff/testhelpers/test_data.go index 831bed218..54c7b14ce 100644 --- a/statediff/testhelpers/test_data.go +++ b/statediff/testhelpers/test_data.go @@ -1,12 +1,30 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + package testhelpers import ( + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/rlp" "math/big" "math/rand" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/statediff/builder" + "github.com/ethereum/go-ethereum/statediff" ) func AddressToLeafKey(address common.Address) common.Hash { @@ -16,56 +34,57 @@ func AddressToLeafKey(address common.Address) common.Hash { var ( BlockNumber = rand.Int63() BlockHash = "0xfa40fbe2d98d98b3363a778d52f2bcd29d6790b9b3f3cab2b167fd12d3550f73" - CodeHash = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + CodeHash = common.Hex2Bytes("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470") NewNonceValue = rand.Uint64() NewBalanceValue = rand.Int63() - ContractRoot = "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" - StoragePath = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" - StorageKey = "0000000000000000000000000000000000000000000000000000000000000001" - StorageValue = "0x03" - storage = map[string]builder.DiffStorage{StoragePath: { - Key: &StorageKey, - Value: &StorageValue, + ContractRoot = common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + StoragePath = common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes() + StorageKey = common.HexToHash("0000000000000000000000000000000000000000000000000000000000000001").Bytes() + StorageValue = common.Hex2Bytes("0x03") + storage = []statediff.StorageDiff{{ + Key: StorageKey, + Value: StorageValue, + Path: StoragePath, + Proof: [][]byte{}, }} - emptyStorage = map[string]builder.DiffStorage{} + emptyStorage = make([]statediff.StorageDiff, 0) address = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476592") ContractLeafKey = AddressToLeafKey(address) anotherAddress = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476593") AnotherContractLeafKey = AddressToLeafKey(anotherAddress) - CreatedAccountDiffs = builder.AccountDiffsMap{ + testAccount = state.Account{ + Nonce: NewNonceValue, + Balance: big.NewInt(NewBalanceValue), + Root: ContractRoot, + CodeHash: CodeHash, + } + valueBytes, err = rlp.EncodeToBytes(testAccount) + CreatedAccountDiffs = statediff.AccountDiffsMap{ ContractLeafKey: { - Nonce: builder.DiffUint64{Value: &NewNonceValue}, - Balance: builder.DiffBigInt{Value: big.NewInt(NewBalanceValue)}, - ContractRoot: builder.DiffString{Value: &ContractRoot}, - CodeHash: CodeHash, - Storage: storage, + Key: ContractLeafKey.Bytes(), + Value: valueBytes, + Storage: storage, }, AnotherContractLeafKey: { - Nonce: builder.DiffUint64{Value: &NewNonceValue}, - Balance: builder.DiffBigInt{Value: big.NewInt(NewBalanceValue)}, - CodeHash: CodeHash, - ContractRoot: builder.DiffString{Value: &ContractRoot}, - Storage: emptyStorage, + Key: AnotherContractLeafKey.Bytes(), + Value: valueBytes, + Storage: emptyStorage, }, } - UpdatedAccountDiffs = builder.AccountDiffsMap{ContractLeafKey: { - Nonce: builder.DiffUint64{Value: &NewNonceValue}, - Balance: builder.DiffBigInt{Value: big.NewInt(NewBalanceValue)}, - CodeHash: CodeHash, - ContractRoot: builder.DiffString{Value: &ContractRoot}, - Storage: storage, + UpdatedAccountDiffs = statediff.AccountDiffsMap{ContractLeafKey: { + Key: ContractLeafKey.Bytes(), + Value: valueBytes, + Storage: storage, }} - DeletedAccountDiffs = builder.AccountDiffsMap{ContractLeafKey: { - Nonce: builder.DiffUint64{Value: &NewNonceValue}, - Balance: builder.DiffBigInt{Value: big.NewInt(NewBalanceValue)}, - ContractRoot: builder.DiffString{Value: &ContractRoot}, - CodeHash: CodeHash, - Storage: storage, + DeletedAccountDiffs = statediff.AccountDiffsMap{ContractLeafKey: { + Key: ContractLeafKey.Bytes(), + Value: valueBytes, + Storage: storage, }} - TestStateDiff = builder.StateDiff{ + TestStateDiff = statediff.StateDiff{ BlockNumber: BlockNumber, BlockHash: common.HexToHash(BlockHash), CreatedAccounts: CreatedAccountDiffs, diff --git a/statediff/builder/struct.go b/statediff/types.go similarity index 63% rename from statediff/builder/struct.go rename to statediff/types.go index f0fd43b75..91c11335e 100644 --- a/statediff/builder/struct.go +++ b/statediff/types.go @@ -17,23 +17,30 @@ // Contains a batch of utility type declarations used by the tests. As the node // operates on unique types, a lot of them are needed to check various features. -package builder +package statediff import ( "encoding/json" - "math/big" - "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" ) -type AccountDiffsMap map[common.Hash]AccountDiff +type AccountsMap map[common.Hash]*accountWrapper + +type accountWrapper struct { + Account state.Account + RawKey []byte + RawValue []byte + Proof [][]byte + Path []byte +} type StateDiff struct { - BlockNumber int64 `json:"blockNumber" gencodec:"required"` - BlockHash common.Hash `json:"blockHash" gencodec:"required"` - CreatedAccounts AccountDiffsMap `json:"createdAccounts" gencodec:"required"` - DeletedAccounts AccountDiffsMap `json:"deletedAccounts" gencodec:"required"` - UpdatedAccounts AccountDiffsMap `json:"updatedAccounts" gencodec:"required"` + BlockNumber int64 `json:"blockNumber" gencodec:"required"` + BlockHash common.Hash `json:"blockHash" gencodec:"required"` + CreatedAccounts AccountDiffsMap `json:"createdAccounts" gencodec:"required"` + DeletedAccounts AccountDiffsMap `json:"deletedAccounts" gencodec:"required"` + UpdatedAccounts AccountDiffsMap `json:"updatedAccounts" gencodec:"required"` encoded []byte err error @@ -57,26 +64,21 @@ func (sd *StateDiff) Encode() ([]byte, error) { return sd.encoded, sd.err } +type AccountDiffsMap map[common.Hash]AccountDiff + type AccountDiff struct { - Nonce DiffUint64 `json:"nonce" gencodec:"required"` - Balance DiffBigInt `json:"balance" gencodec:"required"` - CodeHash string `json:"codeHash" gencodec:"required"` - ContractRoot DiffString `json:"contractRoot" gencodec:"required"` - Storage map[string]DiffStorage `json:"storage" gencodec:"required"` + Key []byte `json:"key" gencodec:"required"` + Value []byte `json:"value" gencodec:"required"` + Proof [][]byte `json:"proof" gencodec:"required"` + Storage []StorageDiff `json:"storage" gencodec:"required"` + Path []byte `json:"path" gencodec:"required"` } -type DiffStorage struct { - Key *string `json:"key" gencodec:"optional"` - Value *string `json:"value" gencodec:"optional"` -} -type DiffString struct { - Value *string `json:"value" gencodec:"optional"` -} -type DiffUint64 struct { - Value *uint64 `json:"value" gencodec:"optional"` -} -type DiffBigInt struct { - Value *big.Int `json:"value" gencodec:"optional"` +type StorageDiff struct { + Key []byte `json:"key" gencodec:"required"` + Value []byte `json:"value" gencodec:"required"` + Proof [][]byte `json:"proof" gencodec:"required"` + Path []byte `json:"path" gencodec:"required"` } /* -- 2.45.2 From 79c9c5e59823b9b814d828a907e594d28509123b Mon Sep 17 00:00:00 2001 From: Ian Norden Date: Sun, 14 Apr 2019 17:22:15 -0500 Subject: [PATCH 11/11] edit to fix tests after rebase + linting + a semi-mock service test --- cmd/utils/flags.go | 7 +- core/blockchain.go | 14 +-- core/blockchain_test.go | 4 +- eth/backend.go | 10 +- statediff/api.go | 22 ++-- statediff/builder.go | 49 ++++---- statediff/builder_test.go | 119 ++++-------------- statediff/helpers.go | 33 ++--- statediff/service.go | 112 ++++++++++------- statediff/service_test.go | 9 +- statediff/testhelpers/helpers.go | 67 ++++++++++- statediff/testhelpers/mocks/blockchain.go | 30 +++-- statediff/testhelpers/mocks/builder.go | 4 + statediff/testhelpers/mocks/error.go | 21 ---- statediff/testhelpers/mocks/publisher.go | 3 + statediff/testhelpers/mocks/service.go | 43 ++++--- statediff/testhelpers/mocks/service_test.go | 127 ++++++++++++++++++++ statediff/testhelpers/test_data.go | 26 +++- statediff/types.go | 21 ++-- 19 files changed, 446 insertions(+), 275 deletions(-) delete mode 100644 statediff/testhelpers/mocks/error.go create mode 100644 statediff/testhelpers/mocks/service_test.go diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 7815244b2..e4bb18a89 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -29,6 +29,8 @@ import ( "strings" "time" + cli "gopkg.in/urfave/cli.v1" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" @@ -56,9 +58,8 @@ import ( "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/statediff/service" + "github.com/ethereum/go-ethereum/statediff" whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" - cli "gopkg.in/urfave/cli.v1" ) var ( @@ -1532,7 +1533,7 @@ func RegisterStateDiffService(stack *node.Node, ctx *cli.Context) { ctx.Service(ðServ) chainDb := ethServ.ChainDb() blockChain := ethServ.BlockChain() - return service.NewStateDiffService(chainDb, blockChain) + return statediff.NewStateDiffService(chainDb, blockChain) }); err != nil { Fatalf("Failed to register State Diff Service", err) } diff --git a/core/blockchain.go b/core/blockchain.go index 5eebed9f4..50530fa42 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -90,12 +90,12 @@ const ( // CacheConfig contains the configuration values for the trie caching/pruning // that's resident in a blockchain. type CacheConfig struct { - TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory - TrieCleanNoPrefetch bool // Whether to disable heuristic state prefetching for followup blocks - TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk - TrieDirtyDisabled bool // Whether to disable trie write caching and GC altogether (archive node) - TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk - ProcessStateDiffs bool // Whether statediffs processing should be taken into a account before a trie is pruned + TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory + TrieCleanNoPrefetch bool // Whether to disable heuristic state prefetching for followup blocks + TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk + TrieDirtyDisabled bool // Whether to disable trie write caching and GC altogether (archive node) + TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk + ProcessingStateDiffs bool // Whether statediffs processing should be taken into a account before a trie is pruned } // BlockChain represents the canonical chain given a database with a genesis @@ -1025,7 +1025,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. bc.triegc.Push(root, number) break } - if bc.cacheConfig.ProcessStateDiffs { + if bc.cacheConfig.ProcessingStateDiffs { if !bc.allowedRootToBeDereferenced(root.(common.Hash)) { bc.triegc.Push(root, number) break diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 682bab8d9..4af139a85 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -1816,13 +1816,13 @@ func TestProcessingStateDiffs(t *testing.T) { defaultTrieDirtyCache := 256 defaultTrieTimeout := 60 * time.Minute cacheConfig := &CacheConfig{ - Disabled: false, + TrieDirtyDisabled: false, TrieCleanLimit: defaultTrieCleanCache, TrieDirtyLimit: defaultTrieDirtyCache, TrieTimeLimit: defaultTrieTimeout, ProcessingStateDiffs: true, } - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() genesis := new(Genesis).MustCommit(db) numberOfBlocks := triesInMemory engine := ethash.NewFaker() diff --git a/eth/backend.go b/eth/backend.go index 130cb44ef..1b5258c3d 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -166,11 +166,11 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { EVMInterpreter: config.EVMInterpreter, } cacheConfig = &core.CacheConfig{ - TrieCleanLimit: config.TrieCleanCache, - TrieCleanNoPrefetch: config.NoPrefetch, - TrieDirtyLimit: config.TrieDirtyCache, - TrieDirtyDisabled: config.NoPruning, - TrieTimeLimit: config.TrieTimeout, + TrieCleanLimit: config.TrieCleanCache, + TrieCleanNoPrefetch: config.NoPrefetch, + TrieDirtyLimit: config.TrieDirtyCache, + TrieDirtyDisabled: config.NoPruning, + TrieTimeLimit: config.TrieTimeout, ProcessingStateDiffs: config.StateDiff, } ) diff --git a/statediff/api.go b/statediff/api.go index c4267c1f5..19ad7cfc9 100644 --- a/statediff/api.go +++ b/statediff/api.go @@ -25,21 +25,24 @@ import ( "github.com/ethereum/go-ethereum/rpc" ) +// APIName is the namespace used for the state diffing service API const APIName = "statediff" + +// APIVersion is the version of the state diffing service API const APIVersion = "0.0.1" // PublicStateDiffAPI provides the a websocket service // that can be used to stream out state diffs as they // are produced by a full node type PublicStateDiffAPI struct { - sds SDS + sds IService mu sync.Mutex lastUsed map[string]time.Time // keeps track when a filter was polled for the last time. } // NewPublicStateDiffAPI create a new state diff websocket streaming service. -func NewPublicStateDiffAPI(sds SDS) *PublicStateDiffAPI { +func NewPublicStateDiffAPI(sds IService) *PublicStateDiffAPI { return &PublicStateDiffAPI{ sds: sds, lastUsed: make(map[string]time.Time), @@ -47,8 +50,8 @@ func NewPublicStateDiffAPI(sds SDS) *PublicStateDiffAPI { } } -// StreamData set up a subscription that fires off state-diffs when they are created -func (api *PublicStateDiffAPI) StreamStateDiffs(ctx context.Context) (*rpc.Subscription, error) { +// Subscribe is the public method to setup a subscription that fires off state-diff payloads as they are created +func (api *PublicStateDiffAPI) Subscribe(ctx context.Context) (*rpc.Subscription, error) { // ensure that the RPC connection supports subscriptions notifier, supported := rpc.NotifierFromContext(ctx) if !supported { @@ -57,29 +60,28 @@ func (api *PublicStateDiffAPI) StreamStateDiffs(ctx context.Context) (*rpc.Subsc // create subscription and start waiting for statediff events rpcSub := notifier.CreateSubscription() - id := rpcSub.ID go func() { // subscribe to events from the state diff service - payloadChannel := make(chan StateDiffPayload) + payloadChannel := make(chan Payload) quitChan := make(chan bool) - api.sds.Subscribe(id, payloadChannel, quitChan) + api.sds.Subscribe(rpcSub.ID, payloadChannel, quitChan) // loop and await state diff payloads and relay them to the subscriber with then notifier for { select { case packet := <-payloadChannel: - if err := notifier.Notify(id, packet); err != nil { + if err := notifier.Notify(rpcSub.ID, packet); err != nil { log.Error("Failed to send state diff packet", "err", err) } case <-rpcSub.Err(): - err := api.sds.Unsubscribe(id) + err := api.sds.Unsubscribe(rpcSub.ID) if err != nil { log.Error("Failed to unsubscribe from the state diff service", err) } return case <-notifier.Closed(): - err := api.sds.Unsubscribe(id) + err := api.sds.Unsubscribe(rpcSub.ID) if err != nil { log.Error("Failed to unsubscribe from the state diff service", err) } diff --git a/statediff/builder.go b/statediff/builder.go index 0a37fafe6..4e8ab0e21 100644 --- a/statediff/builder.go +++ b/statediff/builder.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/trie" ) +// Builder interface exposes the method for building a state diff between two blocks type Builder interface { BuildStateDiff(oldStateRoot, newStateRoot common.Hash, blockNumber int64, blockHash common.Hash) (StateDiff, error) } @@ -39,13 +40,15 @@ type builder struct { blockChain *core.BlockChain } -func NewBuilder(db ethdb.Database, blockChain *core.BlockChain) *builder { +// NewBuilder is used to create a builder +func NewBuilder(db ethdb.Database, blockChain *core.BlockChain) Builder { return &builder{ chainDB: db, blockChain: blockChain, } } +// BuildStateDiff builds a StateDiff object from two blocks func (sdb *builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, blockNumber int64, blockHash common.Hash) (StateDiff, error) { // Generate tries for old and new states stateCache := sdb.blockChain.StateCache() @@ -140,7 +143,7 @@ func (sdb *builder) collectDiffNodes(a, b trie.NodeIterator) (AccountsMap, error } // record account to diffs (creation if we are looking at new - old; deletion if old - new) log.Debug("Account lookup successful", "address", leafKeyHash, "account", account) - diffAccounts[leafKeyHash] = &aw + diffAccounts[leafKeyHash] = aw } cont := it.Next(true) if !cont { @@ -153,14 +156,13 @@ func (sdb *builder) collectDiffNodes(a, b trie.NodeIterator) (AccountsMap, error func (sdb *builder) buildDiffEventual(accounts AccountsMap) (AccountDiffsMap, error) { accountDiffs := make(AccountDiffsMap) - for addr, val := range accounts { - sr := val.Account.Root - storageDiffs, err := sdb.buildStorageDiffsEventual(sr) + for _, val := range accounts { + storageDiffs, err := sdb.buildStorageDiffsEventual(val.Account.Root) if err != nil { - log.Error("Failed building eventual storage diffs", "Address", addr, "error", err) + log.Error("Failed building eventual storage diffs", "Address", common.BytesToHash(val.RawKey), "error", err) return nil, err } - accountDiffs[addr] = AccountDiff{ + accountDiffs[common.BytesToHash(val.RawKey)] = AccountDiff{ Key: val.RawKey, Value: val.RawValue, Proof: val.Proof, @@ -179,21 +181,22 @@ func (sdb *builder) buildDiffIncremental(creations AccountsMap, deletions Accoun deletedAcc := deletions[common.HexToHash(val)] oldSR := deletedAcc.Account.Root newSR := createdAcc.Account.Root - if storageDiffs, err := sdb.buildStorageDiffsIncremental(oldSR, newSR); err != nil { + storageDiffs, err := sdb.buildStorageDiffsIncremental(oldSR, newSR) + if err != nil { log.Error("Failed building storage diffs", "Address", val, "error", err) return nil, err - } else { - updatedAccounts[common.HexToHash(val)] = AccountDiff{ - Key: createdAcc.RawKey, - Value: createdAcc.RawValue, - Proof: createdAcc.Proof, - Path: createdAcc.Path, - Storage: storageDiffs, - } - delete(creations, common.HexToHash(val)) - delete(deletions, common.HexToHash(val)) } + updatedAccounts[common.HexToHash(val)] = AccountDiff{ + Key: createdAcc.RawKey, + Value: createdAcc.RawValue, + Proof: createdAcc.Proof, + Path: createdAcc.Path, + Storage: storageDiffs, + } + delete(creations, common.HexToHash(val)) + delete(deletions, common.HexToHash(val)) } + return updatedAccounts, nil } @@ -263,12 +266,12 @@ func buildStorageDiffsFromTrie(it trie.NodeIterator) []StorageDiff { func (sdb *builder) addressByPath(path []byte) (*common.Address, error) { log.Debug("Looking up address from path", "path", hexutil.Encode(append([]byte("secure-key-"), path...))) - if addrBytes, err := sdb.chainDB.Get(append([]byte("secure-key-"), hexToKeyBytes(path)...)); err != nil { + addrBytes, err := sdb.chainDB.Get(append([]byte("secure-key-"), hexToKeyBytes(path)...)) + if err != nil { log.Error("Error looking up address via path", "path", hexutil.Encode(append([]byte("secure-key-"), path...)), "error", err) return nil, err - } else { - addr := common.BytesToAddress(addrBytes) - log.Debug("Address found", "Address", addr) - return &addr, nil } + addr := common.BytesToAddress(addrBytes) + log.Debug("Address found", "Address", addr) + return &addr, nil } diff --git a/statediff/builder_test.go b/statediff/builder_test.go index d74a285dc..b4df80f81 100644 --- a/statediff/builder_test.go +++ b/statediff/builder_test.go @@ -18,41 +18,20 @@ package statediff_test import ( "bytes" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/rlp" "math/big" "reflect" "testing" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core" + "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/ethdb" - "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/statediff" "github.com/ethereum/go-ethereum/statediff/testhelpers" ) var ( - testdb = ethdb.NewMemDatabase() - - testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) //0x71562b71999873DB5b286dF957af199Ec94617F7 - bankLeafKey = testhelpers.AddressToLeafKey(testBankAddress) - testBankFunds = big.NewInt(100000000) - genesis = core.GenesisBlockForTesting(testdb, testBankAddress, testBankFunds) - - account1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") - account2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") - account1Addr = crypto.PubkeyToAddress(account1Key.PublicKey) //0x703c4b2bD70c169f5717101CaeE543299Fc946C7 - account1LeafKey = testhelpers.AddressToLeafKey(account1Addr) - account2Addr = crypto.PubkeyToAddress(account2Key.PublicKey) //0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e - account2LeafKey = testhelpers.AddressToLeafKey(account2Addr) - contractCode = common.Hex2Bytes("608060405234801561001057600080fd5b50602060405190810160405280600160ff16815250600090600161003592919061003b565b506100a5565b826064810192821561006f579160200282015b8281111561006e578251829060ff1690559160200191906001019061004e565b5b50905061007c9190610080565b5090565b6100a291905b8082111561009e576000816000905550600101610086565b5090565b90565b610124806100b46000396000f3fe6080604052348015600f57600080fd5b5060043610604f576000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146054578063c16431b9146093575b600080fd5b607d60048036036020811015606857600080fd5b810190808035906020019092919050505060c8565b6040518082815260200191505060405180910390f35b60c66004803603604081101560a757600080fd5b81019080803590602001909291908035906020019092919050505060e0565b005b6000808260648110151560d757fe5b01549050919050565b8060008360648110151560ef57fe5b0181905550505056fea165627a7a7230582064e918c3140a117bf3aa65865a9b9e83fae21ad1720506e7933b2a9f54bb40260029") - contractAddr common.Address contractLeafKey common.Hash emptyAccountDiffEventualMap = make(statediff.AccountDiffsMap) emptyAccountDiffIncrementalMap = make(statediff.AccountDiffsMap) @@ -65,19 +44,19 @@ var ( ) func TestBuilder(t *testing.T) { - _, blockMap, chain := makeChain(3, genesis) - contractLeafKey = testhelpers.AddressToLeafKey(contractAddr) + _, blockMap, chain := testhelpers.MakeChain(3, testhelpers.Genesis) + contractLeafKey = testhelpers.AddressToLeafKey(testhelpers.ContractAddr) defer chain.Stop() block0Hash = common.HexToHash("0xd1721cfd0b29c36fd7a68f25c128e86413fb666a6e1d68e89b875bd299262661") block1Hash = common.HexToHash("0xbbe88de60ba33a3f18c0caa37d827bfb70252e19e40a07cd34041696c35ecb1a") - block2Hash = common.HexToHash("0xde75663f36a8497b4bdda2a4b52bd9540b705a2728c7391c59b8cb2cde5a2feb") - block3Hash = common.HexToHash("0x76c6d0e39285cee40d5e5fadc6141ca88c8ab8bd1a15d46717205af2efbb4a3c") + block2Hash = common.HexToHash("0x34ad0fd9bb2911986b75d518c822641079dea823bc6952343ebf05da1062b6f5") + block3Hash = common.HexToHash("0x9872058136c560a6ebed0c0522b8d3016fc21f4fb0fb6585ddd8fd4c54f9909a") block0 = blockMap[block0Hash] block1 = blockMap[block1Hash] block2 = blockMap[block2Hash] block3 = blockMap[block3Hash] - builder = statediff.NewBuilder(testdb, chain) + builder = statediff.NewBuilder(testhelpers.Testdb, chain) type arguments struct { oldStateRoot common.Hash @@ -119,7 +98,7 @@ func TestBuilder(t *testing.T) { }) bankAccount1, _ = rlp.EncodeToBytes(state.Account{ Nonce: nonce1, - Balance: big.NewInt(testBankFunds.Int64() - balanceChange10000), + Balance: big.NewInt(testhelpers.TestBankFunds.Int64() - balanceChange10000), CodeHash: common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(), Root: common.HexToHash(originalContractRoot), }) @@ -207,8 +186,8 @@ func TestBuilder(t *testing.T) { BlockNumber: block1.Number().Int64(), BlockHash: block1.Hash(), CreatedAccounts: statediff.AccountDiffsMap{ - account1LeafKey: { - Key: account1LeafKey.Bytes(), + testhelpers.Account1LeafKey: { + Key: testhelpers.Account1LeafKey.Bytes(), Value: account1, Proof: [][]byte{{248, 113, 160, 87, 118, 82, 182, 37, 183, 123, 219, 91, 247, 123, 196, 63, 49, 37, 202, 215, 70, 77, 103, 157, 21, 117, 86, 82, 119, 211, 97, 27, 128, 83, 231, 128, 128, 128, 128, 160, 254, 136, 159, 16, 229, 219, 143, 44, 43, 243, 85, 146, 129, 82, 161, 127, 110, 59, 185, 154, 146, 65, 172, 109, 132, 199, 126, 98, 100, 80, 156, 121, 128, 128, 128, 128, 128, 128, 128, 128, 160, 17, 219, 12, 218, 52, 168, 150, 218, 190, 182, 131, 155, 176, 106, 56, 244, 149, 20, 207, 164, 134, 67, 89, 132, 235, 1, 59, 125, 249, 238, 133, 197, 128, 128}, {248, 107, 160, 57, 38, 219, 105, 170, 206, 213, 24, 233, 185, 240, 244, 52, 164, 115, 231, 23, 65, 9, 201, 67, 84, 139, 184, 242, 59, 228, 28, 167, 109, 154, 210, 184, 72, 248, 70, 128, 130, 39, 16, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, @@ -226,8 +205,8 @@ func TestBuilder(t *testing.T) { }, DeletedAccounts: emptyAccountDiffEventualMap, UpdatedAccounts: statediff.AccountDiffsMap{ - bankLeafKey: { - Key: bankLeafKey.Bytes(), + testhelpers.BankLeafKey: { + Key: testhelpers.BankLeafKey.Bytes(), Value: bankAccount1, Proof: [][]byte{{248, 113, 160, 87, 118, 82, 182, 37, 183, 123, 219, 91, 247, 123, 196, 63, 49, 37, 202, 215, 70, 77, 103, 157, 21, 117, 86, 82, 119, 211, 97, 27, 128, 83, 231, 128, 128, 128, 128, 160, 254, 136, 159, 16, 229, 219, 143, 44, 43, 243, 85, 146, 129, 82, 161, 127, 110, 59, 185, 154, 146, 65, 172, 109, 132, 199, 126, 98, 100, 80, 156, 121, 128, 128, 128, 128, 128, 128, 128, 128, 160, 17, 219, 12, 218, 52, 168, 150, 218, 190, 182, 131, 155, 176, 106, 56, 244, 149, 20, 207, 164, 134, 67, 89, 132, 235, 1, 59, 125, 249, 238, 133, 197, 128, 128}, {248, 109, 160, 48, 191, 73, 244, 64, 161, 205, 5, 39, 228, 208, 110, 39, 101, 101, 76, 15, 86, 69, 34, 87, 81, 109, 121, 58, 155, 141, 96, 77, 207, 223, 42, 184, 74, 248, 72, 1, 132, 5, 245, 185, 240, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, @@ -251,8 +230,8 @@ func TestBuilder(t *testing.T) { BlockNumber: block2.Number().Int64(), BlockHash: block2.Hash(), CreatedAccounts: statediff.AccountDiffsMap{ - account2LeafKey: { - Key: account2LeafKey.Bytes(), + testhelpers.Account2LeafKey: { + Key: testhelpers.Account2LeafKey.Bytes(), Value: account2, Proof: [][]byte{{248, 177, 160, 177, 155, 238, 178, 242, 47, 83, 2, 49, 141, 155, 92, 149, 175, 245, 120, 233, 177, 101, 67, 46, 200, 23, 250, 41, 74, 135, 94, 61, 133, 51, 162, 128, 128, 128, 128, 160, 179, 86, 53, 29, 96, 188, 152, 148, 207, 31, 29, 108, 182, 140, 129, 95, 1, 49, 213, 15, 29, 168, 60, 64, 35, 160, 158, 200, 85, 207, 255, 145, 160, 114, 57, 32, 11, 115, 232, 140, 238, 165, 222, 121, 226, 208, 2, 192, 216, 67, 198, 179, 31, 181, 27, 208, 243, 99, 202, 48, 148, 207, 107, 106, 177, 128, 128, 128, 128, 128, 160, 10, 173, 165, 125, 110, 240, 77, 112, 149, 100, 135, 237, 25, 228, 116, 7, 195, 9, 210, 166, 208, 148, 101, 23, 244, 238, 84, 84, 211, 249, 138, 137, 128, 160, 255, 115, 147, 190, 57, 135, 174, 188, 86, 51, 227, 70, 22, 253, 237, 49, 24, 19, 149, 199, 142, 195, 186, 244, 70, 51, 138, 0, 146, 148, 117, 60, 128, 128}, {248, 107, 160, 57, 87, 243, 226, 240, 74, 7, 100, 195, 160, 73, 27, 23, 95, 105, 146, 109, 166, 30, 251, 204, 143, 97, 250, 20, 85, 253, 45, 43, 76, 221, 69, 184, 72, 248, 70, 128, 130, 3, 232, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, @@ -277,16 +256,16 @@ func TestBuilder(t *testing.T) { }, DeletedAccounts: emptyAccountDiffEventualMap, UpdatedAccounts: statediff.AccountDiffsMap{ - bankLeafKey: { - Key: bankLeafKey.Bytes(), + testhelpers.BankLeafKey: { + Key: testhelpers.BankLeafKey.Bytes(), Value: bankAccount2, Proof: [][]byte{{248, 177, 160, 177, 155, 238, 178, 242, 47, 83, 2, 49, 141, 155, 92, 149, 175, 245, 120, 233, 177, 101, 67, 46, 200, 23, 250, 41, 74, 135, 94, 61, 133, 51, 162, 128, 128, 128, 128, 160, 179, 86, 53, 29, 96, 188, 152, 148, 207, 31, 29, 108, 182, 140, 129, 95, 1, 49, 213, 15, 29, 168, 60, 64, 35, 160, 158, 200, 85, 207, 255, 145, 160, 114, 57, 32, 11, 115, 232, 140, 238, 165, 222, 121, 226, 208, 2, 192, 216, 67, 198, 179, 31, 181, 27, 208, 243, 99, 202, 48, 148, 207, 107, 106, 177, 128, 128, 128, 128, 128, 160, 10, 173, 165, 125, 110, 240, 77, 112, 149, 100, 135, 237, 25, 228, 116, 7, 195, 9, 210, 166, 208, 148, 101, 23, 244, 238, 84, 84, 211, 249, 138, 137, 128, 160, 255, 115, 147, 190, 57, 135, 174, 188, 86, 51, 227, 70, 22, 253, 237, 49, 24, 19, 149, 199, 142, 195, 186, 244, 70, 51, 138, 0, 146, 148, 117, 60, 128, 128}, {248, 109, 160, 48, 191, 73, 244, 64, 161, 205, 5, 39, 228, 208, 110, 39, 101, 101, 76, 15, 86, 69, 34, 87, 81, 109, 121, 58, 155, 141, 96, 77, 207, 223, 42, 184, 74, 248, 72, 2, 132, 5, 245, 182, 8, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, Path: []byte{0, 0, 11, 15, 4, 9, 15, 4, 4, 0, 10, 1, 12, 13, 0, 5, 2, 7, 14, 4, 13, 0, 6, 14, 2, 7, 6, 5, 6, 5, 4, 12, 0, 15, 5, 6, 4, 5, 2, 2, 5, 7, 5, 1, 6, 13, 7, 9, 3, 10, 9, 11, 8, 13, 6, 0, 4, 13, 12, 15, 13, 15, 2, 10, 16}, Storage: []statediff.StorageDiff{}, }, - account1LeafKey: { - Key: account1LeafKey.Bytes(), + testhelpers.Account1LeafKey: { + Key: testhelpers.Account1LeafKey.Bytes(), Value: account3, Proof: [][]byte{{248, 177, 160, 177, 155, 238, 178, 242, 47, 83, 2, 49, 141, 155, 92, 149, 175, 245, 120, 233, 177, 101, 67, 46, 200, 23, 250, 41, 74, 135, 94, 61, 133, 51, 162, 128, 128, 128, 128, 160, 179, 86, 53, 29, 96, 188, 152, 148, 207, 31, 29, 108, 182, 140, 129, 95, 1, 49, 213, 15, 29, 168, 60, 64, 35, 160, 158, 200, 85, 207, 255, 145, 160, 114, 57, 32, 11, 115, 232, 140, 238, 165, 222, 121, 226, 208, 2, 192, 216, 67, 198, 179, 31, 181, 27, 208, 243, 99, 202, 48, 148, 207, 107, 106, 177, 128, 128, 128, 128, 128, 160, 10, 173, 165, 125, 110, 240, 77, 112, 149, 100, 135, 237, 25, 228, 116, 7, 195, 9, 210, 166, 208, 148, 101, 23, 244, 238, 84, 84, 211, 249, 138, 137, 128, 160, 255, 115, 147, 190, 57, 135, 174, 188, 86, 51, 227, 70, 22, 253, 237, 49, 24, 19, 149, 199, 142, 195, 186, 244, 70, 51, 138, 0, 146, 148, 117, 60, 128, 128}, {248, 107, 160, 57, 38, 219, 105, 170, 206, 213, 24, 233, 185, 240, 244, 52, 164, 115, 231, 23, 65, 9, 201, 67, 84, 139, 184, 242, 59, 228, 28, 167, 109, 154, 210, 184, 72, 248, 70, 2, 130, 39, 16, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, @@ -320,8 +299,8 @@ func TestBuilder(t *testing.T) { CreatedAccounts: statediff.AccountDiffsMap{}, DeletedAccounts: emptyAccountDiffEventualMap, UpdatedAccounts: statediff.AccountDiffsMap{ - account2LeafKey: { - Key: account2LeafKey.Bytes(), + testhelpers.Account2LeafKey: { + Key: testhelpers.Account2LeafKey.Bytes(), Value: account4, Proof: [][]byte{{248, 177, 160, 101, 223, 138, 81, 34, 40, 229, 170, 198, 188, 136, 99, 7, 55, 33, 112, 160, 111, 181, 131, 167, 201, 131, 24, 201, 211, 177, 30, 159, 229, 246, 6, 128, 128, 128, 128, 160, 179, 86, 53, 29, 96, 188, 152, 148, 207, 31, 29, 108, 182, 140, 129, 95, 1, 49, 213, 15, 29, 168, 60, 64, 35, 160, 158, 200, 85, 207, 255, 145, 160, 32, 135, 108, 213, 150, 150, 110, 44, 170, 65, 75, 154, 74, 249, 94, 65, 74, 107, 100, 115, 39, 5, 3, 26, 22, 238, 138, 114, 254, 21, 6, 171, 128, 128, 128, 128, 128, 160, 4, 228, 121, 222, 255, 218, 60, 247, 15, 0, 34, 198, 28, 229, 180, 129, 109, 157, 68, 181, 248, 229, 200, 123, 29, 81, 145, 114, 90, 209, 205, 210, 128, 160, 255, 115, 147, 190, 57, 135, 174, 188, 86, 51, 227, 70, 22, 253, 237, 49, 24, 19, 149, 199, 142, 195, 186, 244, 70, 51, 138, 0, 146, 148, 117, 60, 128, 128}, {248, 113, 160, 57, 87, 243, 226, 240, 74, 7, 100, 195, 160, 73, 27, 23, 95, 105, 146, 109, 166, 30, 251, 204, 143, 97, 250, 20, 85, 253, 45, 43, 76, 221, 69, 184, 78, 248, 76, 128, 136, 27, 193, 109, 103, 78, 200, 3, 232, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, @@ -344,8 +323,8 @@ func TestBuilder(t *testing.T) { }, }, }, - bankLeafKey: { - Key: bankLeafKey.Bytes(), + testhelpers.BankLeafKey: { + Key: testhelpers.BankLeafKey.Bytes(), Value: bankAccount3, Proof: [][]byte{{248, 177, 160, 101, 223, 138, 81, 34, 40, 229, 170, 198, 188, 136, 99, 7, 55, 33, 112, 160, 111, 181, 131, 167, 201, 131, 24, 201, 211, 177, 30, 159, 229, 246, 6, 128, 128, 128, 128, 160, 179, 86, 53, 29, 96, 188, 152, 148, 207, 31, 29, 108, 182, 140, 129, 95, 1, 49, 213, 15, 29, 168, 60, 64, 35, 160, 158, 200, 85, 207, 255, 145, 160, 32, 135, 108, 213, 150, 150, 110, 44, 170, 65, 75, 154, 74, 249, 94, 65, 74, 107, 100, 115, 39, 5, 3, 26, 22, 238, 138, 114, 254, 21, 6, 171, 128, 128, 128, 128, 128, 160, 4, 228, 121, 222, 255, 218, 60, 247, 15, 0, 34, 198, 28, 229, 180, 129, 109, 157, 68, 181, 248, 229, 200, 123, 29, 81, 145, 114, 90, 209, 205, 210, 128, 160, 255, 115, 147, 190, 57, 135, 174, 188, 86, 51, 227, 70, 22, 253, 237, 49, 24, 19, 149, 199, 142, 195, 186, 244, 70, 51, 138, 0, 146, 148, 117, 60, 128, 128}, {248, 109, 160, 48, 191, 73, 244, 64, 161, 205, 5, 39, 228, 208, 110, 39, 101, 101, 76, 15, 86, 69, 34, 87, 81, 109, 121, 58, 155, 141, 96, 77, 207, 223, 42, 184, 74, 248, 72, 3, 132, 5, 245, 182, 8, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, @@ -393,60 +372,6 @@ func equals(actual, expected interface{}) (success bool) { return reflect.DeepEqual(actual, expected) } -// makeChain creates a chain of n blocks starting at and including parent. -// the returned hash chain is ordered head->parent. In addition, every 3rd block -// contains a transaction and every 5th an uncle to allow testing correct block -// reassembly. -func makeChain(n int, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block, *core.BlockChain) { - blocks, _ := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testdb, n, testChainGen) - headers := make([]*types.Header, len(blocks)) - for i, block := range blocks { - headers[i] = block.Header() - } - chain, _ := core.NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil) - - hashes := make([]common.Hash, n+1) - hashes[len(hashes)-1] = parent.Hash() - blockm := make(map[common.Hash]*types.Block, n+1) - blockm[parent.Hash()] = parent - for i, b := range blocks { - hashes[len(hashes)-i-2] = b.Hash() - blockm[b.Hash()] = b - } - return hashes, blockm, chain -} - -func testChainGen(i int, block *core.BlockGen) { - signer := types.HomesteadSigner{} - switch i { - case 0: - // In block 1, the test bank sends account #1 some ether. - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), account1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) - block.AddTx(tx) - case 1: - // In block 2, the test bank sends some more ether to account #1. - // account1Addr passes it on to account #2. - // account1Addr creates a test contract. - tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), account1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey) - nonce := block.TxNonce(account1Addr) - tx2, _ := types.SignTx(types.NewTransaction(nonce, account2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, account1Key) - nonce++ - tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, big.NewInt(0), contractCode), signer, account1Key) - contractAddr = crypto.CreateAddress(account1Addr, nonce) //0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476592 - block.AddTx(tx1) - block.AddTx(tx2) - block.AddTx(tx3) - case 2: - // Block 3 is empty but was mined by account #2. - block.SetCoinbase(account2Addr) - //get function: 60cd2685 - //put function: c16431b9 - data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), contractAddr, big.NewInt(0), 100000, nil, data), signer, testBankKey) - block.AddTx(tx) - } -} - /* contract test { diff --git a/statediff/helpers.go b/statediff/helpers.go index e1a989144..96f9ddf30 100644 --- a/statediff/helpers.go +++ b/statediff/helpers.go @@ -37,6 +37,22 @@ func sortKeys(data AccountsMap) []string { return keys } +// BytesToNiblePath +func bytesToNiblePath(path []byte) string { + if hasTerm(path) { + path = path[:len(path)-1] + } + nibblePath := "" + for i, v := range common.ToHex(path) { + if i%2 == 0 && i > 1 { + continue + } + nibblePath = nibblePath + string(v) + } + + return nibblePath +} + func findIntersection(a, b []string) []string { lenA := len(a) lenB := len(b) @@ -73,22 +89,7 @@ func findIntersection(a, b []string) []string { } func pathToStr(it trie.NodeIterator) string { - return BytesToNiblePath(it.Path()) -} - -func BytesToNiblePath(path []byte) string { - if hasTerm(path) { - path = path[:len(path)-1] - } - nibblePath := "" - for i, v := range common.ToHex(path) { - if i%2 == 0 && i > 1 { - continue - } - nibblePath = nibblePath + string(v) - } - - return nibblePath + return bytesToNiblePath(it.Path()) } // Duplicated from trie/encoding.go diff --git a/statediff/service.go b/statediff/service.go index 0609861d5..314a8ab12 100644 --- a/statediff/service.go +++ b/statediff/service.go @@ -19,9 +19,11 @@ package statediff import ( "bytes" "fmt" - "github.com/ethereum/go-ethereum/node" "sync" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" @@ -32,60 +34,69 @@ import ( "github.com/ethereum/go-ethereum/rpc" ) -type BlockChain interface { +type blockChain interface { SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription GetBlockByHash(hash common.Hash) *types.Block AddToStateDiffProcessedCollection(hash common.Hash) } -type SDS interface { +// IService is the state-diffing service interface +type IService interface { // APIs(), Protocols(), Start() and Stop() node.Service // Main event loop for processing state diffs Loop(chainEventCh chan core.ChainEvent) // Method to subscribe to receive state diff processing output - Subscribe(id rpc.ID, sub chan<- StateDiffPayload, quitChan chan<- bool) + Subscribe(id rpc.ID, sub chan<- Payload, quitChan chan<- bool) // Method to unsubscribe from state diff processing Unsubscribe(id rpc.ID) error } -type StateDiffingService struct { +// Service is the underlying struct for the state diffing service +type Service struct { + // Used to sync access to the Subscriptions sync.Mutex - Builder Builder - BlockChain BlockChain - QuitChan chan bool - Subscriptions Subscriptions + // Used to build the state diff objects + Builder Builder + // Used to subscribe to chain events (blocks) + BlockChain blockChain + // Used to signal shutdown of the service + QuitChan chan bool + // A mapping of rpc.IDs to their subscription channels + Subscriptions map[rpc.ID]Subscription } -type Subscriptions map[rpc.ID]Subscription - +// Subscription struct holds our subscription channels type Subscription struct { - PayloadChan chan<- StateDiffPayload + PayloadChan chan<- Payload QuitChan chan<- bool } -type StateDiffPayload struct { - BlockRlp []byte `json:"block"` - StateDiff StateDiff `json:"state_diff"` - Err error `json:"error"` +// Payload packages the data to send to StateDiffingService subscriptions +type Payload struct { + BlockRlp []byte `json:"blockRlp" gencodec:"required"` + StateDiffRlp []byte `json:"stateDiffRlp" gencodec:"required"` + Err error `json:"error"` } -func NewStateDiffService(db ethdb.Database, blockChain *core.BlockChain) (*StateDiffingService, error) { - return &StateDiffingService{ +// NewStateDiffService creates a new StateDiffingService +func NewStateDiffService(db ethdb.Database, blockChain *core.BlockChain) (*Service, error) { + return &Service{ Mutex: sync.Mutex{}, BlockChain: blockChain, Builder: NewBuilder(db, blockChain), QuitChan: make(chan bool), - Subscriptions: make(Subscriptions), + Subscriptions: make(map[rpc.ID]Subscription), }, nil } -func (sds *StateDiffingService) Protocols() []p2p.Protocol { +// Protocols exports the services p2p protocols, this service has none +func (sds *Service) Protocols() []p2p.Protocol { return []p2p.Protocol{} } -// APIs returns the RPC descriptors the Whisper implementation offers -func (sds *StateDiffingService) APIs() []rpc.API { +// APIs returns the RPC descriptors the StateDiffingService offers +func (sds *Service) APIs() []rpc.API { return []rpc.API{ { Namespace: APIName, @@ -96,7 +107,9 @@ func (sds *StateDiffingService) APIs() []rpc.API { } } -func (sds *StateDiffingService) Loop(chainEventCh chan core.ChainEvent) { +// Loop is the main processing method +func (sds *Service) Loop(chainEventCh chan core.ChainEvent) { + chainEventSub := sds.BlockChain.SubscribeChainEvent(chainEventCh) defer chainEventSub.Unsubscribe() @@ -144,10 +157,11 @@ HandleBlockChLoop: rlpBuff := new(bytes.Buffer) currentBlock.EncodeRLP(rlpBuff) blockRlp := rlpBuff.Bytes() - payload := StateDiffPayload{ - BlockRlp: blockRlp, - StateDiff: stateDiff, - Err: err, + stateDiffRlp, _ := rlp.EncodeToBytes(stateDiff) + payload := Payload{ + BlockRlp: blockRlp, + StateDiffRlp: stateDiffRlp, + Err: err, } // If we have any websocket subscription listening in, send the data to them sds.send(payload) @@ -159,7 +173,8 @@ HandleBlockChLoop: } } -func (sds *StateDiffingService) Subscribe(id rpc.ID, sub chan<- StateDiffPayload, quitChan chan<- bool) { +// Subscribe is used by the API to subscribe to the StateDiffingService loop +func (sds *Service) Subscribe(id rpc.ID, sub chan<- Payload, quitChan chan<- bool) { log.Info("Subscribing to the statediff service") sds.Lock() sds.Subscriptions[id] = Subscription{ @@ -169,7 +184,8 @@ func (sds *StateDiffingService) Subscribe(id rpc.ID, sub chan<- StateDiffPayload sds.Unlock() } -func (sds *StateDiffingService) Unsubscribe(id rpc.ID) error { +// Unsubscribe is used to unsubscribe to the StateDiffingService loop +func (sds *Service) Unsubscribe(id rpc.ID) error { log.Info("Unsubscribing from the statediff service") sds.Lock() _, ok := sds.Subscriptions[id] @@ -181,7 +197,25 @@ func (sds *StateDiffingService) Unsubscribe(id rpc.ID) error { return nil } -func (sds *StateDiffingService) send(payload StateDiffPayload) { +// Start is used to begin the StateDiffingService +func (sds *Service) Start(*p2p.Server) error { + log.Info("Starting statediff service") + + chainEventCh := make(chan core.ChainEvent, 10) + go sds.Loop(chainEventCh) + + return nil +} + +// Stop is used to close down the StateDiffingService +func (sds *Service) Stop() error { + log.Info("Stopping statediff service") + close(sds.QuitChan) + return nil +} + +// send is used to fan out and serve a payload to any subscriptions +func (sds *Service) send(payload Payload) { sds.Lock() for id, sub := range sds.Subscriptions { select { @@ -194,7 +228,8 @@ func (sds *StateDiffingService) send(payload StateDiffPayload) { sds.Unlock() } -func (sds *StateDiffingService) close() { +// close is used to close all listening subscriptions +func (sds *Service) close() { sds.Lock() for id, sub := range sds.Subscriptions { select { @@ -207,18 +242,3 @@ func (sds *StateDiffingService) close() { } sds.Unlock() } - -func (sds *StateDiffingService) Start(server *p2p.Server) error { - log.Info("Starting statediff service") - - chainEventCh := make(chan core.ChainEvent, 10) - go sds.Loop(chainEventCh) - - return nil -} - -func (sds *StateDiffingService) Stop() error { - log.Info("Stopping statediff service") - close(sds.QuitChan) - return nil -} diff --git a/statediff/service_test.go b/statediff/service_test.go index 2221e2e66..79e2f4dbc 100644 --- a/statediff/service_test.go +++ b/statediff/service_test.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/statediff" "github.com/ethereum/go-ethereum/statediff/testhelpers/mocks" ) @@ -69,11 +70,11 @@ func testErrorInChainEventLoop(t *testing.T) { //the first chain event causes and error (in blockchain mock) builder := mocks.Builder{} blockChain := mocks.BlockChain{} - service := statediff.StateDiffingService{ + service := statediff.Service{ Builder: &builder, BlockChain: &blockChain, QuitChan: make(chan bool), - Subscriptions: make(statediff.Subscriptions), + Subscriptions: make(map[rpc.ID]statediff.Subscription), } testRoot2 = common.HexToHash("0xTestRoot2") blockChain.SetParentBlocksToReturn([]*types.Block{parentBlock1, parentBlock2}) @@ -103,11 +104,11 @@ func testErrorInBlockLoop(t *testing.T) { //second block's parent block can't be found builder := mocks.Builder{} blockChain := mocks.BlockChain{} - service := statediff.StateDiffingService{ + service := statediff.Service{ Builder: &builder, BlockChain: &blockChain, QuitChan: make(chan bool), - Subscriptions: make(statediff.Subscriptions), + Subscriptions: make(map[rpc.ID]statediff.Subscription), } blockChain.SetParentBlocksToReturn([]*types.Block{parentBlock1, nil}) diff --git a/statediff/testhelpers/helpers.go b/statediff/testhelpers/helpers.go index 86b851f89..5126c6556 100644 --- a/statediff/testhelpers/helpers.go +++ b/statediff/testhelpers/helpers.go @@ -16,5 +16,68 @@ package testhelpers -var ErrorFormatString = "Error: %s, %+v" -var TestFailureFormatString = "Test failed: %s\nexpected %+v, got %+v\n" +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core" + "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/params" +) + +// MakeChain creates a chain of n blocks starting at and including parent. +// the returned hash chain is ordered head->parent. In addition, every 3rd block +// contains a transaction and every 5th an uncle to allow testing correct block +// reassembly. +func MakeChain(n int, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block, *core.BlockChain) { + blocks, _ := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), Testdb, n, testChainGen) + headers := make([]*types.Header, len(blocks)) + for i, block := range blocks { + headers[i] = block.Header() + } + chain, _ := core.NewBlockChain(Testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil) + + hashes := make([]common.Hash, n+1) + hashes[len(hashes)-1] = parent.Hash() + blockm := make(map[common.Hash]*types.Block, n+1) + blockm[parent.Hash()] = parent + for i, b := range blocks { + hashes[len(hashes)-i-2] = b.Hash() + blockm[b.Hash()] = b + } + return hashes, blockm, chain +} + +func testChainGen(i int, block *core.BlockGen) { + signer := types.HomesteadSigner{} + switch i { + case 0: + // In block 1, the test bank sends account #1 some ether. + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(TestBankAddress), Account1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, TestBankKey) + block.AddTx(tx) + case 1: + // In block 2, the test bank sends some more ether to account #1. + // account1Addr passes it on to account #2. + // account1Addr creates a test contract. + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(TestBankAddress), Account1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, TestBankKey) + nonce := block.TxNonce(Account1Addr) + tx2, _ := types.SignTx(types.NewTransaction(nonce, Account2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, Account1Key) + nonce++ + tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, big.NewInt(0), ContractCode), signer, Account1Key) + ContractAddr = crypto.CreateAddress(Account1Addr, nonce) //0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476592 + block.AddTx(tx1) + block.AddTx(tx2) + block.AddTx(tx3) + case 2: + // Block 3 is empty but was mined by account #2. + block.SetCoinbase(Account2Addr) + //get function: 60cd2685 + //put function: c16431b9 + data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(TestBankAddress), ContractAddr, big.NewInt(0), 100000, nil, data), signer, TestBankKey) + block.AddTx(tx) + } +} diff --git a/statediff/testhelpers/mocks/blockchain.go b/statediff/testhelpers/mocks/blockchain.go index ed16c3471..cececde6f 100644 --- a/statediff/testhelpers/mocks/blockchain.go +++ b/statediff/testhelpers/mocks/blockchain.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/event" ) +// BlockChain is a mock blockchain for testing type BlockChain struct { ParentHashesLookedUp []common.Hash parentBlocksToReturn []*types.Block @@ -34,34 +35,39 @@ type BlockChain struct { ChainEvents []core.ChainEvent } -func (mc *BlockChain) AddToStateDiffProcessedCollection(hash common.Hash) {} +// AddToStateDiffProcessedCollection mock method +func (blockChain *BlockChain) AddToStateDiffProcessedCollection(hash common.Hash) {} -func (mc *BlockChain) SetParentBlocksToReturn(blocks []*types.Block) { - mc.parentBlocksToReturn = blocks +// SetParentBlocksToReturn mock method +func (blockChain *BlockChain) SetParentBlocksToReturn(blocks []*types.Block) { + blockChain.parentBlocksToReturn = blocks } -func (mc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block { - mc.ParentHashesLookedUp = append(mc.ParentHashesLookedUp, hash) +// GetBlockByHash mock method +func (blockChain *BlockChain) GetBlockByHash(hash common.Hash) *types.Block { + blockChain.ParentHashesLookedUp = append(blockChain.ParentHashesLookedUp, hash) var parentBlock *types.Block - if len(mc.parentBlocksToReturn) > 0 { - parentBlock = mc.parentBlocksToReturn[mc.callCount] + if len(blockChain.parentBlocksToReturn) > 0 { + parentBlock = blockChain.parentBlocksToReturn[blockChain.callCount] } - mc.callCount++ + blockChain.callCount++ return parentBlock } -func (bc *BlockChain) SetChainEvents(chainEvents []core.ChainEvent) { - bc.ChainEvents = chainEvents +// SetChainEvents mock method +func (blockChain *BlockChain) SetChainEvents(chainEvents []core.ChainEvent) { + blockChain.ChainEvents = chainEvents } -func (bc *BlockChain) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { +// SubscribeChainEvent mock method +func (blockChain *BlockChain) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { subErr := errors.New("Subscription Error") var eventCounter int subscription := event.NewSubscription(func(quit <-chan struct{}) error { - for _, chainEvent := range bc.ChainEvents { + for _, chainEvent := range blockChain.ChainEvents { if eventCounter > 1 { time.Sleep(250 * time.Millisecond) return subErr diff --git a/statediff/testhelpers/mocks/builder.go b/statediff/testhelpers/mocks/builder.go index d3252fe4b..e9668629e 100644 --- a/statediff/testhelpers/mocks/builder.go +++ b/statediff/testhelpers/mocks/builder.go @@ -21,6 +21,7 @@ import ( "github.com/ethereum/go-ethereum/statediff" ) +// Builder is a mock state diff builder type Builder struct { OldStateRoot common.Hash NewStateRoot common.Hash @@ -30,6 +31,7 @@ type Builder struct { builderError error } +// BuildStateDiff mock method func (builder *Builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, blockNumber int64, blockHash common.Hash) (statediff.StateDiff, error) { builder.OldStateRoot = oldStateRoot builder.NewStateRoot = newStateRoot @@ -39,10 +41,12 @@ func (builder *Builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, b return builder.stateDiff, builder.builderError } +// SetStateDiffToBuild mock method func (builder *Builder) SetStateDiffToBuild(stateDiff statediff.StateDiff) { builder.stateDiff = stateDiff } +// SetBuilderError mock method func (builder *Builder) SetBuilderError(err error) { builder.builderError = err } diff --git a/statediff/testhelpers/mocks/error.go b/statediff/testhelpers/mocks/error.go deleted file mode 100644 index dafb0371e..000000000 --- a/statediff/testhelpers/mocks/error.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package mocks - -import "errors" - -var Error = errors.New("mock error") diff --git a/statediff/testhelpers/mocks/publisher.go b/statediff/testhelpers/mocks/publisher.go index 01f4c5997..3ea18abf0 100644 --- a/statediff/testhelpers/mocks/publisher.go +++ b/statediff/testhelpers/mocks/publisher.go @@ -18,16 +18,19 @@ package mocks import "github.com/ethereum/go-ethereum/statediff" +// Publisher mock type Publisher struct { StateDiff *statediff.StateDiff publisherError error } +// PublishStateDiff mock method func (publisher *Publisher) PublishStateDiff(sd *statediff.StateDiff) (string, error) { publisher.StateDiff = sd return "", publisher.publisherError } +// SetPublisherError mock method func (publisher *Publisher) SetPublisherError(err error) { publisher.publisherError = err } diff --git a/statediff/testhelpers/mocks/service.go b/statediff/testhelpers/mocks/service.go index d948091a1..5882f7e9b 100644 --- a/statediff/testhelpers/mocks/service.go +++ b/statediff/testhelpers/mocks/service.go @@ -26,26 +26,29 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/statediff" ) +// MockStateDiffService is a mock state diff service type MockStateDiffService struct { sync.Mutex - Builder statediff.Builder - ReturnProtocol []p2p.Protocol - ReturnAPIs []rpc.API - MockBlockChan chan *types.Block - MockParentBlockChan chan *types.Block - QuitChan chan bool - Subscriptions statediff.Subscriptions + Builder statediff.Builder + ReturnProtocol []p2p.Protocol + ReturnAPIs []rpc.API + BlockChan chan *types.Block + ParentBlockChan chan *types.Block + QuitChan chan bool + Subscriptions map[rpc.ID]statediff.Subscription } +// Protocols mock method func (sds *MockStateDiffService) Protocols() []p2p.Protocol { return []p2p.Protocol{} } -// APIs returns the RPC descriptors the Whisper implementation offers +// APIs mock method func (sds *MockStateDiffService) APIs() []rpc.API { return []rpc.API{ { @@ -57,14 +60,15 @@ func (sds *MockStateDiffService) APIs() []rpc.API { } } +// Loop mock method func (sds *MockStateDiffService) Loop(chan core.ChainEvent) { //loop through chain events until no more HandleBlockChLoop: for { select { - case block := <-sds.MockBlockChan: + case block := <-sds.BlockChan: currentBlock := block - parentBlock := <-sds.MockParentBlockChan + parentBlock := <-sds.ParentBlockChan parentHash := parentBlock.Hash() if parentBlock == nil { log.Error("Parent block is nil, skipping this block", @@ -80,10 +84,11 @@ HandleBlockChLoop: rlpBuff := new(bytes.Buffer) currentBlock.EncodeRLP(rlpBuff) blockRlp := rlpBuff.Bytes() - payload := statediff.StateDiffPayload{ - BlockRlp: blockRlp, - StateDiff: stateDiff, - Err: err, + stateDiffRlp, _ := rlp.EncodeToBytes(stateDiff) + payload := statediff.Payload{ + BlockRlp: blockRlp, + StateDiffRlp: stateDiffRlp, + Err: err, } // If we have any websocket subscription listening in, send the data to them sds.send(payload) @@ -95,7 +100,8 @@ HandleBlockChLoop: } } -func (sds *MockStateDiffService) Subscribe(id rpc.ID, sub chan<- statediff.StateDiffPayload, quitChan chan<- bool) { +// Subscribe mock method +func (sds *MockStateDiffService) Subscribe(id rpc.ID, sub chan<- statediff.Payload, quitChan chan<- bool) { log.Info("Subscribing to the statediff service") sds.Lock() sds.Subscriptions[id] = statediff.Subscription{ @@ -105,6 +111,7 @@ func (sds *MockStateDiffService) Subscribe(id rpc.ID, sub chan<- statediff.State sds.Unlock() } +// Unsubscribe mock method func (sds *MockStateDiffService) Unsubscribe(id rpc.ID) error { log.Info("Unsubscribing from the statediff service") sds.Lock() @@ -117,7 +124,7 @@ func (sds *MockStateDiffService) Unsubscribe(id rpc.ID) error { return nil } -func (sds *MockStateDiffService) send(payload statediff.StateDiffPayload) { +func (sds *MockStateDiffService) send(payload statediff.Payload) { sds.Lock() for id, sub := range sds.Subscriptions { select { @@ -144,9 +151,10 @@ func (sds *MockStateDiffService) close() { sds.Unlock() } +// Start mock method func (sds *MockStateDiffService) Start(server *p2p.Server) error { log.Info("Starting statediff service") - if sds.MockParentBlockChan == nil || sds.MockBlockChan == nil { + if sds.ParentBlockChan == nil || sds.BlockChan == nil { return errors.New("mock StateDiffingService requires preconfiguration with a MockParentBlockChan and MockBlockChan") } chainEventCh := make(chan core.ChainEvent, 10) @@ -155,6 +163,7 @@ func (sds *MockStateDiffService) Start(server *p2p.Server) error { return nil } +// Stop mock method func (sds *MockStateDiffService) Stop() error { log.Info("Stopping statediff service") close(sds.QuitChan) diff --git a/statediff/testhelpers/mocks/service_test.go b/statediff/testhelpers/mocks/service_test.go new file mode 100644 index 000000000..f310eb063 --- /dev/null +++ b/statediff/testhelpers/mocks/service_test.go @@ -0,0 +1,127 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package mocks + +import ( + "bytes" + "math/big" + "sync" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/statediff" + "github.com/ethereum/go-ethereum/statediff/testhelpers" +) + +var block0, block1 *types.Block +var burnLeafKey = testhelpers.AddressToLeafKey(common.HexToAddress("0x0")) +var emptyAccountDiffEventualMap = make(statediff.AccountDiffsMap) +var account1, _ = rlp.EncodeToBytes(state.Account{ + Nonce: uint64(0), + Balance: big.NewInt(10000), + CodeHash: common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(), + Root: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), +}) +var burnAccount1, _ = rlp.EncodeToBytes(state.Account{ + Nonce: uint64(0), + Balance: big.NewInt(2000000000000000000), + CodeHash: common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(), + Root: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), +}) +var bankAccount1, _ = rlp.EncodeToBytes(state.Account{ + Nonce: uint64(1), + Balance: big.NewInt(testhelpers.TestBankFunds.Int64() - 10000), + CodeHash: common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(), + Root: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), +}) + +func TestAPI(t *testing.T) { + _, blockMap, chain := testhelpers.MakeChain(3, testhelpers.Genesis) + defer chain.Stop() + block0Hash := common.HexToHash("0xd1721cfd0b29c36fd7a68f25c128e86413fb666a6e1d68e89b875bd299262661") + block1Hash := common.HexToHash("0xbbe88de60ba33a3f18c0caa37d827bfb70252e19e40a07cd34041696c35ecb1a") + block0 = blockMap[block0Hash] + block1 = blockMap[block1Hash] + blockChan := make(chan *types.Block) + parentBlockChain := make(chan *types.Block) + serviceQuitChan := make(chan bool) + mockService := MockStateDiffService{ + Mutex: sync.Mutex{}, + Builder: statediff.NewBuilder(testhelpers.Testdb, chain), + BlockChan: blockChan, + ParentBlockChan: parentBlockChain, + QuitChan: serviceQuitChan, + Subscriptions: make(map[rpc.ID]statediff.Subscription), + } + mockService.Start(nil) + id := rpc.NewID() + payloadChan := make(chan statediff.Payload) + quitChan := make(chan bool) + mockService.Subscribe(id, payloadChan, quitChan) + blockChan <- block1 + parentBlockChain <- block0 + expectedBlockRlp, _ := rlp.EncodeToBytes(block1) + expectedStateDiff := &statediff.StateDiff{ + BlockNumber: block1.Number().Int64(), + BlockHash: block1.Hash(), + CreatedAccounts: statediff.AccountDiffsMap{ + testhelpers.Account1LeafKey: { + Key: testhelpers.Account1LeafKey.Bytes(), + Value: account1, + Proof: [][]byte{{248, 113, 160, 87, 118, 82, 182, 37, 183, 123, 219, 91, 247, 123, 196, 63, 49, 37, 202, 215, 70, 77, 103, 157, 21, 117, 86, 82, 119, 211, 97, 27, 128, 83, 231, 128, 128, 128, 128, 160, 254, 136, 159, 16, 229, 219, 143, 44, 43, 243, 85, 146, 129, 82, 161, 127, 110, 59, 185, 154, 146, 65, 172, 109, 132, 199, 126, 98, 100, 80, 156, 121, 128, 128, 128, 128, 128, 128, 128, 128, 160, 17, 219, 12, 218, 52, 168, 150, 218, 190, 182, 131, 155, 176, 106, 56, 244, 149, 20, 207, 164, 134, 67, 89, 132, 235, 1, 59, 125, 249, 238, 133, 197, 128, 128}, + {248, 107, 160, 57, 38, 219, 105, 170, 206, 213, 24, 233, 185, 240, 244, 52, 164, 115, 231, 23, 65, 9, 201, 67, 84, 139, 184, 242, 59, 228, 28, 167, 109, 154, 210, 184, 72, 248, 70, 128, 130, 39, 16, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, + Path: []byte{14, 9, 2, 6, 13, 11, 6, 9, 10, 10, 12, 14, 13, 5, 1, 8, 14, 9, 11, 9, 15, 0, 15, 4, 3, 4, 10, 4, 7, 3, 14, 7, 1, 7, 4, 1, 0, 9, 12, 9, 4, 3, 5, 4, 8, 11, 11, 8, 15, 2, 3, 11, 14, 4, 1, 12, 10, 7, 6, 13, 9, 10, 13, 2, 16}, + Storage: []statediff.StorageDiff{}, + }, + burnLeafKey: { + Key: burnLeafKey.Bytes(), + Value: burnAccount1, + Proof: [][]byte{{248, 113, 160, 87, 118, 82, 182, 37, 183, 123, 219, 91, 247, 123, 196, 63, 49, 37, 202, 215, 70, 77, 103, 157, 21, 117, 86, 82, 119, 211, 97, 27, 128, 83, 231, 128, 128, 128, 128, 160, 254, 136, 159, 16, 229, 219, 143, 44, 43, 243, 85, 146, 129, 82, 161, 127, 110, 59, 185, 154, 146, 65, 172, 109, 132, 199, 126, 98, 100, 80, 156, 121, 128, 128, 128, 128, 128, 128, 128, 128, 160, 17, 219, 12, 218, 52, 168, 150, 218, 190, 182, 131, 155, 176, 106, 56, 244, 149, 20, 207, 164, 134, 67, 89, 132, 235, 1, 59, 125, 249, 238, 133, 197, 128, 128}, + {248, 113, 160, 51, 128, 199, 183, 174, 129, 165, 142, 185, 141, 156, 120, 222, 74, 31, 215, 253, 149, 53, 252, 149, 62, 210, 190, 96, 45, 170, 164, 23, 103, 49, 42, 184, 78, 248, 76, 128, 136, 27, 193, 109, 103, 78, 200, 0, 0, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, + Path: []byte{5, 3, 8, 0, 12, 7, 11, 7, 10, 14, 8, 1, 10, 5, 8, 14, 11, 9, 8, 13, 9, 12, 7, 8, 13, 14, 4, 10, 1, 15, 13, 7, 15, 13, 9, 5, 3, 5, 15, 12, 9, 5, 3, 14, 13, 2, 11, 14, 6, 0, 2, 13, 10, 10, 10, 4, 1, 7, 6, 7, 3, 1, 2, 10, 16}, + Storage: []statediff.StorageDiff{}, + }, + }, + DeletedAccounts: emptyAccountDiffEventualMap, + UpdatedAccounts: statediff.AccountDiffsMap{ + testhelpers.BankLeafKey: { + Key: testhelpers.BankLeafKey.Bytes(), + Value: bankAccount1, + Proof: [][]byte{{248, 113, 160, 87, 118, 82, 182, 37, 183, 123, 219, 91, 247, 123, 196, 63, 49, 37, 202, 215, 70, 77, 103, 157, 21, 117, 86, 82, 119, 211, 97, 27, 128, 83, 231, 128, 128, 128, 128, 160, 254, 136, 159, 16, 229, 219, 143, 44, 43, 243, 85, 146, 129, 82, 161, 127, 110, 59, 185, 154, 146, 65, 172, 109, 132, 199, 126, 98, 100, 80, 156, 121, 128, 128, 128, 128, 128, 128, 128, 128, 160, 17, 219, 12, 218, 52, 168, 150, 218, 190, 182, 131, 155, 176, 106, 56, 244, 149, 20, 207, 164, 134, 67, 89, 132, 235, 1, 59, 125, 249, 238, 133, 197, 128, 128}, + {248, 109, 160, 48, 191, 73, 244, 64, 161, 205, 5, 39, 228, 208, 110, 39, 101, 101, 76, 15, 86, 69, 34, 87, 81, 109, 121, 58, 155, 141, 96, 77, 207, 223, 42, 184, 74, 248, 72, 1, 132, 5, 245, 185, 240, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}}, + Path: []byte{0, 0, 11, 15, 4, 9, 15, 4, 4, 0, 10, 1, 12, 13, 0, 5, 2, 7, 14, 4, 13, 0, 6, 14, 2, 7, 6, 5, 6, 5, 4, 12, 0, 15, 5, 6, 4, 5, 2, 2, 5, 7, 5, 1, 6, 13, 7, 9, 3, 10, 9, 11, 8, 13, 6, 0, 4, 13, 12, 15, 13, 15, 2, 10, 16}, + Storage: []statediff.StorageDiff{}, + }, + }, + } + expectedStateDiffRlp, _ := rlp.EncodeToBytes(expectedStateDiff) + select { + case payload := <-payloadChan: + if !bytes.Equal(payload.BlockRlp, expectedBlockRlp) { + t.Errorf("payload does not have expected block\r\actual: %v\r\nexpected: %v", payload.BlockRlp, expectedBlockRlp) + } + if !bytes.Equal(payload.StateDiffRlp, expectedStateDiffRlp) { + t.Errorf("payload does not have expected state diff\r\actual: %v\r\nexpected: %v", payload.StateDiffRlp, expectedStateDiffRlp) + } + case <-quitChan: + t.Errorf("channel quit before delivering payload") + } +} diff --git a/statediff/testhelpers/test_data.go b/statediff/testhelpers/test_data.go index 54c7b14ce..17dac8473 100644 --- a/statediff/testhelpers/test_data.go +++ b/statediff/testhelpers/test_data.go @@ -17,20 +17,24 @@ package testhelpers import ( - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/rlp" "math/big" "math/rand" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/statediff" ) +// AddressToLeafKey hashes an returns an address func AddressToLeafKey(address common.Address) common.Hash { return common.BytesToHash(crypto.Keccak256(address[:])) } +// Test variables var ( BlockNumber = rand.Int63() BlockHash = "0xfa40fbe2d98d98b3363a778d52f2bcd29d6790b9b3f3cab2b167fd12d3550f73" @@ -58,7 +62,7 @@ var ( Root: ContractRoot, CodeHash: CodeHash, } - valueBytes, err = rlp.EncodeToBytes(testAccount) + valueBytes, _ = rlp.EncodeToBytes(testAccount) CreatedAccountDiffs = statediff.AccountDiffsMap{ ContractLeafKey: { Key: ContractLeafKey.Bytes(), @@ -91,4 +95,20 @@ var ( DeletedAccounts: DeletedAccountDiffs, UpdatedAccounts: UpdatedAccountDiffs, } + Testdb = rawdb.NewMemoryDatabase() + + TestBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + TestBankAddress = crypto.PubkeyToAddress(TestBankKey.PublicKey) //0x71562b71999873DB5b286dF957af199Ec94617F7 + BankLeafKey = AddressToLeafKey(TestBankAddress) + TestBankFunds = big.NewInt(100000000) + Genesis = core.GenesisBlockForTesting(Testdb, TestBankAddress, TestBankFunds) + + Account1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + Account2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + Account1Addr = crypto.PubkeyToAddress(Account1Key.PublicKey) //0x703c4b2bD70c169f5717101CaeE543299Fc946C7 + Account2Addr = crypto.PubkeyToAddress(Account2Key.PublicKey) //0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e + Account1LeafKey = AddressToLeafKey(Account1Addr) + Account2LeafKey = AddressToLeafKey(Account2Addr) + ContractCode = common.Hex2Bytes("608060405234801561001057600080fd5b50602060405190810160405280600160ff16815250600090600161003592919061003b565b506100a5565b826064810192821561006f579160200282015b8281111561006e578251829060ff1690559160200191906001019061004e565b5b50905061007c9190610080565b5090565b6100a291905b8082111561009e576000816000905550600101610086565b5090565b90565b610124806100b46000396000f3fe6080604052348015600f57600080fd5b5060043610604f576000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146054578063c16431b9146093575b600080fd5b607d60048036036020811015606857600080fd5b810190808035906020019092919050505060c8565b6040518082815260200191505060405180910390f35b60c66004803603604081101560a757600080fd5b81019080803590602001909291908035906020019092919050505060e0565b005b6000808260648110151560d757fe5b01549050919050565b8060008360648110151560ef57fe5b0181905550505056fea165627a7a7230582064e918c3140a117bf3aa65865a9b9e83fae21ad1720506e7933b2a9f54bb40260029") + ContractAddr common.Address ) diff --git a/statediff/types.go b/statediff/types.go index 91c11335e..41e83dddb 100644 --- a/statediff/types.go +++ b/statediff/types.go @@ -21,12 +21,15 @@ package statediff import ( "encoding/json" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" ) -type AccountsMap map[common.Hash]*accountWrapper +// AccountsMap is a mapping of keccak256(address) => accountWrapper +type AccountsMap map[common.Hash]accountWrapper +// AccountWrapper is used to temporary associate the unpacked account with its raw values type accountWrapper struct { Account state.Account RawKey []byte @@ -35,8 +38,9 @@ type accountWrapper struct { Path []byte } +// StateDiff is the final output structure from the builder type StateDiff struct { - BlockNumber int64 `json:"blockNumber" gencodec:"required"` + BlockNumber int64 `json:"blockNumber" gencodec:"required"` BlockHash common.Hash `json:"blockHash" gencodec:"required"` CreatedAccounts AccountDiffsMap `json:"createdAccounts" gencodec:"required"` DeletedAccounts AccountDiffsMap `json:"deletedAccounts" gencodec:"required"` @@ -46,26 +50,28 @@ type StateDiff struct { err error } -func (self *StateDiff) ensureEncoded() { - if self.encoded == nil && self.err == nil { - self.encoded, self.err = json.Marshal(self) +func (sd *StateDiff) ensureEncoded() { + if sd.encoded == nil && sd.err == nil { + sd.encoded, sd.err = json.Marshal(sd) } } -// Implement Encoder interface for StateDiff +// Length to implement Encoder interface for StateDiff func (sd *StateDiff) Length() int { sd.ensureEncoded() return len(sd.encoded) } -// Implement Encoder interface for StateDiff +// Encode to implement Encoder interface for StateDiff func (sd *StateDiff) Encode() ([]byte, error) { sd.ensureEncoded() return sd.encoded, sd.err } +// AccountDiffsMap is a mapping of keccak256(address) => AccountDiff type AccountDiffsMap map[common.Hash]AccountDiff +// AccountDiff holds the data for a single state diff leaf node type AccountDiff struct { Key []byte `json:"key" gencodec:"required"` Value []byte `json:"value" gencodec:"required"` @@ -74,6 +80,7 @@ type AccountDiff struct { Path []byte `json:"path" gencodec:"required"` } +// StorageDiff holds the data for a single storage diff leaf node type StorageDiff struct { Key []byte `json:"key" gencodec:"required"` Value []byte `json:"value" gencodec:"required"` -- 2.45.2