Skip to content

Commit

Permalink
Export get diff accounts in block api (#431)
Browse files Browse the repository at this point in the history
* support get diff accounts

Signed-off-by: Keefe-Liu <[email protected]>

* add testcase for diff accounts

Signed-off-by: Keefe-Liu <[email protected]>
  • Loading branch information
keefel authored Oct 8, 2021
1 parent 1ded097 commit b2f1d25
Show file tree
Hide file tree
Showing 9 changed files with 495 additions and 24 deletions.
36 changes: 36 additions & 0 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,42 @@ func (bc *BlockChain) GetDiffLayerRLP(blockHash common.Hash) rlp.RawValue {
return rawData
}

func (bc *BlockChain) GetDiffAccounts(blockHash common.Hash) ([]common.Address, error) {
var (
accounts []common.Address
diffLayer *types.DiffLayer
)

header := bc.GetHeaderByHash(blockHash)
if header == nil {
return nil, fmt.Errorf("no block found")
}

if cached, ok := bc.diffLayerCache.Get(blockHash); ok {
diffLayer = cached.(*types.DiffLayer)
} else if diffStore := bc.db.DiffStore(); diffStore != nil {
diffLayer = rawdb.ReadDiffLayer(diffStore, blockHash)
}

if diffLayer == nil {
if header.TxHash != types.EmptyRootHash {
return nil, fmt.Errorf("no diff layer found")
}

return nil, nil
}

for _, diffAccounts := range diffLayer.Accounts {
accounts = append(accounts, diffAccounts.Account)
}

if header.TxHash != types.EmptyRootHash && len(accounts) == 0 {
return nil, fmt.Errorf("no diff account in block, maybe bad diff layer")
}

return accounts, nil
}

// HasBlock checks if a block is fully present in the database or not.
func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool {
if bc.blockCache.Contains(hash) {
Expand Down
174 changes: 158 additions & 16 deletions core/blockchain_diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,79 @@ var (
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
// testAddr is the Ethereum address of the tester account.
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
// testBlocks is the test parameters array for specific blocks.
testBlocks = []testBlockParam{
{
// This txs params also used to default block.
blockNr: 11,
txs: []testTransactionParam{
{
to: common.Address{0x01},
value: big.NewInt(1),
gasPrice: big.NewInt(1),
data: nil,
},
},
},
{
blockNr: 12,
txs: []testTransactionParam{
{
to: common.Address{0x01},
value: big.NewInt(1),
gasPrice: big.NewInt(1),
data: nil,
},
{
to: common.Address{0x02},
value: big.NewInt(2),
gasPrice: big.NewInt(2),
data: nil,
},
},
},
{
blockNr: 13,
txs: []testTransactionParam{
{
to: common.Address{0x01},
value: big.NewInt(1),
gasPrice: big.NewInt(1),
data: nil,
},
{
to: common.Address{0x02},
value: big.NewInt(2),
gasPrice: big.NewInt(2),
data: nil,
},
{
to: common.Address{0x03},
value: big.NewInt(3),
gasPrice: big.NewInt(3),
data: nil,
},
},
},
{
blockNr: 14,
txs: []testTransactionParam{},
},
}
)

type testTransactionParam struct {
to common.Address
value *big.Int
gasPrice *big.Int
data []byte
}

type testBlockParam struct {
blockNr int
txs []testTransactionParam
}

// testBackend is a mock implementation of the live Ethereum message handler. Its
// purpose is to allow testing the request/reply workflows and wire serialization
// in the `eth` protocol without actually doing any data processing.
Expand Down Expand Up @@ -78,13 +149,35 @@ func newTestBackendWithGenerator(blocks int, lightProcess bool) *testBackend {
// lets unset (nil). Set it here to the correct value.
block.SetCoinbase(testAddr)

// We want to simulate an empty middle block, having the same state as the
// first one. The last is needs a state change again to force a reorg.
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), common.Address{0x01}, big.NewInt(1), params.TxGas, big.NewInt(1), nil), signer, testKey)
if err != nil {
panic(err)
for idx, testBlock := range testBlocks {
// Specific block setting, the index in this generator has 1 diff from specified blockNr.
if i+1 == testBlock.blockNr {
for _, testTransaction := range testBlock.txs {
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), testTransaction.to,
testTransaction.value, params.TxGas, testTransaction.gasPrice, testTransaction.data), signer, testKey)
if err != nil {
panic(err)
}
block.AddTxWithChain(chain, tx)
}
break
}

