aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbojie <bojie@dexon.org>2019-01-07 15:40:56 +0800
committerWei-Ning Huang <w@dexon.org>2019-03-12 12:19:09 +0800
commit7285c3b0af76e2e470ba3917b006264a06644246 (patch)
tree97e0e582ff951d5bedddb22cf3f4201e44bbb358
parentf8bd72b9e430960790db8972826a541425b4c552 (diff)
downloaddexon-7285c3b0af76e2e470ba3917b006264a06644246.tar
dexon-7285c3b0af76e2e470ba3917b006264a06644246.tar.gz
dexon-7285c3b0af76e2e470ba3917b006264a06644246.tar.bz2
dexon-7285c3b0af76e2e470ba3917b006264a06644246.tar.lz
dexon-7285c3b0af76e2e470ba3917b006264a06644246.tar.xz
dexon-7285c3b0af76e2e470ba3917b006264a06644246.tar.zst
dexon-7285c3b0af76e2e470ba3917b006264a06644246.zip
app: implement logic for prepare/verify correctly when chain number change (#118)
-rw-r--r--core/blockchain.go116
-rw-r--r--core/blockchain_test.go6
-rw-r--r--core/genesis.go2
-rw-r--r--core/rawdb/accessors_chain.go15
-rw-r--r--core/rawdb/schema.go2
-rw-r--r--dex/app.go56
-rw-r--r--dex/app_test.go207
-rw-r--r--eth/helper_test.go4
8 files changed, 383 insertions, 25 deletions
diff --git a/core/blockchain.go b/core/blockchain.go
index b4a2882dd..73e820c32 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -160,6 +160,7 @@ type BlockChain struct {
pendingBlocks map[uint64]struct {
block *types.Block
receipts types.Receipts
+ proctime time.Duration
}
}
@@ -199,6 +200,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
pendingBlocks: make(map[uint64]struct {
block *types.Block
receipts types.Receipts
+ proctime time.Duration
}),
confirmedBlocks: make(map[uint32]map[coreCommon.Hash]*blockInfo),
addressNonce: make(map[uint32]map[common.Address]uint64),
@@ -1073,6 +1075,13 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
bytes += batch.ValueSize()
batch.Reset()
}
+
+ if i == len(blockChain)-1 {
+ err := bc.updateLastRoundNumber(block.Round())
+ if err != nil {
+ return 0, err
+ }
+ }
}
if batch.ValueSize() > 0 {
bytes += batch.ValueSize()
@@ -1350,7 +1359,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
case err == consensus.ErrPrunedAncestor:
return bc.insertSidechain(block, it)
- // First block is future, shove it (and all children) to the future queue (unknown ancestor)
+ // First block is future, shove it (and all children) to the future queue (unknown ancestor)
case err == consensus.ErrFutureBlock || (err == consensus.ErrUnknownAncestor && bc.futureBlocks.Contains(it.first().ParentHash())):
for block != nil && (it.index == 0 || err == consensus.ErrUnknownAncestor) {
if err := bc.addFutureBlock(block); err != nil {
@@ -1364,10 +1373,10 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
// If there are any still remaining, mark as ignored
return it.index, events, coalescedLogs, err
- // First block (and state) is known
- // 1. We did a roll-back, and should now do a re-import
- // 2. The block is stored as a sidechain, and is lying about it's stateroot, and passes a stateroot
- // from the canonical chain, which has not been verified.
+ // First block (and state) is known
+ // 1. We did a roll-back, and should now do a re-import
+ // 2. The block is stored as a sidechain, and is lying about it's stateroot, and passes a stateroot
+ // from the canonical chain, which has not been verified.
case err == ErrKnownBlock:
// Skip all known blocks that behind us
current := bc.CurrentBlock().NumberU64()
@@ -1378,7 +1387,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
}
// Falls through to the block import
- // Some other error occurred, abort
+ // Some other error occurred, abort
case err != nil:
stats.ignored += len(it.chain)
bc.reportBlock(block, nil, err)
@@ -1786,6 +1795,13 @@ func (bc *BlockChain) insertDexonChain(chain types.Blocks) (int, []interface{},
cache, _ := bc.stateCache.TrieDB().Size()
stats.report(chain, i, cache)
+
+ if i == len(chain)-1 {
+ err = bc.updateLastRoundNumber(block.Round())
+ if err != nil {
+ return 0, nil, nil, err
+ }
+ }
}
// Append a single chain head event if we've progressed the chain
if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() {
@@ -1823,15 +1839,7 @@ func (bc *BlockChain) processPendingBlock(
coalescedLogs []*types.Log
)
- // Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
- senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, block.Number()), []*types.Block{block})
-
- if atomic.LoadInt32(&bc.procInterrupt) == 1 {
- log.Debug("Premature abort during blocks processing")
- return nil, nil, nil, fmt.Errorf("interrupt")
- }
bstart := time.Now()
-
var witnessData types.WitnessData
if err := rlp.Decode(bytes.NewReader(witness.Data), &witnessData); err != nil {
log.Error("Witness rlp decode failed", "error", err)
@@ -1898,13 +1906,18 @@ func (bc *BlockChain) processPendingBlock(
}
// Add into pending blocks.
- bc.addPendingBlock(newPendingBlock, receipts)
+ bc.addPendingBlock(newPendingBlock, receipts, proctime)
events = append(events, BlockConfirmedEvent{newPendingBlock})
log.Debug("Inserted pending block", "height", newPendingBlock.Number(), "hash", newPendingBlock.Hash())
// Start insert available pending blocks into db
for pendingHeight := bc.CurrentBlock().NumberU64() + 1; pendingHeight <= witness.Height; pendingHeight++ {
+ if atomic.LoadInt32(&bc.procInterrupt) == 1 {
+ log.Debug("Premature abort during blocks processing")
+ return nil, nil, nil, fmt.Errorf("interrupt")
+ }
+
pendingIns, exist := bc.pendingBlocks[pendingHeight]
if !exist {
log.Error("Block has already inserted", "height", pendingHeight)
@@ -1917,6 +1930,7 @@ func (bc *BlockChain) processPendingBlock(
}
// Write the block to the chain and get the status.
+ insertTime := time.Now()
status, err := bc.WriteBlockWithState(pendingIns.block, pendingIns.receipts, s)
if err != nil {
return nil, events, coalescedLogs, fmt.Errorf("WriteBlockWithState error: %v", err)
@@ -1933,12 +1947,12 @@ func (bc *BlockChain) processPendingBlock(
allLogs = append(allLogs, r.Logs...)
}
coalescedLogs = append(coalescedLogs, allLogs...)
- blockInsertTimer.UpdateSince(bstart)
+ blockInsertTimer.UpdateSince(insertTime)
events = append(events, ChainEvent{pendingIns.block, pendingIns.block.Hash(), allLogs})
lastCanon = pendingIns.block
// Only count canonical blocks for GC processing time
- bc.gcproc += proctime
+ bc.gcproc += pendingIns.proctime
case SideStatTy:
return nil, nil, nil, fmt.Errorf("insert pending block and fork found")
@@ -1950,6 +1964,13 @@ func (bc *BlockChain) processPendingBlock(
cache, _ := bc.stateCache.TrieDB().Size()
stats.report([]*types.Block{pendingIns.block}, 0, cache)
+
+ if pendingHeight == witness.Height {
+ err = bc.updateLastRoundNumber(pendingIns.block.Round())
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ }
}
// Append a single chain head event if we've progressed the chain
if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() {
@@ -1960,6 +1981,46 @@ func (bc *BlockChain) processPendingBlock(
return &root, events, coalescedLogs, nil
}
+func (bc *BlockChain) ProcessEmptyBlock(block *types.Block) error {
+ bstart := time.Now()
+ var header = block.Header()
+ var parentBlock *types.Block
+ var pendingState *state.StateDB
+ var err error
+ parent, exist := bc.pendingBlocks[block.NumberU64()-1]
+ if !exist {
+ parentBlock = bc.GetBlockByNumber(block.NumberU64() - 1)
+ if parentBlock == nil {
+ return fmt.Errorf("parent block %d not exist", block.NumberU64()-1)
+ }
+ } else {
+ parentBlock = parent.block
+ }
+
+ pendingState, err = state.New(parentBlock.Root(), bc.stateCache)
+ if err != nil {
+ return err
+ }
+
+ header.ParentHash = parentBlock.Hash()
+ header.GasUsed = 0
+ header.Root = pendingState.IntermediateRoot(true)
+ if header.Root != parentBlock.Root() {
+ return fmt.Errorf("empty block state root must same as parent")
+ }
+
+ newPendingBlock := types.NewBlock(header, nil, nil, nil)
+ if _, ok := bc.GetRoundHeight(newPendingBlock.Round()); !ok {
+ bc.storeRoundHeight(newPendingBlock.Round(), newPendingBlock.NumberU64())
+ }
+
+ proctime := time.Since(bstart)
+ bc.addPendingBlock(newPendingBlock, nil, proctime)
+ bc.PostChainEvents([]interface{}{BlockConfirmedEvent{newPendingBlock}}, nil)
+
+ return nil
+}
+
func (bc *BlockChain) removePendingBlock(height uint64) {
bc.pendingBlockMu.Lock()
defer bc.pendingBlockMu.Unlock()
@@ -1967,14 +2028,15 @@ func (bc *BlockChain) removePendingBlock(height uint64) {
delete(bc.pendingBlocks, height)
}
-func (bc *BlockChain) addPendingBlock(block *types.Block, receipts types.Receipts) {
+func (bc *BlockChain) addPendingBlock(block *types.Block, receipts types.Receipts, proctime time.Duration) {
bc.pendingBlockMu.Lock()
defer bc.pendingBlockMu.Unlock()
bc.pendingBlocks[block.NumberU64()] = struct {
block *types.Block
receipts types.Receipts
- }{block: block, receipts: receipts}
+ proctime time.Duration
+ }{block: block, receipts: receipts, proctime: proctime}
bc.lastPendingHeight = block.NumberU64()
}
@@ -2418,3 +2480,19 @@ func (bc *BlockChain) GetRoundHeight(round uint64) (uint64, bool) {
func (bc *BlockChain) storeRoundHeight(round uint64, height uint64) {
bc.roundHeightMap.Store(round, height)
}
+
+func (bc *BlockChain) updateLastRoundNumber(round uint64) error {
+ currentLastRound, err := rawdb.ReadLastRoundNumber(bc.db)
+ if err != nil {
+ return err
+ }
+
+ if round > currentLastRound {
+ err = rawdb.WriteLastRoundNumber(bc.db, round)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index ba0c8480f..516e43998 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -654,6 +654,9 @@ func TestFastVsFullChains(t *testing.T) {
// Fast import the chain as a non-archive node to test
fastDb := ethdb.NewMemDatabase()
gspec.MustCommit(fastDb)
+ if err := rawdb.WriteLastRoundNumber(fastDb, 0); err != nil {
+ t.Fatalf("failed to write last round: %v", err)
+ }
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer fast.Stop()
@@ -745,6 +748,9 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
// Import the chain as a non-archive node and ensure all pointers are updated
fastDb := ethdb.NewMemDatabase()
gspec.MustCommit(fastDb)
+ if err := rawdb.WriteLastRoundNumber(fastDb, 0); err != nil {
+ t.Fatalf("failed to write last round: %v", err)
+ }
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer fast.Stop()
diff --git a/core/genesis.go b/core/genesis.go
index d0875d36d..f07b7a270 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -170,6 +170,8 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig
return SetupGenesisBlockWithOverride(db, genesis, nil)
}
func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, constantinopleOverride *big.Int) (*params.ChainConfig, common.Hash, error) {
+ rawdb.WriteLastRoundNumber(db, 0)
+
if genesis != nil && genesis.Config == nil {
return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig
}
diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go
index 801ad9362..5e23bef8f 100644
--- a/core/rawdb/accessors_chain.go
+++ b/core/rawdb/accessors_chain.go
@@ -348,6 +348,21 @@ func WriteBlock(db DatabaseWriter, block *types.Block) {
WriteHeader(db, block.Header())
}
+// ReadLastRoundNumber get current round number.
+func ReadLastRoundNumber(db DatabaseReader) (uint64, error) {
+ number, err := db.Get(lastRoundNum)
+ if err != nil {
+ return 0, err
+ }
+
+ return new(big.Int).SetBytes(number).Uint64(), nil
+}
+
+// WriteLastRoundNumber write current round number.
+func WriteLastRoundNumber(db DatabaseWriter, number uint64) error {
+ return db.Put(lastRoundNum, new(big.Int).SetUint64(number).Bytes())
+}
+
// DeleteBlock removes all block data associated with a hash.
func DeleteBlock(db DatabaseDeleter, hash common.Hash, number uint64) {
DeleteReceipts(db, hash, number)
diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go
index 87a56c0ba..954ebfc1d 100644
--- a/core/rawdb/schema.go
+++ b/core/rawdb/schema.go
@@ -63,6 +63,8 @@ var (
// Chain index prefixes (use `i` + single byte to avoid mixing data types).
BloomBitsIndexPrefix = []byte("iB") // BloomBitsIndexPrefix is the data table of a chain indexer to track its progress
+ lastRoundNum = []byte("LastRoundNum")
+
preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil)
preimageHitCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil)
)
diff --git a/dex/app.go b/dex/app.go
index c52a6c79b..b1a0b4548 100644
--- a/dex/app.go
+++ b/dex/app.go
@@ -29,6 +29,7 @@ import (
"github.com/dexon-foundation/dexon/common"
"github.com/dexon-foundation/dexon/core"
+ "github.com/dexon-foundation/dexon/core/rawdb"
"github.com/dexon-foundation/dexon/core/types"
"github.com/dexon-foundation/dexon/ethdb"
"github.com/dexon-foundation/dexon/event"
@@ -175,6 +176,22 @@ func (d *DexconApp) preparePayload(ctx context.Context, position coreTypes.Posit
default:
}
+ if position.Round > 0 {
+ // If round chain number changed but new round is not delivered yet, payload must be nil.
+ previousNumChains := d.gov.Configuration(position.Round - 1).NumChains
+ currentNumChains := d.gov.Configuration(position.Round).NumChains
+ if previousNumChains != currentNumChains {
+ deliveredRound, err := rawdb.ReadLastRoundNumber(d.chainDB)
+ if err != nil {
+ panic(fmt.Errorf("read current round error: %v", err))
+ }
+
+ if deliveredRound < position.Round {
+ return nil, nil
+ }
+ }
+ }
+
if position.Height != 0 {
// Check if chain block height is strictly increamental.
chainLastHeight, ok := d.blockchain.GetChainLastConfirmedHeight(position.ChainID)
@@ -186,8 +203,7 @@ func (d *DexconApp) preparePayload(ctx context.Context, position coreTypes.Posit
}
b, latestState := d.blockchain.GetPending()
- log.Debug("Prepare payload", "chain", position.ChainID, "height", position.Height, "state",
- b.Root().String())
+ log.Debug("Prepare payload", "chain", position.ChainID, "height", position.Height, "state", b.Root().String())
txsMap, err := d.txPool.Pending()
if err != nil {
@@ -345,6 +361,26 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) coreTypes.BlockVerifySta
}
}
+ if block.Position.Round > 0 {
+ // If round chain number changed but new round is not delivered yet, payload must be nil.
+ previousNumChains := d.gov.Configuration(block.Position.Round - 1).NumChains
+ currentNumChains := d.gov.Configuration(block.Position.Round).NumChains
+ if previousNumChains != currentNumChains {
+ deliveredRound, err := rawdb.ReadLastRoundNumber(d.chainDB)
+ if err != nil {
+ panic(fmt.Errorf("read current round error: %v", err))
+ }
+
+ if deliveredRound < block.Position.Round {
+ if len(block.Payload) > 0 {
+ return coreTypes.VerifyInvalidBlock
+ }
+
+ return coreTypes.VerifyOK
+ }
+ }
+ }
+
// Get latest pending state.
b, latestState := d.blockchain.GetPending()
log.Debug("Verify block", "chain", block.Position.ChainID, "height", block.Position.Height, "state",
@@ -482,10 +518,18 @@ func (d *DexconApp) BlockDelivered(
}, txs, nil, nil)
h := d.blockchain.CurrentBlock().NumberU64() + 1
- _, err = d.blockchain.ProcessPendingBlock(newBlock, &block.Witness)
- if err != nil {
- log.Error("Failed to process pending block", "error", err)
- panic(err)
+ if block.IsEmpty() {
+ err = d.blockchain.ProcessEmptyBlock(newBlock)
+ if err != nil {
+ log.Error("Failed to process empty block", "error", err)
+ panic(err)
+ }
+ } else {
+ _, err = d.blockchain.ProcessPendingBlock(newBlock, &block.Witness)
+ if err != nil {
+ log.Error("Failed to process pending block", "error", err)
+ panic(err)
+ }
}
d.blockchain.RemoveConfirmedBlock(chainID, blockHash)
diff --git a/dex/app_test.go b/dex/app_test.go
index 7fc4933de..f4cc2fd9a 100644
--- a/dex/app_test.go
+++ b/dex/app_test.go
@@ -4,12 +4,14 @@ import (
"crypto/ecdsa"
"fmt"
"math/big"
+ "strings"
"testing"
"time"
coreCommon "github.com/dexon-foundation/dexon-consensus/common"
coreTypes "github.com/dexon-foundation/dexon-consensus/core/types"
+ "github.com/dexon-foundation/dexon/accounts/abi"
"github.com/dexon-foundation/dexon/common"
"github.com/dexon-foundation/dexon/consensus/dexcon"
"github.com/dexon-foundation/dexon/core"
@@ -158,6 +160,7 @@ func TestVerifyBlock(t *testing.T) {
block.Hash = coreCommon.NewRandomHash()
block.Position.ChainID = uint32(chainID.Uint64())
block.Position.Height = 1
+ block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
block.Payload, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 100)
if err != nil {
t.Errorf("prepare data error: %v", err)
@@ -174,6 +177,7 @@ func TestVerifyBlock(t *testing.T) {
block.Hash = coreCommon.NewRandomHash()
block.Position.ChainID = uint32(chainID.Uint64())
block.Position.Height = 1
+ block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
block.Payload, block.Witness, err = prepareDataWithoutTxPool(dex, key, 1, 100)
if err != nil {
t.Errorf("prepare data error: %v", err)
@@ -190,6 +194,7 @@ func TestVerifyBlock(t *testing.T) {
block.Hash = coreCommon.NewRandomHash()
block.Position.ChainID = uint32(chainID.Uint64())
block.Position.Height = 2
+ block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
block.Payload, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 100)
if err != nil {
t.Errorf("prepare data error: %v", err)
@@ -206,6 +211,7 @@ func TestVerifyBlock(t *testing.T) {
block.Hash = coreCommon.NewRandomHash()
block.Position.ChainID = uint32(chainID.Uint64())
block.Position.Height = 1
+ block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
block.Payload, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 10000)
if err != nil {
t.Errorf("prepare data error: %v", err)
@@ -222,6 +228,7 @@ func TestVerifyBlock(t *testing.T) {
block.Hash = coreCommon.NewRandomHash()
block.Position.ChainID = uint32(chainID.Uint64())
block.Position.Height = 1
+ block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
_, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 0)
if err != nil {
t.Errorf("prepare data error: %v", err)
@@ -256,6 +263,7 @@ func TestVerifyBlock(t *testing.T) {
block.Hash = coreCommon.NewRandomHash()
block.Position.ChainID = uint32(chainID.Uint64())
block.Position.Height = 1
+ block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
_, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 0)
if err != nil {
t.Errorf("prepare data error: %v", err)
@@ -290,6 +298,7 @@ func TestVerifyBlock(t *testing.T) {
block.Hash = coreCommon.NewRandomHash()
block.Position.ChainID = uint32(chainID.Uint64())
block.Position.Height = 1
+ block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
_, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 0)
if err != nil {
t.Errorf("prepare data error: %v", err)
@@ -368,6 +377,7 @@ func TestBlockConfirmed(t *testing.T) {
block.Hash = coreCommon.NewRandomHash()
block.Witness = witness
block.Payload = payload
+ block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
block.Position.ChainID = uint32(chainID.Uint64())
dex.app.BlockConfirmed(*block)
@@ -463,6 +473,147 @@ func TestBlockDelivered(t *testing.T) {
}
}
+func TestNumChainsChange(t *testing.T) {
+ key, err := crypto.GenerateKey()
+ if err != nil {
+ t.Errorf("hex to ecdsa error: %v", err)
+ }
+
+ params.TestnetChainConfig.Dexcon.Owner = crypto.PubkeyToAddress(key.PublicKey)
+
+ dex, err := newTestDexonWithGenesis(key)
+ if err != nil {
+ t.Errorf("new test dexon error: %v", err)
+ }
+
+ abiObject, err := abi.JSON(strings.NewReader(vm.GovernanceABIJSON))
+ if err != nil {
+ t.Errorf("get abi object fail: %v", err)
+ }
+
+ // Update config in round 1 and height 1.
+ // Config will affect in round 3.
+ input, err := abiObject.Pack("updateConfiguration",
+ new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e4)), big.NewInt(2000),
+ big.NewInt(1e17), big.NewInt(9000000), big.NewInt(3), big.NewInt(500), big.NewInt(5000),
+ big.NewInt(1), big.NewInt(700000), big.NewInt(5), big.NewInt(5), big.NewInt(700000), big.NewInt(1000),
+ []*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1)})
+ if err != nil {
+ t.Errorf("updateConfiguration abiObject pack error: %v", err)
+ }
+
+ block, err := prepareConfirmedBlockWithTxAndData(dex, key, [][]byte{input}, 1)
+ if err != nil {
+ t.Errorf("prepare block error: %v", err)
+ }
+ dex.app.BlockDelivered(block.Hash, block.Position,
+ coreTypes.FinalizationResult{
+ Timestamp: time.Now(),
+ Height: 1,
+ })
+
+ // Snapshot round on round 1 and height 2.
+ input, err = abiObject.Pack("snapshotRound", big.NewInt(1), big.NewInt(1))
+ if err != nil {
+ t.Errorf("abiObject pack error: %v", err)
+ }
+
+ block, err = prepareConfirmedBlockWithTxAndData(dex, key, [][]byte{input}, 1)
+ if err != nil {
+ t.Errorf("prepare block error: %v", err)
+ }
+ dex.app.BlockDelivered(block.Hash, block.Position,
+ coreTypes.FinalizationResult{
+ Timestamp: time.Now(),
+ Height: 2,
+ })
+
+ // Make round 1 height 2 block write into chain.
+ block, err = prepareConfirmedBlockWithTxAndData(dex, key, nil, 1)
+ if err != nil {
+ t.Errorf("prepare block error: %v", err)
+ }
+ dex.app.BlockDelivered(block.Hash, block.Position,
+ coreTypes.FinalizationResult{
+ Timestamp: time.Now(),
+ Height: 3,
+ })
+
+ // Round 2 will keep prepare tx as usual.
+ block, err = prepareConfirmedBlockWithTxAndData(dex, key, [][]byte{{1}}, 2)
+ if err != nil {
+ t.Errorf("prepare block error: %v", err)
+ }
+ if block.Payload == nil {
+ t.Errorf("payload should not be nil")
+ }
+ dex.app.BlockDelivered(block.Hash, block.Position,
+ coreTypes.FinalizationResult{
+ Timestamp: time.Now(),
+ Height: 4,
+ })
+
+ // It will prepare empty payload until witness any block in round 3.
+ block, err = prepareConfirmedBlockWithTxAndData(dex, key, [][]byte{{1}}, 3)
+ if err != nil {
+ t.Errorf("prepare block error: %v", err)
+ }
+ if block.Payload != nil {
+ t.Errorf("payload should be nil but %v", block.Payload)
+ }
+
+ // Test non-empty payload.
+ block.Payload = []byte{1}
+ if status := dex.app.VerifyBlock(block); status != coreTypes.VerifyInvalidBlock {
+ t.Errorf("unexpected verify status %v", status)
+ }
+ block.Payload = nil
+
+ dex.app.BlockDelivered(block.Hash, block.Position,
+ coreTypes.FinalizationResult{
+ Timestamp: time.Now(),
+ Height: 5,
+ })
+
+ // Still empty payload because round 3 is not witness yet.
+ // This block just for witness round 3 height 5.
+ block, err = prepareConfirmedBlockWithTxAndData(dex, key, [][]byte{{1}}, 3)
+ if err != nil {
+ t.Errorf("prepare block error: %v", err)
+ }
+ if block.Payload != nil {
+ t.Errorf("payload should be nil but %v", block.Payload)
+ }
+
+ // Test non-empty payload.
+ block.Payload = []byte{1}
+ if status := dex.app.VerifyBlock(block); status != coreTypes.VerifyInvalidBlock {
+ t.Errorf("unexpected verify status %v", status)
+ }
+ block.Payload = nil
+
+ dex.app.BlockDelivered(block.Hash, block.Position,
+ coreTypes.FinalizationResult{
+ Timestamp: time.Now(),
+ Height: 6,
+ })
+
+ // Empty block in round 3 has been delivered.
+ // Now can prepare payload as usual.
+ block, err = prepareConfirmedBlockWithTxAndData(dex, key, [][]byte{{1}}, 3)
+ if err != nil {
+ t.Errorf("prepare block error: %v", err)
+ }
+ if block.Payload == nil {
+ t.Errorf("payload should not be nil")
+ }
+ dex.app.BlockDelivered(block.Hash, block.Position,
+ coreTypes.FinalizationResult{
+ Timestamp: time.Now(),
+ Height: 7,
+ })
+}
+
func BenchmarkBlockDeliveredFlow(b *testing.B) {
key, err := crypto.GenerateKey()
if err != nil {
@@ -594,6 +745,61 @@ func prepareData(dex *Dexon, key *ecdsa.PrivateKey, startNonce, txNum int) (
return
}
+func prepareConfirmedBlockWithTxAndData(dex *Dexon, key *ecdsa.PrivateKey, data [][]byte, round uint64) (
+ Block *coreTypes.Block, err error) {
+ address := crypto.PubkeyToAddress(key.PublicKey)
+ numChains := dex.governance.GetConfigHelper(round).Configuration().NumChains
+ chainID := new(big.Int).Mod(address.Big(), big.NewInt(int64(numChains)))
+
+ for _, d := range data {
+ // Prepare one block for pending.
+ nonce := dex.txPool.State().GetNonce(address)
+ signer := types.NewEIP155Signer(dex.chainConfig.ChainID)
+ tx := types.NewTransaction(uint64(nonce), vm.GovernanceContractAddress, big.NewInt(0), params.TxGas*2,
+ big.NewInt(1), d)
+ tx, err = types.SignTx(tx, signer, key)
+ if err != nil {
+ return nil, err
+ }
+
+ dex.txPool.AddRemote(tx)
+ if err != nil {
+ return nil, err
+ }
+ }
+ var (
+ payload []byte
+ witness coreTypes.Witness
+ )
+
+ payload, err = dex.app.PreparePayload(coreTypes.Position{Round: round, ChainID: uint32(chainID.Uint64())})
+ if err != nil {
+ return
+ }
+
+ witness, err = dex.app.PrepareWitness(0)
+ if err != nil {
+ return nil, err
+ }
+
+ block := &coreTypes.Block{}
+ block.Hash = coreCommon.NewRandomHash()
+ block.Witness = witness
+ block.Payload = payload
+ block.Position.ChainID = uint32(chainID.Uint64())
+ block.Position.Round = round
+ block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
+
+ status := dex.app.VerifyBlock(block)
+ if status != coreTypes.VerifyOK {
+ err = fmt.Errorf("verify fail: %v", status)
+ return nil, err
+ }
+
+ dex.app.BlockConfirmed(*block)
+ return block, nil
+}
+
func prepareDataWithoutTxPool(dex *Dexon, key *ecdsa.PrivateKey, startNonce, txNum int) (
payload []byte, witness coreTypes.Witness, err error) {
signer := types.NewEIP155Signer(dex.chainConfig.ChainID)
@@ -656,6 +862,7 @@ func prepareConfirmedBlocks(dex *Dexon, keys []*ecdsa.PrivateKey, txNum int) (bl
block.Witness = witness
block.Payload = payload
block.Position.ChainID = uint32(chainID.Uint64())
+ block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
status := dex.app.VerifyBlock(block)
if status != coreTypes.VerifyOK {
diff --git a/eth/helper_test.go b/eth/helper_test.go
index 9a30afef3..e4d7a28fb 100644
--- a/eth/helper_test.go
+++ b/eth/helper_test.go
@@ -30,6 +30,7 @@ import (
"github.com/dexon-foundation/dexon/common"
"github.com/dexon-foundation/dexon/consensus/ethash"
"github.com/dexon-foundation/dexon/core"
+ "github.com/dexon-foundation/dexon/core/rawdb"
"github.com/dexon-foundation/dexon/core/types"
"github.com/dexon-foundation/dexon/core/vm"
"github.com/dexon-foundation/dexon/crypto"
@@ -61,6 +62,9 @@ func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func
genesis = gspec.MustCommit(db)
blockchain, _ = core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil)
)
+ if err := rawdb.WriteLastRoundNumber(db, 0); err != nil {
+ panic(err)
+ }
chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator)
if _, err := blockchain.InsertChain(chain); err != nil {
panic(err)