diff options
-rwxr-xr-x | build/fullnode-test.sh | 4 | ||||
-rw-r--r-- | core/blockchain.go | 160 | ||||
-rw-r--r-- | dex/app.go | 274 | ||||
-rw-r--r-- | indexer/blockchain.go | 13 |
4 files changed, 161 insertions, 290 deletions
diff --git a/build/fullnode-test.sh b/build/fullnode-test.sh index 24e555787..982e877a5 100755 --- a/build/fullnode-test.sh +++ b/build/fullnode-test.sh @@ -12,7 +12,7 @@ tarAndUpload() endpoint=http://127.0.0.1:8545 -for round in 0 1 2 +for round in 0 1 2 3 4 do echo "Start verify round $round" @@ -39,7 +39,7 @@ if [ $code == 1 ]; then exit 1 fi -if [ $round -lt 2 ]; then +if [ $round -lt 4 ]; then cmd="PKG_CONFIG_PATH=/usr/local/opt/openssl/lib/pkgconfig go run build/testtool/testtool.go monkeyTest $endpoint" eval $cmd code=$? diff --git a/core/blockchain.go b/core/blockchain.go index 34235c10b..aa2326c8d 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -28,7 +28,6 @@ import ( "sync/atomic" "time" - coreCommon "github.com/dexon-foundation/dexon-consensus/common" dexCore "github.com/dexon-foundation/dexon-consensus/core" coreTypes "github.com/dexon-foundation/dexon-consensus/core/types" "github.com/hashicorp/golang-lru" @@ -148,13 +147,6 @@ type BlockChain struct { gov *Governance verifierCache *dexCore.TSigVerifierCache - - confirmedBlockInitMu sync.Mutex - confirmedBlocks map[uint32]map[coreCommon.Hash]*blockInfo - addressNonce map[uint32]map[common.Address]uint64 - addressCost map[uint32]map[common.Address]*big.Int - addressCounter map[uint32]map[common.Address]uint64 - chainLastHeight sync.Map } // NewBlockChain returns a fully initialised block chain using information @@ -176,24 +168,20 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par badBlocks, _ := lru.New(badBlockLimit) bc := &BlockChain{ - chainConfig: chainConfig, - cacheConfig: cacheConfig, - db: db, - triegc: prque.New(nil), - stateCache: state.NewDatabaseWithCache(db, cacheConfig.TrieCleanLimit), - quit: make(chan struct{}), - bodyCache: bodyCache, - bodyRLPCache: bodyRLPCache, - receiptsCache: receiptsCache, - blockCache: blockCache, - futureBlocks: futureBlocks, - engine: engine, - vmConfig: vmConfig, - badBlocks: badBlocks, - confirmedBlocks: make(map[uint32]map[coreCommon.Hash]*blockInfo), - addressNonce: make(map[uint32]map[common.Address]uint64), - addressCost: make(map[uint32]map[common.Address]*big.Int), - addressCounter: make(map[uint32]map[common.Address]uint64), + chainConfig: chainConfig, + cacheConfig: cacheConfig, + db: db, + triegc: prque.New(nil), + stateCache: state.NewDatabaseWithCache(db, cacheConfig.TrieCleanLimit), + quit: make(chan struct{}), + bodyCache: bodyCache, + bodyRLPCache: bodyRLPCache, + receiptsCache: receiptsCache, + blockCache: blockCache, + futureBlocks: futureBlocks, + engine: engine, + vmConfig: vmConfig, + badBlocks: badBlocks, } bc.SetValidator(NewBlockValidator(chainConfig, bc, engine)) bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine)) @@ -286,126 +274,6 @@ func (bc *BlockChain) GetVMConfig() *vm.Config { return &bc.vmConfig } -type blockInfo struct { - addresses map[common.Address]struct{} - block *coreTypes.Block - txs types.Transactions -} - -func (bc *BlockChain) AddConfirmedBlock(block *coreTypes.Block) error { - chainID := uint32(0) - bc.confirmedBlockInitMu.Lock() - _, exist := bc.confirmedBlocks[chainID] - if !exist { - bc.confirmedBlocks[chainID] = make(map[coreCommon.Hash]*blockInfo) - bc.addressNonce[chainID] = make(map[common.Address]uint64) - bc.addressCost[chainID] = make(map[common.Address]*big.Int) - bc.addressCounter[chainID] = make(map[common.Address]uint64) - } - bc.confirmedBlockInitMu.Unlock() - - var transactions types.Transactions - if len(block.Payload) != 0 { - err := rlp.Decode(bytes.NewReader(block.Payload), &transactions) - if err != nil { - return err - } - _, err = types.GlobalSigCache.Add(types.NewEIP155Signer(bc.Config().ChainID), transactions) - if err != nil { - return err - } - } - - addressMap := map[common.Address]struct{}{} - for _, tx := range transactions { - msg, err := tx.AsMessage(types.MakeSigner(bc.Config(), new(big.Int))) - if err != nil { - return err - } - addressMap[msg.From()] = struct{}{} - - // get latest nonce in block - bc.addressNonce[chainID][msg.From()] = msg.Nonce() - - // calculate max cost in confirmed blocks - if bc.addressCost[chainID][msg.From()] == nil { - bc.addressCost[chainID][msg.From()] = big.NewInt(0) - } - bc.addressCost[chainID][msg.From()] = new(big.Int).Add(bc.addressCost[chainID][msg.From()], tx.Cost()) - } - - for addr := range addressMap { - bc.addressCounter[chainID][addr]++ - } - - bc.confirmedBlocks[chainID][block.Hash] = &blockInfo{ - addresses: addressMap, - block: block, - txs: transactions, - } - bc.chainLastHeight.Store(chainID, block.Position.Height) - return nil -} - -func (bc *BlockChain) RemoveConfirmedBlock(chainID uint32, hash coreCommon.Hash) { - blockInfo := bc.confirmedBlocks[chainID][hash] - for addr := range blockInfo.addresses { - bc.addressCounter[chainID][addr]-- - if bc.addressCounter[chainID][addr] == 0 { - delete(bc.addressCounter[chainID], addr) - delete(bc.addressCost[chainID], addr) - delete(bc.addressNonce[chainID], addr) - } - } - - delete(bc.confirmedBlocks[chainID], hash) -} - -func (bc *BlockChain) GetConfirmedBlockByHash(chainID uint32, hash coreCommon.Hash) (*coreTypes.Block, types.Transactions) { - return bc.confirmedBlocks[chainID][hash].block, bc.confirmedBlocks[chainID][hash].txs -} - -func (bc *BlockChain) GetLastNonceInConfirmedBlocks(chainID uint32, address common.Address) (uint64, bool) { - addressNonce, exist := bc.addressNonce[chainID] - if !exist { - return 0, exist - } - - nonce, exist := addressNonce[address] - return nonce, exist -} - -func (bc *BlockChain) GetCostInConfirmedBlocks(chainID uint32, address common.Address) (*big.Int, bool) { - addressCost, exist := bc.addressCost[chainID] - if !exist { - return nil, exist - } - - cost, exist := addressCost[address] - return cost, exist -} - -func (bc *BlockChain) GetChainLastConfirmedHeight(chainID uint32) (uint64, bool) { - val := uint64(0) - v, ok := bc.chainLastHeight.Load(chainID) - if ok { - val = v.(uint64) - } - return val, ok -} - -func (bc *BlockChain) GetAddressInfo(chainID uint32, address common.Address) ( - info struct { - Nonce uint64 - Cost *big.Int - Counter uint64 - }) { - info.Nonce = bc.addressNonce[chainID][address] - info.Cost = bc.addressCost[chainID][address] - info.Counter = bc.addressCounter[chainID][address] - return -} - // loadLastState loads the last known chain state from the database. This method // assumes that the chain manager mutex is held. func (bc *BlockChain) loadLastState() error { diff --git a/dex/app.go b/dex/app.go index e7fdbb4fe..eabdf52f5 100644 --- a/dex/app.go +++ b/dex/app.go @@ -18,6 +18,7 @@ package dex import ( + "bytes" "context" "fmt" "math/big" @@ -47,57 +48,31 @@ type DexconApp struct { finalizedBlockFeed event.Feed scope event.SubscriptionScope - chainLocks sync.Map - chainRoot sync.Map + appMu sync.RWMutex + + confirmedBlocks map[coreCommon.Hash]*blockInfo + addressNonce map[common.Address]uint64 + addressCost map[common.Address]*big.Int + addressCounter map[common.Address]uint64 + undeliveredNum uint64 + deliveredHeight uint64 } func NewDexconApp(txPool *core.TxPool, blockchain *core.BlockChain, gov *DexconGovernance, chainDB ethdb.Database, config *Config) *DexconApp { return &DexconApp{ - txPool: txPool, - blockchain: blockchain, - gov: gov, - chainDB: chainDB, - config: config, + txPool: txPool, + blockchain: blockchain, + gov: gov, + chainDB: chainDB, + config: config, + confirmedBlocks: map[coreCommon.Hash]*blockInfo{}, + addressNonce: map[common.Address]uint64{}, + addressCost: map[common.Address]*big.Int{}, + addressCounter: map[common.Address]uint64{}, } } -func (d *DexconApp) addrBelongsToChain(address common.Address, chainSize, chainID *big.Int) bool { - return true -} - -func (d *DexconApp) chainLock(chainID uint32) { - v, ok := d.chainLocks.Load(chainID) - if !ok { - v, _ = d.chainLocks.LoadOrStore(chainID, &sync.RWMutex{}) - } - v.(*sync.RWMutex).Lock() -} - -func (d *DexconApp) chainUnlock(chainID uint32) { - v, ok := d.chainLocks.Load(chainID) - if !ok { - panic(fmt.Errorf("chain %v is not init yet", chainID)) - } - v.(*sync.RWMutex).Unlock() -} - -func (d *DexconApp) chainRLock(chainID uint32) { - v, ok := d.chainLocks.Load(chainID) - if !ok { - v, _ = d.chainLocks.LoadOrStore(chainID, &sync.RWMutex{}) - } - v.(*sync.RWMutex).RLock() -} - -func (d *DexconApp) chainRUnlock(chainID uint32) { - v, ok := d.chainLocks.Load(chainID) - if !ok { - panic(fmt.Errorf("chain %v is not init yet", chainID)) - } - v.(*sync.RWMutex).RUnlock() -} - // validateNonce check if nonce is in order and return first nonce of every address. func (d *DexconApp) validateNonce(txs types.Transactions) (map[common.Address]uint64, error) { addressFirstNonce := map[common.Address]uint64{} @@ -177,8 +152,8 @@ func (d *DexconApp) PreparePayload(position coreTypes.Position) (payload []byte, func (d *DexconApp) preparePayload(ctx context.Context, position coreTypes.Position) ( payload []byte, err error) { - d.chainRLock(uint32(0)) - defer d.chainRUnlock(uint32(0)) + d.appMu.RLock() + defer d.appMu.RUnlock() select { // This case will hit if previous RLock took too much time. case <-ctx.Done(): @@ -186,37 +161,27 @@ func (d *DexconApp) preparePayload(ctx context.Context, position coreTypes.Posit default: } - if position.Height != 0 { - // Check if chain block height is strictly increamental. - chainLastHeight, ok := d.blockchain.GetChainLastConfirmedHeight(uint32(0)) - if !ok || chainLastHeight != position.Height-1 { - log.Debug("Previous confirmed block not exists", "current pos", position.String(), - "prev height", chainLastHeight, "ok", ok) - return nil, fmt.Errorf("previous block not exists") - } - } - - root, exist := d.chainRoot.Load(uint32(0)) - if !exist { - return nil, nil + // deliver height = position height + 1 + if d.deliveredHeight+d.undeliveredNum != position.Height { + return nil, fmt.Errorf("expected height %d but get %d", d.deliveredHeight+d.undeliveredNum, position.Height) } - currentState, err := d.blockchain.StateAt(*root.(*common.Hash)) + deliveredBlock := d.blockchain.GetBlockByNumber(d.deliveredHeight) + state, err := d.blockchain.StateAt(deliveredBlock.Root()) if err != nil { - return nil, err + return nil, fmt.Errorf("get state by root %v error: %v", deliveredBlock.Root(), err) } - log.Debug("Prepare payload", "chain", uint32(0), "height", position.Height) + + log.Debug("Prepare payload", "height", position.Height) txsMap, err := d.txPool.Pending() if err != nil { return } - chainID := new(big.Int).SetUint64(uint64(uint32(0))) - chainNums := new(big.Int).SetUint64(uint64(d.gov.GetNumChains(position.Round))) blockGasLimit := new(big.Int).SetUint64(d.gov.DexconConfiguration(position.Round).BlockGasLimit) blockGasUsed := new(big.Int) - allTxs := make([]*types.Transaction, 0, 3000) + allTxs := make([]*types.Transaction, 0, 10000) addressMap: for address, txs := range txsMap { @@ -225,21 +190,17 @@ addressMap: break addressMap default: } - // TX hash need to be slot to the given chain in order to be included in the block. - if !d.addrBelongsToChain(address, chainNums, chainID) { - continue - } - balance := currentState.GetBalance(address) - cost, exist := d.blockchain.GetCostInConfirmedBlocks(uint32(0), address) + balance := state.GetBalance(address) + cost, exist := d.addressCost[address] if exist { balance = new(big.Int).Sub(balance, cost) } var expectNonce uint64 - lastConfirmedNonce, exist := d.blockchain.GetLastNonceInConfirmedBlocks(uint32(0), address) + lastConfirmedNonce, exist := d.addressNonce[address] if !exist { - expectNonce = currentState.GetNonce(address) + expectNonce = state.GetNonce(address) } else { expectNonce = lastConfirmedNonce + 1 } @@ -337,37 +298,25 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) coreTypes.BlockVerifySta return coreTypes.VerifyInvalidBlock } - d.chainRLock(uint32(0)) - defer d.chainRUnlock(uint32(0)) + d.appMu.RLock() + defer d.appMu.RUnlock() - if block.Position.Height != 0 { - // Check if target block is the next height to be verified, we can only - // verify the next block in a given chain. - chainLastHeight, ok := d.blockchain.GetChainLastConfirmedHeight(uint32(0)) - if !ok || chainLastHeight != block.Position.Height-1 { - log.Debug("Previous confirmed block not exists", "current pos", block.Position.String(), - "prev height", chainLastHeight, "ok", ok) - return coreTypes.VerifyRetryLater - } + // deliver height = position height + 1 + if d.deliveredHeight+d.undeliveredNum != block.Position.Height { + return coreTypes.VerifyRetryLater } - // Get latest state with current chain. - root, exist := d.chainRoot.Load(uint32(0)) - if !exist { - return coreTypes.VerifyRetryLater + var transactions types.Transactions + if len(block.Payload) == 0 { + return coreTypes.VerifyOK } - currentState, err := d.blockchain.StateAt(*root.(*common.Hash)) - log.Debug("Verify block", "chain", uint32(0), "height", block.Position.Height) + deliveredBlock := d.blockchain.GetBlockByNumber(d.deliveredHeight) + state, err := d.blockchain.StateAt(deliveredBlock.Root()) if err != nil { - log.Debug("Invalid state root", "root", *root.(*common.Hash), "err", err) return coreTypes.VerifyInvalidBlock } - var transactions types.Transactions - if len(block.Payload) == 0 { - return coreTypes.VerifyOK - } err = rlp.DecodeBytes(block.Payload, &transactions) if err != nil { log.Error("Payload rlp decode", "error", err) @@ -390,22 +339,13 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) coreTypes.BlockVerifySta return coreTypes.VerifyInvalidBlock } - // Check if nonce is strictly increasing for every address. - chainID := big.NewInt(int64(uint32(0))) - chainNums := big.NewInt(int64(d.gov.GetNumChains(block.Position.Round))) - for address, firstNonce := range addressNonce { - if !d.addrBelongsToChain(address, chainNums, chainID) { - log.Error("Address does not belong to given chain ID", "address", address, "chainD", chainID) - return coreTypes.VerifyInvalidBlock - } - var expectNonce uint64 - lastConfirmedNonce, exist := d.blockchain.GetLastNonceInConfirmedBlocks(uint32(0), address) + nonce, exist := d.addressNonce[address] if exist { - expectNonce = lastConfirmedNonce + 1 + expectNonce = nonce + 1 } else { - expectNonce = currentState.GetNonce(address) + expectNonce = state.GetNonce(address) } if expectNonce != firstNonce { @@ -417,11 +357,11 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) coreTypes.BlockVerifySta // Calculate balance in last state (including pending state). addressesBalance := map[common.Address]*big.Int{} for address := range addressNonce { - cost, exist := d.blockchain.GetCostInConfirmedBlocks(uint32(0), address) + cost, exist := d.addressCost[address] if exist { - addressesBalance[address] = new(big.Int).Sub(currentState.GetBalance(address), cost) + addressesBalance[address] = new(big.Int).Sub(state.GetBalance(address), cost) } else { - addressesBalance[address] = currentState.GetBalance(address) + addressesBalance[address] = state.GetBalance(address) } } @@ -472,11 +412,10 @@ func (d *DexconApp) BlockDelivered( log.Debug("DexconApp block deliver", "height", result.Height, "hash", blockHash, "position", blockPosition.String()) defer log.Debug("DexconApp block delivered", "height", result.Height, "hash", blockHash, "position", blockPosition.String()) - chainID := uint32(0) - d.chainLock(chainID) - defer d.chainUnlock(chainID) + d.appMu.Lock() + defer d.appMu.Unlock() - block, txs := d.blockchain.GetConfirmedBlockByHash(chainID, blockHash) + block, txs := d.getConfirmedBlockByHash(blockHash) if block == nil { panic("Can not get confirmed block") } @@ -508,46 +447,123 @@ func (d *DexconApp) BlockDelivered( Randomness: result.Randomness, }, txs, nil, nil) - h := d.blockchain.CurrentBlock().NumberU64() + 1 - var root *common.Hash if block.IsEmpty() { - root, err = d.blockchain.ProcessEmptyBlock(newBlock) + _, err = d.blockchain.ProcessEmptyBlock(newBlock) if err != nil { log.Error("Failed to process empty block", "error", err) panic(err) } } else { - root, err = d.blockchain.ProcessBlock(newBlock, &block.Witness) + _, err = d.blockchain.ProcessBlock(newBlock, &block.Witness) if err != nil { log.Error("Failed to process pending block", "error", err) panic(err) } } - d.chainRoot.Store(chainID, root) - d.blockchain.RemoveConfirmedBlock(chainID, blockHash) + d.removeConfirmedBlock(blockHash) + d.deliveredHeight = result.Height // New blocks are finalized, notify other components. - newHeight := d.blockchain.CurrentBlock().NumberU64() - for h <= newHeight { - b := d.blockchain.GetBlockByNumber(h) - go d.finalizedBlockFeed.Send(core.NewFinalizedBlockEvent{b}) - log.Debug("Send new finalized block event", "number", h) - h++ - } + go d.finalizedBlockFeed.Send(core.NewFinalizedBlockEvent{Block: d.blockchain.CurrentBlock()}) } // BlockConfirmed is called when a block is confirmed and added to lattice. func (d *DexconApp) BlockConfirmed(block coreTypes.Block) { - d.chainLock(uint32(0)) - defer d.chainUnlock(uint32(0)) + d.appMu.Lock() + defer d.appMu.Unlock() log.Debug("DexconApp block confirmed", "block", block.String()) - if err := d.blockchain.AddConfirmedBlock(&block); err != nil { + if err := d.addConfirmedBlock(&block); err != nil { panic(err) } } +type addressInfo struct { + cost *big.Int +} + +type blockInfo struct { + addresses map[common.Address]*addressInfo + block *coreTypes.Block + txs types.Transactions +} + +func (d *DexconApp) addConfirmedBlock(block *coreTypes.Block) error { + var transactions types.Transactions + if len(block.Payload) != 0 { + err := rlp.Decode(bytes.NewReader(block.Payload), &transactions) + if err != nil { + return err + } + _, err = types.GlobalSigCache.Add(types.NewEIP155Signer(d.blockchain.Config().ChainID), transactions) + if err != nil { + return err + } + } + + addressMap := map[common.Address]*addressInfo{} + for _, tx := range transactions { + msg, err := tx.AsMessage(types.MakeSigner(d.blockchain.Config(), new(big.Int))) + if err != nil { + return err + } + + if addrInfo, exist := addressMap[msg.From()]; !exist { + addressMap[msg.From()] = &addressInfo{cost: tx.Cost()} + } else { + addrInfo.cost = new(big.Int).Add(addrInfo.cost, tx.Cost()) + } + + // get latest nonce in block + d.addressNonce[msg.From()] = msg.Nonce() + + // calculate max cost in confirmed blocks + if d.addressCost[msg.From()] == nil { + d.addressCost[msg.From()] = big.NewInt(0) + } + d.addressCost[msg.From()] = new(big.Int).Add(d.addressCost[msg.From()], tx.Cost()) + } + + for addr := range addressMap { + d.addressCounter[addr]++ + } + + d.confirmedBlocks[block.Hash] = &blockInfo{ + addresses: addressMap, + block: block, + txs: transactions, + } + + d.undeliveredNum++ + return nil +} + +func (d *DexconApp) removeConfirmedBlock(hash coreCommon.Hash) { + blockInfo := d.confirmedBlocks[hash] + for addr, info := range blockInfo.addresses { + d.addressCounter[addr]-- + d.addressCost[addr] = new(big.Int).Sub(d.addressCost[addr], info.cost) + if d.addressCounter[addr] == 0 { + delete(d.addressCounter, addr) + delete(d.addressCost, addr) + delete(d.addressNonce, addr) + } + } + + delete(d.confirmedBlocks, hash) + d.undeliveredNum-- +} + +func (d *DexconApp) getConfirmedBlockByHash(hash coreCommon.Hash) (*coreTypes.Block, types.Transactions) { + info, exist := d.confirmedBlocks[hash] + if !exist { + return nil, nil + } + + return info.block, info.txs +} + func (d *DexconApp) SubscribeNewFinalizedBlockEvent( ch chan<- core.NewFinalizedBlockEvent) event.Subscription { return d.scope.Track(d.finalizedBlockFeed.Subscribe(ch)) diff --git a/indexer/blockchain.go b/indexer/blockchain.go index f3275f882..910305f9c 100644 --- a/indexer/blockchain.go +++ b/indexer/blockchain.go @@ -3,9 +3,6 @@ package indexer import ( "math/big" - coreCommon "github.com/dexon-foundation/dexon-consensus/common" - coreTypes "github.com/dexon-foundation/dexon-consensus/core/types" - "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/consensus" "github.com/dexon-foundation/dexon/core" @@ -29,12 +26,6 @@ type ReadOnlyBlockChain interface { GasLimit() uint64 Genesis() *types.Block GetAncestor(common.Hash, uint64, uint64, *uint64) (common.Hash, uint64) - GetAddressInfo(uint32, common.Address) ( - info struct { - Nonce uint64 - Cost *big.Int - Counter uint64 - }) GetBlock(common.Hash, uint64) *types.Block GetBlockByHash(common.Hash) *types.Block GetBlockByNumber(uint64) *types.Block @@ -42,15 +33,11 @@ type ReadOnlyBlockChain interface { GetBlocksFromHash(common.Hash, int) (blocks []*types.Block) GetBody(common.Hash) *types.Body GetBodyRLP(common.Hash) rlp.RawValue - GetChainLastConfirmedHeight(uint32) (uint64, bool) - GetConfirmedBlockByHash(uint32, coreCommon.Hash) (*coreTypes.Block, types.Transactions) - GetCostInConfirmedBlocks(uint32, common.Address) (*big.Int, bool) GetGovStateByHash(common.Hash) (*types.GovState, error) GetGovStateByNumber(uint64) (*types.GovState, error) GetHeader(common.Hash, uint64) *types.Header GetHeaderByHash(common.Hash) *types.Header GetHeaderByNumber(number uint64) *types.Header - GetLastNonceInConfirmedBlocks(uint32, common.Address) (uint64, bool) GetReceiptsByHash(common.Hash) types.Receipts GetRoundHeight(uint64) (uint64, bool) GetTd(common.Hash, uint64) *big.Int |