// Default block setting.
if idx == len(testBlocks)-1 {
// We want to simulate an empty middle block, having the same state as the
// first one. The last is needs a state change again to force a reorg.
for _, testTransaction := range testBlocks[0].txs {
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), testTransaction.to,
testTransaction.value, params.TxGas, testTransaction.gasPrice, testTransaction.data), signer, testKey)
if err != nil {
panic(err)
}
block.AddTxWithChain(chain, tx)
}
}
}
block.AddTxWithChain(chain, tx)

}
bs, _ := GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, generator)
if _, err := chain.InsertChain(bs); err != nil {
Expand Down Expand Up @@ -139,12 +232,17 @@ func TestProcessDiffLayer(t *testing.T) {
}
blockHash := block.Hash()
rawDiff := fullBackend.chain.GetDiffLayerRLP(blockHash)
diff, err := rawDataToDiffLayer(rawDiff)
if err != nil {
t.Errorf("failed to decode rawdata %v", err)
if len(rawDiff) != 0 {
diff, err := rawDataToDiffLayer(rawDiff)
if err != nil {
t.Errorf("failed to decode rawdata %v", err)
}
if diff == nil {
continue
}
lightBackend.Chain().HandleDiffLayer(diff, "testpid", true)
}
lightBackend.Chain().HandleDiffLayer(diff, "testpid", true)
_, err = lightBackend.chain.insertChain([]*types.Block{block}, true)
_, err := lightBackend.chain.insertChain([]*types.Block{block}, true)
if err != nil {
t.Errorf("failed to insert block %v", err)
}
Expand Down Expand Up @@ -186,7 +284,8 @@ func TestFreezeDiffLayer(t *testing.T) {
blockNum := 1024
fullBackend := newTestBackend(blockNum, true)
defer fullBackend.close()
if fullBackend.chain.diffQueue.Size() != blockNum {
// Minus one empty block.
if fullBackend.chain.diffQueue.Size() != blockNum-1 {
t.Errorf("size of diff queue is wrong, expected: %d, get: %d", blockNum, fullBackend.chain.diffQueue.Size())
}
time.Sleep(diffLayerFreezerRecheckInterval + 1*time.Second)
Expand Down Expand Up @@ -215,10 +314,11 @@ func TestPruneDiffLayer(t *testing.T) {
for num := uint64(1); num < uint64(blockNum); num++ {
header := fullBackend.chain.GetHeaderByNumber(num)
rawDiff := fullBackend.chain.GetDiffLayerRLP(header.Hash())
diff, _ := rawDataToDiffLayer(rawDiff)
fullBackend.Chain().HandleDiffLayer(diff, "testpid1", true)
fullBackend.Chain().HandleDiffLayer(diff, "testpid2", true)

if len(rawDiff) != 0 {
diff, _ := rawDataToDiffLayer(rawDiff)
fullBackend.Chain().HandleDiffLayer(diff, "testpid1", true)
fullBackend.Chain().HandleDiffLayer(diff, "testpid2", true)
}
}
fullBackend.chain.pruneDiffLayer()
if len(fullBackend.chain.diffNumToBlockHashes) != maxDiffForkDist {
Expand Down Expand Up @@ -261,3 +361,45 @@ func TestPruneDiffLayer(t *testing.T) {
}

}

func TestGetDiffAccounts(t *testing.T) {
t.Parallel()

blockNum := 128
fullBackend := newTestBackend(blockNum, false)
defer fullBackend.close()

for _, testBlock := range testBlocks {
block := fullBackend.chain.GetBlockByNumber(uint64(testBlock.blockNr))
if block == nil {
t.Fatal("block should not be nil")
}
blockHash := block.Hash()
accounts, err := fullBackend.chain.GetDiffAccounts(blockHash)
if err != nil {
t.Errorf("get diff accounts eror for block number (%d): %v", testBlock.blockNr, err)
}

for idx, account := range accounts {
if testAddr == account {
break
}

if idx == len(accounts)-1 {
t.Errorf("the diff accounts does't include addr: %v", testAddr)
}
}

for _, transaction := range testBlock.txs {
for idx, account := range accounts {
if transaction.to == account {
break
}

if idx == len(accounts)-1 {
t.Errorf("the diff accounts does't include addr: %v", transaction.to)
}
}
}
}
}
11 changes: 11 additions & 0 deletions core/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,3 +453,14 @@ type DiffStorage struct {
Keys []string
Vals [][]byte
}

type DiffAccountsInTx struct {
TxHash common.Hash
Accounts map[common.Address]*big.Int
}

type DiffAccountsInBlock struct {
Number uint64
BlockHash common.Hash
Transactions []DiffAccountsInTx
}
4 changes: 4 additions & 0 deletions eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,10 @@ func (b *EthAPIBackend) SuggestPrice(ctx context.Context) (*big.Int, error) {
return b.gpo.SuggestPrice(ctx)
}

func (b *EthAPIBackend) Chain() *core.BlockChain {
return b.eth.BlockChain()
}

func (b *EthAPIBackend) ChainDb() ethdb.Database {
return b.eth.ChainDb()
}
Expand Down
14 changes: 14 additions & 0 deletions ethclient/ethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,20 @@ func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.H
return head, err
}

// GetDiffAccounts returns changed accounts in a specific block number.
func (ec *Client) GetDiffAccounts(ctx context.Context, number *big.Int) ([]common.Address, error) {
accounts := make([]common.Address, 0)
err := ec.c.CallContext(ctx, &accounts, "eth_getDiffAccounts", toBlockNumArg(number))
return accounts, err
}

// GetDiffAccountsWithScope returns detailed changes of some interested accounts in a specific block number.
func (ec *Client) GetDiffAccountsWithScope(ctx context.Context, number *big.Int, accounts []common.Address) (*types.DiffAccountsInBlock, error) {
var result types.DiffAccountsInBlock
err := ec.c.CallContext(ctx, &result, "eth_getDiffAccountsWithScope", toBlockNumArg(number), accounts)
return &result, err
}

type rpcTransaction struct {
tx *types.Transaction
txExtraInfo
Expand Down
Loading

0 comments on commit b2f1d25

Please sign in to comment.