relay receipts with the rest of the data (don't know why I didn't realize to do this earlier)

This commit is contained in:
Ian Norden 2019-07-18 13:55:37 -05:00
parent 3dcb934614
commit 86949cc1c8
4 changed files with 102 additions and 23 deletions

View File

@ -40,6 +40,7 @@ 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
AddToStateDiffProcessedCollection(hash common.Hash) AddToStateDiffProcessedCollection(hash common.Hash)
GetReceiptsByHash(hash common.Hash) types.Receipts
} }
// IService is the state-diffing service interface // IService is the state-diffing service interface
@ -69,7 +70,7 @@ type Service struct {
// Cache the last block so that we can avoid having to lookup the next block's parent // Cache the last block so that we can avoid having to lookup the next block's parent
lastBlock *types.Block lastBlock *types.Block
// Whether or not the block data is streamed alongside the state diff data in the subscription payload // Whether or not the block data is streamed alongside the state diff data in the subscription payload
streamBlock bool StreamBlock bool
// Whether or not we have any subscribers; only if we do, do we processes state diffs // Whether or not we have any subscribers; only if we do, do we processes state diffs
subscribers int32 subscribers int32
} }
@ -82,7 +83,7 @@ func NewStateDiffService(db ethdb.Database, blockChain *core.BlockChain, config
Builder: NewBuilder(db, blockChain, config), Builder: NewBuilder(db, blockChain, config),
QuitChan: make(chan bool), QuitChan: make(chan bool),
Subscriptions: make(map[rpc.ID]Subscription), Subscriptions: make(map[rpc.ID]Subscription),
streamBlock: config.StreamBlock, StreamBlock: config.StreamBlock,
}, nil }, nil
} }
@ -108,7 +109,6 @@ func (sds *Service) Loop(chainEventCh chan core.ChainEvent) {
chainEventSub := sds.BlockChain.SubscribeChainEvent(chainEventCh) chainEventSub := sds.BlockChain.SubscribeChainEvent(chainEventCh)
defer chainEventSub.Unsubscribe() defer chainEventSub.Unsubscribe()
errCh := chainEventSub.Err() errCh := chainEventSub.Err()
for { for {
select { select {
//Notify chain event channel of events //Notify chain event channel of events
@ -161,12 +161,19 @@ func (sds *Service) processStateDiff(currentBlock, parentBlock *types.Block) err
StateDiffRlp: stateDiffRlp, StateDiffRlp: stateDiffRlp,
Err: err, Err: err,
} }
if sds.streamBlock { if sds.StreamBlock {
rlpBuff := new(bytes.Buffer) blockBuff := new(bytes.Buffer)
if err = currentBlock.EncodeRLP(rlpBuff); err != nil { if err = currentBlock.EncodeRLP(blockBuff); err != nil {
return err return err
} }
payload.BlockRlp = rlpBuff.Bytes() payload.BlockRlp = blockBuff.Bytes()
receiptBuff := new(bytes.Buffer)
receipts := sds.BlockChain.GetReceiptsByHash(currentBlock.Hash())
if err = rlp.Encode(receiptBuff, receipts); err != nil {
println(err.Error())
return err
}
payload.ReceiptsRlp = receiptBuff.Bytes()
} }
// If we have any websocket subscriptions listening in, send the data to them // If we have any websocket subscriptions listening in, send the data to them

View File

@ -21,11 +21,13 @@ import (
"math/big" "math/big"
"math/rand" "math/rand"
"reflect" "reflect"
"sync"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/statediff" "github.com/ethereum/go-ethereum/statediff"
"github.com/ethereum/go-ethereum/statediff/testhelpers/mocks" "github.com/ethereum/go-ethereum/statediff/testhelpers/mocks"
@ -33,7 +35,7 @@ import (
func TestServiceLoop(t *testing.T) { func TestServiceLoop(t *testing.T) {
testErrorInChainEventLoop(t) testErrorInChainEventLoop(t)
//testErrorInBlockLoop(t) testErrorInBlockLoop(t)
} }
var ( var (
@ -61,6 +63,12 @@ var (
testBlock2 = types.NewBlock(&header2, nil, nil, nil) testBlock2 = types.NewBlock(&header2, nil, nil, nil)
testBlock3 = types.NewBlock(&header3, nil, nil, nil) testBlock3 = types.NewBlock(&header3, nil, nil, nil)
receiptRoot1 = common.HexToHash("0x05")
receiptRoot2 = common.HexToHash("0x06")
receiptRoot3 = common.HexToHash("0x07")
testReceipts1 = []*types.Receipt{types.NewReceipt(receiptRoot1.Bytes(), false, 1000), types.NewReceipt(receiptRoot2.Bytes(), false, 2000)}
testReceipts2 = []*types.Receipt{types.NewReceipt(receiptRoot3.Bytes(), false, 3000)}
event1 = core.ChainEvent{Block: testBlock1} event1 = core.ChainEvent{Block: testBlock1}
event2 = core.ChainEvent{Block: testBlock2} event2 = core.ChainEvent{Block: testBlock2}
event3 = core.ChainEvent{Block: testBlock3} event3 = core.ChainEvent{Block: testBlock3}
@ -71,25 +79,61 @@ func testErrorInChainEventLoop(t *testing.T) {
builder := mocks.Builder{} builder := mocks.Builder{}
blockChain := mocks.BlockChain{} blockChain := mocks.BlockChain{}
service := statediff.Service{ service := statediff.Service{
Mutex: sync.Mutex{},
Builder: &builder, Builder: &builder,
BlockChain: &blockChain, BlockChain: &blockChain,
QuitChan: make(chan bool), QuitChan: make(chan bool),
Subscriptions: make(map[rpc.ID]statediff.Subscription), Subscriptions: make(map[rpc.ID]statediff.Subscription),
StreamBlock: true,
} }
payloadChan := make(chan statediff.Payload) payloadChan := make(chan statediff.Payload, 2)
quitChan := make(chan bool) quitChan := make(chan bool)
service.Subscribe(rpc.NewID(), payloadChan, quitChan) service.Subscribe(rpc.NewID(), payloadChan, quitChan)
testRoot2 = common.HexToHash("0xTestRoot2") testRoot2 = common.HexToHash("0xTestRoot2")
blockChain.SetParentBlocksToReturn([]*types.Block{parentBlock1, parentBlock2}) blockMapping := make(map[common.Hash]*types.Block)
blockMapping[parentBlock1.Hash()] = parentBlock1
blockMapping[parentBlock2.Hash()] = parentBlock2
blockChain.SetParentBlocksToReturn(blockMapping)
blockChain.SetChainEvents([]core.ChainEvent{event1, event2, event3}) blockChain.SetChainEvents([]core.ChainEvent{event1, event2, event3})
// Need to have listeners on the channels or the subscription will be closed and the processing halted blockChain.SetReceiptsForHash(testBlock1.Hash(), testReceipts1)
blockChain.SetReceiptsForHash(testBlock2.Hash(), testReceipts2)
payloads := make([]statediff.Payload, 0, 2)
wg := sync.WaitGroup{}
go func() { go func() {
select { wg.Add(1)
case <-payloadChan: for i := 0; i < 2; i++ {
case <-quitChan: select {
case payload := <-payloadChan:
payloads = append(payloads, payload)
case <-quitChan:
}
} }
wg.Done()
}() }()
service.Loop(eventsChannel) service.Loop(eventsChannel)
wg.Wait()
if len(payloads) != 2 {
t.Error("Test failure:", t.Name())
t.Logf("Actual number of payloads does not equal expected.\nactual: %+v\nexpected: 3", len(payloads))
}
testReceipts1Rlp, err := rlp.EncodeToBytes(testReceipts1)
if err != nil {
t.Error(err)
}
testReceipts2Rlp, err := rlp.EncodeToBytes(testReceipts2)
if err != nil {
t.Error(err)
}
expectedReceiptsRlp := [][]byte{testReceipts1Rlp, testReceipts2Rlp, nil}
for i, payload := range payloads {
if !bytes.Equal(payload.ReceiptsRlp, expectedReceiptsRlp[i]) {
t.Error("Test failure:", t.Name())
t.Logf("Actual reeipt rlp for payload %d does not equal expected.\nactual: %+v\nexpected: %+v", i, payload.ReceiptsRlp, expectedReceiptsRlp[i])
}
}
if !reflect.DeepEqual(builder.BlockHash, testBlock2.Hash()) { if !reflect.DeepEqual(builder.BlockHash, testBlock2.Hash()) {
t.Error("Test failure:", t.Name()) t.Error("Test failure:", t.Name())
@ -121,9 +165,20 @@ func testErrorInBlockLoop(t *testing.T) {
QuitChan: make(chan bool), QuitChan: make(chan bool),
Subscriptions: make(map[rpc.ID]statediff.Subscription), Subscriptions: make(map[rpc.ID]statediff.Subscription),
} }
payloadChan := make(chan statediff.Payload)
blockChain.SetParentBlocksToReturn([]*types.Block{parentBlock1, nil}) quitChan := make(chan bool)
service.Subscribe(rpc.NewID(), payloadChan, quitChan)
blockMapping := make(map[common.Hash]*types.Block)
blockMapping[parentBlock1.Hash()] = parentBlock1
blockChain.SetParentBlocksToReturn(blockMapping)
blockChain.SetChainEvents([]core.ChainEvent{event1, event2}) blockChain.SetChainEvents([]core.ChainEvent{event1, event2})
// Need to have listeners on the channels or the subscription will be closed and the processing halted
go func() {
select {
case <-payloadChan:
case <-quitChan:
}
}()
service.Loop(eventsChannel) service.Loop(eventsChannel)
if !bytes.Equal(builder.BlockHash.Bytes(), testBlock1.Hash().Bytes()) { if !bytes.Equal(builder.BlockHash.Bytes(), testBlock1.Hash().Bytes()) {

View File

@ -30,16 +30,20 @@ import (
// BlockChain is a mock blockchain for testing // BlockChain is a mock blockchain for testing
type BlockChain struct { type BlockChain struct {
ParentHashesLookedUp []common.Hash ParentHashesLookedUp []common.Hash
parentBlocksToReturn []*types.Block parentBlocksToReturn map[common.Hash]*types.Block
callCount int callCount int
ChainEvents []core.ChainEvent ChainEvents []core.ChainEvent
Receipts map[common.Hash]types.Receipts
} }
// AddToStateDiffProcessedCollection mock method // AddToStateDiffProcessedCollection mock method
func (blockChain *BlockChain) AddToStateDiffProcessedCollection(hash common.Hash) {} func (blockChain *BlockChain) AddToStateDiffProcessedCollection(hash common.Hash) {}
// SetParentBlocksToReturn mock method // SetParentBlocksToReturn mock method
func (blockChain *BlockChain) SetParentBlocksToReturn(blocks []*types.Block) { func (blockChain *BlockChain) SetParentBlocksToReturn(blocks map[common.Hash]*types.Block) {
if blockChain.parentBlocksToReturn == nil {
blockChain.parentBlocksToReturn = make(map[common.Hash]*types.Block)
}
blockChain.parentBlocksToReturn = blocks blockChain.parentBlocksToReturn = blocks
} }
@ -49,10 +53,9 @@ func (blockChain *BlockChain) GetBlockByHash(hash common.Hash) *types.Block {
var parentBlock *types.Block var parentBlock *types.Block
if len(blockChain.parentBlocksToReturn) > 0 { if len(blockChain.parentBlocksToReturn) > 0 {
parentBlock = blockChain.parentBlocksToReturn[blockChain.callCount] parentBlock = blockChain.parentBlocksToReturn[hash]
} }
blockChain.callCount++
return parentBlock return parentBlock
} }
@ -84,3 +87,16 @@ func (blockChain *BlockChain) SubscribeChainEvent(ch chan<- core.ChainEvent) eve
return subscription return subscription
} }
// SetReceiptsForHash mock method
func (blockChain *BlockChain) SetReceiptsForHash(hash common.Hash, receipts types.Receipts) {
if blockChain.Receipts == nil {
blockChain.Receipts = make(map[common.Hash]types.Receipts)
}
blockChain.Receipts[hash] = receipts
}
// GetReceiptsByHash mock method
func (blockChain *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
return blockChain.Receipts[hash]
}

View File

@ -36,15 +36,16 @@ type Subscription struct {
// Payload packages the data to send to StateDiffingService subscriptions // Payload packages the data to send to StateDiffingService subscriptions
type Payload struct { type Payload struct {
BlockRlp []byte `json:"blockRlp" gencodec:"required"` BlockRlp []byte `json:"blockRlp"`
ReceiptsRlp []byte `json:"receiptsRlp"`
StateDiffRlp []byte `json:"stateDiff" gencodec:"required"` StateDiffRlp []byte `json:"stateDiff" gencodec:"required"`
Err error `json:"error"` Err error `json:"error"`
} }
// StateDiff is the final output structure from the builder // StateDiff is the final output structure from the builder
type StateDiff struct { type StateDiff struct {
BlockNumber *big.Int `json:"blockNumber" gencodec:"required"` BlockNumber *big.Int `json:"blockNumber" gencodec:"required"`
BlockHash common.Hash `json:"blockHash" gencodec:"required"` BlockHash common.Hash `json:"blockHash" gencodec:"required"`
CreatedAccounts []AccountDiff `json:"createdAccounts" gencodec:"required"` CreatedAccounts []AccountDiff `json:"createdAccounts" gencodec:"required"`
DeletedAccounts []AccountDiff `json:"deletedAccounts" gencodec:"required"` DeletedAccounts []AccountDiff `json:"deletedAccounts" gencodec:"required"`
UpdatedAccounts []AccountDiff `json:"updatedAccounts" gencodec:"required"` UpdatedAccounts []AccountDiff `json:"updatedAccounts" gencodec:"required"`