Add statediff extractor and builder tests and small refactoring
This commit is contained in:
parent
a0053ee39b
commit
bf186e427c
@ -109,7 +109,7 @@ func (sdb *builder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, block
|
||||
}
|
||||
|
||||
func (sdb *builder) collectDiffNodes(a, b trie.NodeIterator) (map[common.Address]*state.Account, error) {
|
||||
var diffAccounts map[common.Address]*state.Account
|
||||
var diffAccounts = make(map[common.Address]*state.Account)
|
||||
it, _ := trie.NewDifferenceIterator(a, b)
|
||||
|
||||
for {
|
||||
@ -161,15 +161,15 @@ func (sdb *builder) buildDiffEventual(accounts map[common.Address]*state.Account
|
||||
hexRoot := val.Root.Hex()
|
||||
|
||||
if created {
|
||||
nonce := diffUint64{
|
||||
nonce := DiffUint64{
|
||||
NewValue: &val.Nonce,
|
||||
}
|
||||
|
||||
balance := diffBigInt{
|
||||
balance := DiffBigInt{
|
||||
NewValue: val.Balance,
|
||||
}
|
||||
|
||||
contractRoot := diffString{
|
||||
contractRoot := DiffString{
|
||||
NewValue: &hexRoot,
|
||||
}
|
||||
accountDiffs[addr] = AccountDiffEventual{
|
||||
@ -181,13 +181,13 @@ func (sdb *builder) buildDiffEventual(accounts map[common.Address]*state.Account
|
||||
Storage: storageDiffs,
|
||||
}
|
||||
} else {
|
||||
nonce := diffUint64{
|
||||
nonce := DiffUint64{
|
||||
OldValue: &val.Nonce,
|
||||
}
|
||||
balance := diffBigInt{
|
||||
balance := DiffBigInt{
|
||||
OldValue: val.Balance,
|
||||
}
|
||||
contractRoot := diffString{
|
||||
contractRoot := DiffString{
|
||||
OldValue: &hexRoot,
|
||||
}
|
||||
accountDiffs[addr] = AccountDiffEventual{
|
||||
@ -214,12 +214,12 @@ func (sdb *builder) buildDiffIncremental(creations map[common.Address]*state.Acc
|
||||
log.Error("Failed building storage diffs", "Address", val, "error", err)
|
||||
return nil, err
|
||||
} else {
|
||||
nonce := diffUint64{
|
||||
nonce := DiffUint64{
|
||||
NewValue: &createdAcc.Nonce,
|
||||
OldValue: &deletedAcc.Nonce,
|
||||
}
|
||||
|
||||
balance := diffBigInt{
|
||||
balance := DiffBigInt{
|
||||
NewValue: createdAcc.Balance,
|
||||
OldValue: deletedAcc.Balance,
|
||||
}
|
||||
@ -227,7 +227,7 @@ func (sdb *builder) buildDiffIncremental(creations map[common.Address]*state.Acc
|
||||
|
||||
nHexRoot := createdAcc.Root.Hex()
|
||||
oHexRoot := deletedAcc.Root.Hex()
|
||||
contractRoot := diffString{
|
||||
contractRoot := DiffString{
|
||||
NewValue: &nHexRoot,
|
||||
OldValue: &oHexRoot,
|
||||
}
|
||||
@ -246,14 +246,14 @@ func (sdb *builder) buildDiffIncremental(creations map[common.Address]*state.Acc
|
||||
return updatedAccounts, nil
|
||||
}
|
||||
|
||||
func (sdb *builder) buildStorageDiffsEventual(sr common.Hash, creation bool) (map[string]diffString, error) {
|
||||
func (sdb *builder) buildStorageDiffsEventual(sr common.Hash, creation bool) (map[string]DiffString, error) {
|
||||
log.Debug("Storage Root For Eventual Diff", "root", sr.Hex())
|
||||
sTrie, err := trie.New(sr, sdb.trieDB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
it := sTrie.NodeIterator(make([]byte, 0))
|
||||
storageDiffs := make(map[string]diffString)
|
||||
storageDiffs := make(map[string]DiffString)
|
||||
for {
|
||||
log.Debug("Iterating over state at path ", "path", pathToStr(it))
|
||||
if it.Leaf() {
|
||||
@ -261,9 +261,9 @@ func (sdb *builder) buildStorageDiffsEventual(sr common.Hash, creation bool) (ma
|
||||
path := pathToStr(it)
|
||||
value := common.ToHex(it.LeafBlob())
|
||||
if creation {
|
||||
storageDiffs[path] = diffString{NewValue: &value}
|
||||
storageDiffs[path] = DiffString{NewValue: &value}
|
||||
} else {
|
||||
storageDiffs[path] = diffString{OldValue: &value}
|
||||
storageDiffs[path] = DiffString{OldValue: &value}
|
||||
}
|
||||
}
|
||||
cont := it.Next(true)
|
||||
@ -274,7 +274,7 @@ func (sdb *builder) buildStorageDiffsEventual(sr common.Hash, creation bool) (ma
|
||||
return storageDiffs, nil
|
||||
}
|
||||
|
||||
func (sdb *builder) buildStorageDiffsIncremental(oldSR common.Hash, newSR common.Hash) (map[string]diffString, error) {
|
||||
func (sdb *builder) buildStorageDiffsIncremental(oldSR common.Hash, newSR common.Hash) (map[string]DiffString, error) {
|
||||
log.Debug("Storage Roots for Incremental Diff", "old", oldSR.Hex(), "new", newSR.Hex())
|
||||
oldTrie, err := trie.New(oldSR, sdb.trieDB)
|
||||
if err != nil {
|
||||
@ -288,7 +288,7 @@ func (sdb *builder) buildStorageDiffsIncremental(oldSR common.Hash, newSR common
|
||||
oldIt := oldTrie.NodeIterator(make([]byte, 0))
|
||||
newIt := newTrie.NodeIterator(make([]byte, 0))
|
||||
it, _ := trie.NewDifferenceIterator(oldIt, newIt)
|
||||
storageDiffs := make(map[string]diffString)
|
||||
storageDiffs := make(map[string]DiffString)
|
||||
for {
|
||||
if it.Leaf() {
|
||||
log.Debug("Found leaf in storage", "path", pathToStr(it))
|
||||
@ -298,7 +298,7 @@ func (sdb *builder) buildStorageDiffsIncremental(oldSR common.Hash, newSR common
|
||||
log.Error("Failed to look up value in oldTrie", "path", path, "error", err)
|
||||
} else {
|
||||
hexOldVal := common.ToHex(oldVal)
|
||||
storageDiffs[path] = diffString{OldValue: &hexOldVal, NewValue: &value}
|
||||
storageDiffs[path] = DiffString{OldValue: &hexOldVal, NewValue: &value}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,3 +19,302 @@
|
||||
|
||||
package statediff_test
|
||||
|
||||
import (
|
||||
"github.com/onsi/ginkgo"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"math/big"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/statediff"
|
||||
"github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
|
||||
var (
|
||||
testdb = ethdb.NewMemDatabase()
|
||||
|
||||
testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) //0x71562b71999873DB5b286dF957af199Ec94617F7
|
||||
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
|
||||
|
||||
contractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056")
|
||||
contractAddr common.Address
|
||||
|
||||
emptyAccountDiffEventualMap = make(map[common.Address]statediff.AccountDiffEventual)
|
||||
emptyAccountDiffIncrementalMap = make(map[common.Address]statediff.AccountDiffIncremental)
|
||||
)
|
||||
/*
|
||||
contract test {
|
||||
|
||||
uint256[100] data;
|
||||
|
||||
function Put(uint256 addr, uint256 value) {
|
||||
data[addr] = value;
|
||||
}
|
||||
|
||||
function Get(uint256 addr) constant returns (uint256 value) {
|
||||
return data[addr];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// 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, seed byte, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block) {
|
||||
blocks, _ := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testdb, n, testChainGen)
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
var _ = ginkgo.FDescribe("", func() {
|
||||
var (
|
||||
block0Hash, block1Hash, block2Hash, block3Hash common.Hash
|
||||
block0, block1, block2, block3 *types.Block
|
||||
builder statediff.Builder
|
||||
miningReward = int64(3000000000000000000)
|
||||
burnAddress = common.HexToAddress("0x0")
|
||||
diff *statediff.StateDiff
|
||||
err error
|
||||
)
|
||||
|
||||
ginkgo.BeforeEach(func() {
|
||||
_, blocks := makeChain(3, 0, genesis)
|
||||
block0Hash = common.HexToHash("0xd1721cfd0b29c36fd7a68f25c128e86413fb666a6e1d68e89b875bd299262661")
|
||||
block1Hash = common.HexToHash("0x47c398dd688eaa4dd11b006888156783fe32df83d59b197c0fcd303408103d39")
|
||||
block2Hash = common.HexToHash("0x351b2f531838683ba457e8ca4d3a844cc48147dceafbcb589dc6e3227856ee75")
|
||||
block3Hash = common.HexToHash("0xfa40fbe2d98d98b3363a778d52f2bcd29d6790b9b3f3cab2b167fd12d3550f73")
|
||||
|
||||
block0 = blocks[block0Hash]
|
||||
block1 = blocks[block1Hash]
|
||||
block2 = blocks[block2Hash]
|
||||
block3 = blocks[block3Hash]
|
||||
builder = statediff.NewBuilder(testdb)
|
||||
})
|
||||
|
||||
ginkgo.It("returns empty account diff collections when the state root hasn't changed", func() {
|
||||
expectedDiff := statediff.StateDiff{
|
||||
BlockNumber: block0.Number().Int64(),
|
||||
BlockHash: block0Hash,
|
||||
CreatedAccounts: emptyAccountDiffEventualMap,
|
||||
DeletedAccounts: emptyAccountDiffEventualMap,
|
||||
UpdatedAccounts: emptyAccountDiffIncrementalMap,
|
||||
}
|
||||
|
||||
diff, err := builder.BuildStateDiff(block0.Root(), block0.Root(), block0.Number().Int64(), block0Hash)
|
||||
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
gomega.Expect(diff).To(gomega.Equal(&expectedDiff))
|
||||
})
|
||||
|
||||
ginkgo.Context("Block 1", func() {
|
||||
//10000 transferred from testBankAddress to account1Addr
|
||||
var balanceChange = int64(10000)
|
||||
|
||||
ginkgo.BeforeEach(func() {
|
||||
diff, err = builder.BuildStateDiff(block0.Root(), block1.Root(), block1.Number().Int64(), block1Hash)
|
||||
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
})
|
||||
|
||||
ginkgo.It("includes the block number and hash", func() {
|
||||
gomega.Expect(diff.BlockNumber).To(gomega.Equal(block1.Number().Int64()))
|
||||
gomega.Expect(diff.BlockHash).To(gomega.Equal(block1Hash))
|
||||
})
|
||||
|
||||
ginkgo.It("returns an empty collection for deleted accounts", func() {
|
||||
gomega.Expect(diff.DeletedAccounts).To(gomega.Equal(emptyAccountDiffEventualMap))
|
||||
})
|
||||
|
||||
ginkgo.It("returns balance diffs for updated accounts", func() {
|
||||
expectedBankBalanceDiff := statediff.DiffBigInt{
|
||||
NewValue: big.NewInt(testBankFunds.Int64() - balanceChange),
|
||||
OldValue: testBankFunds,
|
||||
}
|
||||
|
||||
gomega.Expect(len(diff.UpdatedAccounts)).To(gomega.Equal(1))
|
||||
gomega.Expect(diff.UpdatedAccounts[testBankAddress].Balance).To(gomega.Equal(expectedBankBalanceDiff))
|
||||
})
|
||||
|
||||
ginkgo.It("returns balance diffs for new accounts", func() {
|
||||
expectedAccount1BalanceDiff := statediff.DiffBigInt{
|
||||
NewValue: big.NewInt(balanceChange),
|
||||
OldValue: nil,
|
||||
}
|
||||
|
||||
expectedBurnAddrBalanceDiff := statediff.DiffBigInt{
|
||||
NewValue: big.NewInt(miningReward),
|
||||
OldValue: nil,
|
||||
}
|
||||
|
||||
gomega.Expect(len(diff.CreatedAccounts)).To(gomega.Equal(2))
|
||||
gomega.Expect(diff.CreatedAccounts[account1Addr].Balance).To(gomega.Equal(expectedAccount1BalanceDiff))
|
||||
gomega.Expect(diff.CreatedAccounts[burnAddress].Balance).To(gomega.Equal(expectedBurnAddrBalanceDiff))
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("Block 2", func() {
|
||||
//1000 transferred from testBankAddress to account1Addr
|
||||
//1000 transferred from account1Addr to account2Addr
|
||||
var (
|
||||
balanceChange = int64(1000)
|
||||
block1BankBalance = int64(99990000)
|
||||
block1Account1Balance = int64(10000)
|
||||
)
|
||||
|
||||
ginkgo.BeforeEach(func() {
|
||||
diff, err = builder.BuildStateDiff(block1.Root(), block2.Root(), block2.Number().Int64(), block2Hash)
|
||||
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
})
|
||||
|
||||
ginkgo.It("includes the block number and hash", func() {
|
||||
gomega.Expect(diff.BlockNumber).To(gomega.Equal(block2.Number().Int64()))
|
||||
gomega.Expect(diff.BlockHash).To(gomega.Equal(block2Hash))
|
||||
})
|
||||
|
||||
ginkgo.It("returns an empty collection for deleted accounts", func() {
|
||||
gomega.Expect(diff.DeletedAccounts).To(gomega.Equal(emptyAccountDiffEventualMap))
|
||||
})
|
||||
|
||||
ginkgo.It("returns balance diffs for updated accounts", func() {
|
||||
expectedBankBalanceDiff := statediff.DiffBigInt{
|
||||
NewValue: big.NewInt(block1BankBalance - balanceChange),
|
||||
OldValue: big.NewInt(block1BankBalance),
|
||||
}
|
||||
|
||||
expectedAccount1BalanceDiff := statediff.DiffBigInt{
|
||||
NewValue: big.NewInt(block1Account1Balance - balanceChange + balanceChange),
|
||||
OldValue: big.NewInt(block1Account1Balance),
|
||||
}
|
||||
|
||||
expectedBurnBalanceDiff := statediff.DiffBigInt{
|
||||
NewValue: big.NewInt(miningReward + miningReward),
|
||||
OldValue: big.NewInt(miningReward),
|
||||
}
|
||||
|
||||
gomega.Expect(len(diff.UpdatedAccounts)).To(gomega.Equal(3))
|
||||
gomega.Expect(diff.UpdatedAccounts[testBankAddress].Balance).To(gomega.Equal(expectedBankBalanceDiff))
|
||||
gomega.Expect(diff.UpdatedAccounts[account1Addr].Balance).To(gomega.Equal(expectedAccount1BalanceDiff))
|
||||
gomega.Expect(diff.UpdatedAccounts[burnAddress].Balance).To(gomega.Equal(expectedBurnBalanceDiff))
|
||||
})
|
||||
|
||||
ginkgo.It("returns balance diffs for new accounts", func() {
|
||||
expectedAccount2BalanceDiff := statediff.DiffBigInt{
|
||||
NewValue: big.NewInt(balanceChange),
|
||||
OldValue: nil,
|
||||
}
|
||||
|
||||
expectedContractBalanceDiff := statediff.DiffBigInt{
|
||||
NewValue: big.NewInt(0),
|
||||
OldValue: nil,
|
||||
}
|
||||
|
||||
gomega.Expect(len(diff.CreatedAccounts)).To(gomega.Equal(2))
|
||||
gomega.Expect(diff.CreatedAccounts[account2Addr].Balance).To(gomega.Equal(expectedAccount2BalanceDiff))
|
||||
gomega.Expect(diff.CreatedAccounts[contractAddr].Balance).To(gomega.Equal(expectedContractBalanceDiff))
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("Block 3", func() {
|
||||
//the contract's storage is changed
|
||||
//and the block is mined by account 2
|
||||
ginkgo.BeforeEach(func() {
|
||||
diff, err = builder.BuildStateDiff(block2.Root(), block3.Root(), block3.Number().Int64(), block3Hash)
|
||||
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
})
|
||||
|
||||
ginkgo.It("includes the block number and hash", func() {
|
||||
gomega.Expect(diff.BlockNumber).To(gomega.Equal(block3.Number().Int64()))
|
||||
gomega.Expect(diff.BlockHash).To(gomega.Equal(block3Hash))
|
||||
})
|
||||
|
||||
ginkgo.It("returns an empty collection for deleted accounts", func() {
|
||||
gomega.Expect(diff.DeletedAccounts).To(gomega.Equal(emptyAccountDiffEventualMap))
|
||||
})
|
||||
|
||||
ginkgo.It("returns an empty collection for created accounts", func() {
|
||||
gomega.Expect(diff.CreatedAccounts).To(gomega.Equal(emptyAccountDiffEventualMap))
|
||||
})
|
||||
|
||||
ginkgo.It("returns balance, storage and nonce diffs for updated accounts", func() {
|
||||
block2Account2Balance := int64(1000)
|
||||
expectedAcct2BalanceDiff := statediff.DiffBigInt{
|
||||
NewValue: big.NewInt(block2Account2Balance + miningReward),
|
||||
OldValue: big.NewInt(block2Account2Balance),
|
||||
}
|
||||
|
||||
expectedContractStorageDiff := make(map[string]statediff.DiffString)
|
||||
newVal := "0x03"
|
||||
oldVal := "0x0"
|
||||
path := "0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"
|
||||
expectedContractStorageDiff[path] = statediff.DiffString{
|
||||
NewValue: &newVal,
|
||||
OldValue: &oldVal,
|
||||
}
|
||||
|
||||
oldNonce := uint64(2)
|
||||
newNonce := uint64(3)
|
||||
expectedBankNonceDiff := statediff.DiffUint64{
|
||||
NewValue: &newNonce,
|
||||
OldValue: &oldNonce,
|
||||
}
|
||||
|
||||
gomega.Expect(len(diff.UpdatedAccounts)).To(gomega.Equal(3))
|
||||
gomega.Expect(diff.UpdatedAccounts[account2Addr].Balance).To(gomega.Equal(expectedAcct2BalanceDiff))
|
||||
gomega.Expect(diff.UpdatedAccounts[contractAddr].Storage).To(gomega.Equal(expectedContractStorageDiff))
|
||||
gomega.Expect(diff.UpdatedAccounts[testBankAddress].Nonce).To(gomega.Equal(expectedBankNonceDiff))
|
||||
})
|
||||
})
|
||||
})
|
@ -21,7 +21,6 @@ package statediff
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
|
||||
type Extractor interface {
|
||||
@ -29,27 +28,22 @@ type Extractor interface {
|
||||
}
|
||||
|
||||
type extractor struct {
|
||||
*builder // Interface for building state diff objects from two blocks
|
||||
*publisher // Interface for publishing state diff objects to a datastore (e.g. IPFS)
|
||||
Builder Builder // Interface for building state diff objects from two blocks
|
||||
Publisher Publisher // Interface for publishing state diff objects to a datastore (e.g. IPFS)
|
||||
}
|
||||
|
||||
func NewExtractor(db ethdb.Database, config Config) (*extractor, error) {
|
||||
publisher, err := NewPublisher(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func NewExtractor(builder Builder, publisher Publisher) (*extractor, error) {
|
||||
return &extractor{
|
||||
builder: NewBuilder(db),
|
||||
publisher: publisher,
|
||||
Builder: builder,
|
||||
Publisher: publisher,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *extractor) ExtractStateDiff(parent, current types.Block) (string, error) {
|
||||
stateDiff, err := e.BuildStateDiff(parent.Root(), current.Root(), current.Number().Int64(), current.Hash())
|
||||
stateDiff, err := e.Builder.BuildStateDiff(parent.Root(), current.Root(), current.Number().Int64(), current.Hash())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return e.PublishStateDiff(stateDiff)
|
||||
return e.Publisher.PublishStateDiff(stateDiff)
|
||||
}
|
@ -17,4 +17,83 @@
|
||||
// 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_test
|
||||
package statediff_test
|
||||
|
||||
import (
|
||||
"github.com/onsi/ginkgo"
|
||||
"github.com/ethereum/go-ethereum/statediff"
|
||||
"github.com/onsi/gomega"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"math/rand"
|
||||
"github.com/ethereum/go-ethereum/statediff/testhelpers"
|
||||
"math/big"
|
||||
)
|
||||
var _ = ginkgo.Describe("Extractor", func() {
|
||||
var publisher testhelpers.MockPublisher
|
||||
var builder testhelpers.MockBuilder
|
||||
var currentBlockNumber *big.Int
|
||||
var parentBlock, currentBlock *types.Block
|
||||
var expectedStateDiff statediff.StateDiff
|
||||
var extractor statediff.Extractor
|
||||
var err error
|
||||
|
||||
ginkgo.BeforeEach(func() {
|
||||
publisher = testhelpers.MockPublisher{}
|
||||
builder = testhelpers.MockBuilder{}
|
||||
extractor, err = statediff.NewExtractor(&builder, &publisher)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
|
||||
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 = statediff.StateDiff{
|
||||
BlockNumber: blockNumber,
|
||||
BlockHash: currentBlock.Hash(),
|
||||
CreatedAccounts: nil,
|
||||
DeletedAccounts: nil,
|
||||
UpdatedAccounts: nil,
|
||||
}
|
||||
})
|
||||
|
||||
ginkgo.It("builds a state diff struct", func() {
|
||||
builder.SetStateDiffToBuild(&expectedStateDiff)
|
||||
|
||||
_, err = extractor.ExtractStateDiff(*parentBlock, *currentBlock)
|
||||
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
gomega.Expect(builder.OldStateRoot).To(gomega.Equal(parentBlock.Root()))
|
||||
gomega.Expect(builder.NewStateRoot).To(gomega.Equal(currentBlock.Root()))
|
||||
gomega.Expect(builder.BlockNumber).To(gomega.Equal(currentBlockNumber.Int64()))
|
||||
gomega.Expect(builder.BlockHash).To(gomega.Equal(currentBlock.Hash()))
|
||||
})
|
||||
|
||||
ginkgo.It("returns an error if building the state diff fails", func() {
|
||||
builder.SetBuilderError(testhelpers.MockError)
|
||||
|
||||
_, err = extractor.ExtractStateDiff(*parentBlock, *currentBlock)
|
||||
|
||||
gomega.Expect(err).To(gomega.HaveOccurred())
|
||||
gomega.Expect(err).To(gomega.MatchError(testhelpers.MockError))
|
||||
})
|
||||
|
||||
ginkgo.It("publishes the state diff struct", func() {
|
||||
builder.SetStateDiffToBuild(&expectedStateDiff)
|
||||
|
||||
_, err = extractor.ExtractStateDiff(*parentBlock, *currentBlock)
|
||||
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
gomega.Expect(publisher.StateDiff).To(gomega.Equal(&expectedStateDiff))
|
||||
})
|
||||
|
||||
ginkgo.It("returns an error if publishing the diff fails", func() {
|
||||
publisher.SetPublisherError(testhelpers.MockError)
|
||||
|
||||
_, err = extractor.ExtractStateDiff(*parentBlock, *currentBlock)
|
||||
|
||||
gomega.Expect(err).To(gomega.HaveOccurred())
|
||||
gomega.Expect(err).To(gomega.MatchError(testhelpers.MockError))
|
||||
})
|
||||
})
|
||||
|
@ -17,7 +17,13 @@ type StateDiffService struct {
|
||||
|
||||
func NewStateDiffService(db ethdb.Database, blockChain *core.BlockChain) (*StateDiffService, error) {
|
||||
config := Config{}
|
||||
extractor, _ := NewExtractor(db, config)
|
||||
builder := NewBuilder(db)
|
||||
publisher, err := NewPublisher(config)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
extractor, _ := NewExtractor(builder, publisher)
|
||||
return &StateDiffService{
|
||||
blockchain: blockChain,
|
||||
extractor: extractor,
|
||||
|
13
statediff/statediff_suite_test.go
Normal file
13
statediff/statediff_suite_test.go
Normal file
@ -0,0 +1,13 @@
|
||||
package statediff_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestStatediff(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Statediff Suite")
|
||||
}
|
@ -56,31 +56,31 @@ func (sd *StateDiff) Encode() ([]byte, error) {
|
||||
}
|
||||
|
||||
type AccountDiffEventual struct {
|
||||
Nonce diffUint64 `json:"nonce" gencodec:"required"`
|
||||
Balance diffBigInt `json:"balance" gencodec:"required"`
|
||||
Nonce DiffUint64 `json:"nonce" gencodec:"required"`
|
||||
Balance DiffBigInt `json:"balance" gencodec:"required"`
|
||||
Code []byte `json:"code" gencodec:"required"`
|
||||
CodeHash string `json:"codeHash" gencodec:"required"`
|
||||
ContractRoot diffString `json:"contractRoot" gencodec:"required"`
|
||||
Storage map[string]diffString `json:"storage" gencodec:"required"`
|
||||
ContractRoot DiffString `json:"contractRoot" gencodec:"required"`
|
||||
Storage map[string]DiffString `json:"storage" gencodec:"required"`
|
||||
}
|
||||
|
||||
type AccountDiffIncremental struct {
|
||||
Nonce diffUint64 `json:"nonce" gencodec:"required"`
|
||||
Balance diffBigInt `json:"balance" gencodec:"required"`
|
||||
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]diffString `json:"storage" gencodec:"required"`
|
||||
ContractRoot DiffString `json:"contractRoot" gencodec:"required"`
|
||||
Storage map[string]DiffString `json:"storage" gencodec:"required"`
|
||||
}
|
||||
|
||||
type diffString struct {
|
||||
type DiffString struct {
|
||||
NewValue *string `json:"newValue" gencodec:"optional"`
|
||||
OldValue *string `json:"oldValue" gencodec:"optional"`
|
||||
}
|
||||
type diffUint64 struct {
|
||||
type DiffUint64 struct {
|
||||
NewValue *uint64 `json:"newValue" gencodec:"optional"`
|
||||
OldValue *uint64 `json:"oldValue" gencodec:"optional"`
|
||||
}
|
||||
type diffBigInt struct {
|
||||
type DiffBigInt struct {
|
||||
NewValue *big.Int `json:"newValue" gencodec:"optional"`
|
||||
OldValue *big.Int `json:"oldValue" gencodec:"optional"`
|
||||
}
|
||||
|
49
statediff/testhelpers/mocks.go
Normal file
49
statediff/testhelpers/mocks.go
Normal file
@ -0,0 +1,49 @@
|
||||
package testhelpers
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/statediff"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var MockError = errors.New("mock error")
|
||||
|
||||
type MockBuilder struct {
|
||||
OldStateRoot common.Hash
|
||||
NewStateRoot common.Hash
|
||||
BlockNumber int64
|
||||
BlockHash common.Hash
|
||||
stateDiff *statediff.StateDiff
|
||||
builderError error
|
||||
}
|
||||
|
||||
func (builder *MockBuilder) BuildStateDiff(oldStateRoot, newStateRoot common.Hash, blockNumber int64, blockHash common.Hash) (*statediff.StateDiff, error) {
|
||||
builder.OldStateRoot = oldStateRoot
|
||||
builder.NewStateRoot = newStateRoot
|
||||
builder.BlockNumber = blockNumber
|
||||
builder.BlockHash = blockHash
|
||||
|
||||
return builder.stateDiff, builder.builderError
|
||||
}
|
||||
|
||||
func (builder *MockBuilder) SetStateDiffToBuild(stateDiff *statediff.StateDiff) {
|
||||
builder.stateDiff = stateDiff
|
||||
}
|
||||
|
||||
func (builder *MockBuilder) SetBuilderError(err error) {
|
||||
builder.builderError = err
|
||||
}
|
||||
|
||||
type MockPublisher struct{
|
||||
StateDiff *statediff.StateDiff
|
||||
publisherError error
|
||||
}
|
||||
|
||||
func (publisher *MockPublisher) PublishStateDiff(sd *statediff.StateDiff) (string, error) {
|
||||
publisher.StateDiff = sd
|
||||
return "", publisher.publisherError
|
||||
}
|
||||
|
||||
func (publisher *MockPublisher) SetPublisherError(err error) {
|
||||
publisher.publisherError = err
|
||||
}
|
Loading…
Reference in New Issue
Block a user