rpc method to get statediff at specific block; requires archival node or the block be within the pruning range
This commit is contained in:
parent
b3f10c9e69
commit
a8b456883f
@ -89,3 +89,8 @@ func (api *PublicStateDiffAPI) Stream(ctx context.Context) (*rpc.Subscription, e
|
|||||||
|
|
||||||
return rpcSub, nil
|
return rpcSub, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StateDiffAt returns a statediff payload at the specific blockheight
|
||||||
|
func (api *PublicStateDiffAPI) StateDiffAt(ctx context.Context, blockNumber uint64) (*Payload, error) {
|
||||||
|
return api.sds.StateDiffAt(blockNumber)
|
||||||
|
}
|
||||||
|
@ -39,6 +39,7 @@ const chainEventChanSize = 20000
|
|||||||
type blockChain interface {
|
type blockChain interface {
|
||||||
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
|
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
|
||||||
GetBlockByHash(hash common.Hash) *types.Block
|
GetBlockByHash(hash common.Hash) *types.Block
|
||||||
|
GetBlockByNumber(number uint64) *types.Block
|
||||||
AddToStateDiffProcessedCollection(hash common.Hash)
|
AddToStateDiffProcessedCollection(hash common.Hash)
|
||||||
GetReceiptsByHash(hash common.Hash) types.Receipts
|
GetReceiptsByHash(hash common.Hash) types.Receipts
|
||||||
}
|
}
|
||||||
@ -53,6 +54,8 @@ type IService interface {
|
|||||||
Subscribe(id rpc.ID, sub chan<- Payload, quitChan chan<- bool)
|
Subscribe(id rpc.ID, sub chan<- Payload, quitChan chan<- bool)
|
||||||
// Method to unsubscribe from state diff processing
|
// Method to unsubscribe from state diff processing
|
||||||
Unsubscribe(id rpc.ID) error
|
Unsubscribe(id rpc.ID) error
|
||||||
|
// Method to get statediff at specific block
|
||||||
|
StateDiffAt(blockNumber uint64) (*Payload, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Service is the underlying struct for the state diffing service
|
// Service is the underlying struct for the state diffing service
|
||||||
@ -132,9 +135,11 @@ func (sds *Service) Loop(chainEventCh chan core.ChainEvent) {
|
|||||||
log.Error(fmt.Sprintf("Parent block is nil, skipping this block (%d)", currentBlock.Number()))
|
log.Error(fmt.Sprintf("Parent block is nil, skipping this block (%d)", currentBlock.Number()))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := sds.processStateDiff(currentBlock, parentBlock); err != nil {
|
payload, err := sds.processStateDiff(currentBlock, parentBlock)
|
||||||
|
if err != nil {
|
||||||
log.Error(fmt.Sprintf("Error building statediff for block %d; error: ", currentBlock.Number()) + err.Error())
|
log.Error(fmt.Sprintf("Error building statediff for block %d; error: ", currentBlock.Number()) + err.Error())
|
||||||
}
|
}
|
||||||
|
sds.send(*payload)
|
||||||
case err := <-errCh:
|
case err := <-errCh:
|
||||||
log.Warn("Error from chain event subscription, breaking loop", "error", err)
|
log.Warn("Error from chain event subscription, breaking loop", "error", err)
|
||||||
sds.close()
|
sds.close()
|
||||||
@ -148,14 +153,14 @@ func (sds *Service) Loop(chainEventCh chan core.ChainEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// processStateDiff method builds the state diff payload from the current and parent block before sending it to listening subscriptions
|
// processStateDiff method builds the state diff payload from the current and parent block before sending it to listening subscriptions
|
||||||
func (sds *Service) processStateDiff(currentBlock, parentBlock *types.Block) error {
|
func (sds *Service) processStateDiff(currentBlock, parentBlock *types.Block) (*Payload, error) {
|
||||||
stateDiff, err := sds.Builder.BuildStateDiff(parentBlock.Root(), currentBlock.Root(), currentBlock.Number(), currentBlock.Hash())
|
stateDiff, err := sds.Builder.BuildStateDiff(parentBlock.Root(), currentBlock.Root(), currentBlock.Number(), currentBlock.Hash())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
stateDiffRlp, err := rlp.EncodeToBytes(stateDiff)
|
stateDiffRlp, err := rlp.EncodeToBytes(stateDiff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
payload := Payload{
|
payload := Payload{
|
||||||
StateDiffRlp: stateDiffRlp,
|
StateDiffRlp: stateDiffRlp,
|
||||||
@ -163,19 +168,17 @@ func (sds *Service) processStateDiff(currentBlock, parentBlock *types.Block) err
|
|||||||
if sds.StreamBlock {
|
if sds.StreamBlock {
|
||||||
blockBuff := new(bytes.Buffer)
|
blockBuff := new(bytes.Buffer)
|
||||||
if err = currentBlock.EncodeRLP(blockBuff); err != nil {
|
if err = currentBlock.EncodeRLP(blockBuff); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
payload.BlockRlp = blockBuff.Bytes()
|
payload.BlockRlp = blockBuff.Bytes()
|
||||||
receiptBuff := new(bytes.Buffer)
|
receiptBuff := new(bytes.Buffer)
|
||||||
receipts := sds.BlockChain.GetReceiptsByHash(currentBlock.Hash())
|
receipts := sds.BlockChain.GetReceiptsByHash(currentBlock.Hash())
|
||||||
if err = rlp.Encode(receiptBuff, receipts); err != nil {
|
if err = rlp.Encode(receiptBuff, receipts); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
payload.ReceiptsRlp = receiptBuff.Bytes()
|
payload.ReceiptsRlp = receiptBuff.Bytes()
|
||||||
}
|
}
|
||||||
|
return &payload, nil
|
||||||
sds.send(payload)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe is used by the API to subscribe to the service loop
|
// Subscribe is used by the API to subscribe to the service loop
|
||||||
@ -269,3 +272,12 @@ func (sds *Service) close() {
|
|||||||
}
|
}
|
||||||
sds.Unlock()
|
sds.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StateDiffAt returns a statediff payload at the specific blockheight
|
||||||
|
// This operation cannot be performed back past the point of db pruning; it requires an archival node
|
||||||
|
func (sds *Service) StateDiffAt(blockNumber uint64) (*Payload, error) {
|
||||||
|
currentBlock := sds.BlockChain.GetBlockByNumber(blockNumber)
|
||||||
|
parentBlock := sds.BlockChain.GetBlockByHash(currentBlock.ParentHash())
|
||||||
|
log.Info(fmt.Sprintf("sending state diff at %d", blockNumber))
|
||||||
|
return sds.processStateDiff(currentBlock, parentBlock)
|
||||||
|
}
|
||||||
|
@ -194,3 +194,75 @@ func testErrorInBlockLoop(t *testing.T) {
|
|||||||
t.Logf("Actual does not equal expected.\nactual:%+v\nexpected: %+v", builder.NewStateRoot, testBlock1.Root())
|
t.Logf("Actual does not equal expected.\nactual:%+v\nexpected: %+v", builder.NewStateRoot, testBlock1.Root())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetStateDiffAt(t *testing.T) {
|
||||||
|
testErrorInStateDiffAt(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testErrorInStateDiffAt(t *testing.T) {
|
||||||
|
mockStateDiff := statediff.StateDiff{
|
||||||
|
BlockNumber: testBlock1.Number(),
|
||||||
|
BlockHash: testBlock1.Hash(),
|
||||||
|
}
|
||||||
|
expectedStateDiffRlp, err := rlp.EncodeToBytes(mockStateDiff)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
expectedReceiptsRlp, err := rlp.EncodeToBytes(testReceipts1)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
expectedBlockRlp, err := rlp.EncodeToBytes(testBlock1)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
expectedStateDiffPayload := statediff.Payload{
|
||||||
|
StateDiffRlp: expectedStateDiffRlp,
|
||||||
|
ReceiptsRlp: expectedReceiptsRlp,
|
||||||
|
BlockRlp: expectedBlockRlp,
|
||||||
|
}
|
||||||
|
expectedStateDiffPayloadRlp, err := rlp.EncodeToBytes(expectedStateDiffPayload)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
builder := mocks.Builder{}
|
||||||
|
builder.SetStateDiffToBuild(mockStateDiff)
|
||||||
|
blockChain := mocks.BlockChain{}
|
||||||
|
blockMapping := make(map[common.Hash]*types.Block)
|
||||||
|
blockMapping[parentBlock1.Hash()] = parentBlock1
|
||||||
|
blockChain.SetParentBlocksToReturn(blockMapping)
|
||||||
|
blockChain.SetBlockForNumber(testBlock1, testBlock1.NumberU64())
|
||||||
|
blockChain.SetReceiptsForHash(testBlock1.Hash(), testReceipts1)
|
||||||
|
service := statediff.Service{
|
||||||
|
Mutex: sync.Mutex{},
|
||||||
|
Builder: &builder,
|
||||||
|
BlockChain: &blockChain,
|
||||||
|
QuitChan: make(chan bool),
|
||||||
|
Subscriptions: make(map[rpc.ID]statediff.Subscription),
|
||||||
|
StreamBlock: true,
|
||||||
|
}
|
||||||
|
stateDiffPayload, err := service.StateDiffAt(testBlock1.NumberU64())
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
stateDiffPayloadRlp, err := rlp.EncodeToBytes(stateDiffPayload)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
if !bytes.Equal(expectedStateDiffPayloadRlp, stateDiffPayloadRlp) {
|
||||||
|
t.Error("Test failure:", t.Name())
|
||||||
|
t.Logf("Actual does not equal expected.\nactual:%+v\nexpected: %+v", expectedStateDiffPayload, stateDiffPayload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -185,3 +185,8 @@ func (sds *MockStateDiffService) Stop() error {
|
|||||||
close(sds.QuitChan)
|
close(sds.QuitChan)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StateDiffAt mock method
|
||||||
|
func (sds *MockStateDiffService) StateDiffAt(blockNumber uint64) (*statediff.Payload, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
type BlockChain struct {
|
type BlockChain struct {
|
||||||
ParentHashesLookedUp []common.Hash
|
ParentHashesLookedUp []common.Hash
|
||||||
parentBlocksToReturn map[common.Hash]*types.Block
|
parentBlocksToReturn map[common.Hash]*types.Block
|
||||||
|
parentBlocksToReturnByNumber map[uint64]*types.Block
|
||||||
callCount int
|
callCount int
|
||||||
ChainEvents []core.ChainEvent
|
ChainEvents []core.ChainEvent
|
||||||
Receipts map[common.Hash]types.Receipts
|
Receipts map[common.Hash]types.Receipts
|
||||||
@ -100,3 +101,16 @@ func (blockChain *BlockChain) SetReceiptsForHash(hash common.Hash, receipts type
|
|||||||
func (blockChain *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
func (blockChain *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||||
return blockChain.Receipts[hash]
|
return blockChain.Receipts[hash]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetBlockForNumber mock method
|
||||||
|
func (blockChain *BlockChain) SetBlockForNumber(block *types.Block, number uint64) {
|
||||||
|
if blockChain.parentBlocksToReturnByNumber == nil {
|
||||||
|
blockChain.parentBlocksToReturnByNumber = make(map[uint64]*types.Block)
|
||||||
|
}
|
||||||
|
blockChain.parentBlocksToReturnByNumber[number] = block
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockByNumber mock method
|
||||||
|
func (blockChain *BlockChain) GetBlockByNumber(number uint64) *types.Block {
|
||||||
|
return blockChain.parentBlocksToReturnByNumber[number]
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user