From 3f320c9048198d14bc44413861efcbc5665324b1 Mon Sep 17 00:00:00 2001 From: Bojie Wu Date: Tue, 9 Oct 2018 13:28:45 +0800 Subject: app: add cache mechanism to increase performance --- consensus/dexcon/dexcon.go | 2 + core/blockchain.go | 178 ++++++++++++++++++------------------------ core/types/block.go | 35 +++++---- core/types/gen_header_json.go | 75 ++++++++++-------- dex/app.go | 166 +++++++++++++++------------------------ internal/ethapi/api.go | 3 +- 6 files changed, 203 insertions(+), 256 deletions(-) diff --git a/consensus/dexcon/dexcon.go b/consensus/dexcon/dexcon.go index d8c181432..67bf33bbf 100644 --- a/consensus/dexcon/dexcon.go +++ b/consensus/dexcon/dexcon.go @@ -113,6 +113,8 @@ func (d *Dexcon) Finalize(chain consensus.ChainReader, header *types.Header, sta config := d.configFetcher.DexconConfiguration(header.Round) reward := new(big.Int).Div(config.BlockReward, big.NewInt(int64(config.NumChains))) state.AddBalance(header.Coinbase, reward) + + header.BlockReward = reward header.Root = state.IntermediateRoot(true) return types.NewBlock(header, txs, uncles, receipts), nil } diff --git a/core/blockchain.go b/core/blockchain.go index 80d4b2f08..429b3a4cc 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -30,7 +30,7 @@ import ( coreCommon "github.com/dexon-foundation/dexon-consensus-core/common" coreTypes "github.com/dexon-foundation/dexon-consensus-core/core/types" - lru "github.com/hashicorp/golang-lru" + "github.com/hashicorp/golang-lru" "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/common/math" @@ -142,9 +142,11 @@ type BlockChain struct { badBlocks *lru.Cache // Bad block cache shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block. - confirmedBlockMu sync.Mutex - confirmedBlocks map[coreCommon.Hash]*coreTypes.Block - chainConfirmedBlocks map[uint32][]*coreTypes.Block + confirmedBlocks map[coreCommon.Hash]*blockInfo + addressNonce map[common.Address]uint64 + addressCost map[common.Address]*big.Int + addressCounter map[common.Address]uint64 + chainLastHeight map[uint32]uint64 pendingBlocks map[uint64]struct { block *types.Block @@ -171,28 +173,30 @@ 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.NewDatabase(db), - stateCache: state.NewDatabaseWithCache(db, cacheConfig.TrieCleanLimit), - quit: make(chan struct{}), - shouldPreserve: shouldPreserve, - bodyCache: bodyCache, - bodyRLPCache: bodyRLPCache, - receiptsCache: receiptsCache, - blockCache: blockCache, - futureBlocks: futureBlocks, - engine: engine, - vmConfig: vmConfig, - badBlocks: badBlocks, - confirmedBlocks: make(map[coreCommon.Hash]*coreTypes.Block), - chainConfirmedBlocks: make(map[uint32][]*coreTypes.Block), + chainConfig: chainConfig, + cacheConfig: cacheConfig, + db: db, + triegc: prque.New(nil), + stateCache: state.NewDatabaseWithCache(db, cacheConfig.TrieCleanLimit), + quit: make(chan struct{}), + shouldPreserve: shouldPreserve, + bodyCache: bodyCache, + bodyRLPCache: bodyRLPCache, + receiptsCache: receiptsCache, + blockCache: blockCache, + futureBlocks: futureBlocks, + engine: engine, + vmConfig: vmConfig, + badBlocks: badBlocks, + confirmedBlocks: make(map[coreCommon.Hash]*blockInfo), pendingBlocks: make(map[uint64]struct { block *types.Block receipts types.Receipts }), + addressNonce: make(map[common.Address]uint64), + addressCost: make(map[common.Address]*big.Int), + addressCounter: make(map[common.Address]uint64), + chainLastHeight: make(map[uint32]uint64), } bc.SetValidator(NewDexonBlockValidator(chainConfig, bc, engine)) bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine)) @@ -236,105 +240,79 @@ func (bc *BlockChain) GetVMConfig() *vm.Config { return &bc.vmConfig } -func (bc *BlockChain) AddConfirmedBlock(block *coreTypes.Block) { - bc.confirmedBlockMu.Lock() - defer bc.confirmedBlockMu.Unlock() - - bc.confirmedBlocks[block.Hash] = block - chainBlocks := bc.chainConfirmedBlocks[block.Position.ChainID] - bc.chainConfirmedBlocks[block.Position.ChainID] = append(chainBlocks, block) +type blockInfo struct { + addresses map[common.Address]interface{} + block *coreTypes.Block } -func (bc *BlockChain) RemoveConfirmedBlock(hash coreCommon.Hash) { - bc.confirmedBlockMu.Lock() - defer bc.confirmedBlockMu.Unlock() - - block := bc.confirmedBlocks[hash] - delete(bc.confirmedBlocks, block.Hash) - - chainBlocks := bc.chainConfirmedBlocks[block.Position.ChainID] - bc.chainConfirmedBlocks[block.Position.ChainID] = chainBlocks[1:] - if len(bc.chainConfirmedBlocks[block.Position.ChainID]) == 0 { - delete(bc.chainConfirmedBlocks, block.Position.ChainID) +func (bc *BlockChain) AddConfirmedBlock(block *coreTypes.Block) error { + var transactions types.Transactions + err := rlp.Decode(bytes.NewReader(block.Payload), &transactions) + if err != nil { + return err } -} - -func (bc *BlockChain) GetConfirmedBlockByHash(hash coreCommon.Hash) *coreTypes.Block { - bc.confirmedBlockMu.Lock() - defer bc.confirmedBlockMu.Unlock() - - return bc.confirmedBlocks[hash] -} - -func (bc *BlockChain) GetConfirmedTxsByAddress(chainID uint32, address common.Address) (types.Transactions, error) { - bc.confirmedBlockMu.Lock() - defer bc.confirmedBlockMu.Unlock() - var addressTxs types.Transactions - for _, block := range bc.chainConfirmedBlocks[chainID] { - var transactions types.Transactions - err := rlp.Decode(bytes.NewReader(block.Payload), &transactions) + addressMap := map[common.Address]interface{}{} + for _, tx := range transactions { + msg, err := tx.AsMessage(types.MakeSigner(bc.Config(), new(big.Int))) if err != nil { - return nil, err + return err } + addressMap[msg.From()] = nil - for _, tx := range transactions { - msg, err := tx.AsMessage(types.MakeSigner(bc.chainConfig, new(big.Int))) - if err != nil { - return nil, err - } + // get latest nonce in block + bc.addressNonce[msg.From()] = msg.Nonce() - if msg.From() == address { - addressTxs = append(addressTxs, tx) - } + // calculate max cost in confirmed blocks + if bc.addressCost[msg.From()] == nil { + bc.addressCost[msg.From()] = tx.Cost() + } else { + bc.addressCost[msg.From()] = new(big.Int).Add(bc.addressCost[msg.From()], tx.Cost()) } } - return addressTxs, nil -} -func (bc *BlockChain) GetLastNonceFromConfirmedBlocks(chainID uint32, address common.Address) (uint64, bool, error) { - chainBlocks, exist := bc.chainConfirmedBlocks[chainID] - if !exist { - return 0, true, nil + for addr := range addressMap { + bc.addressCounter[addr]++ } - for i := len(chainBlocks) - 1; i >= 0; i-- { - var transactions types.Transactions - err := rlp.Decode(bytes.NewReader(chainBlocks[i].Payload), &transactions) - if err != nil { - return 0, true, err - } - - for _, tx := range transactions { - msg, err := tx.AsMessage(types.MakeSigner(bc.chainConfig, new(big.Int))) - if err != nil { - return 0, true, err - } + bc.confirmedBlocks[block.Hash] = &blockInfo{ + addresses: addressMap, + block: block, + } + bc.chainLastHeight[block.Position.ChainID] = block.Position.Height + return nil +} - if msg.From() == address { - return msg.Nonce(), false, nil - } +func (bc *BlockChain) RemoveConfirmedBlock(hash coreCommon.Hash) { + blockInfo := bc.confirmedBlocks[hash] + for addr := range blockInfo.addresses { + bc.addressCounter[addr]-- + if bc.addressCounter[addr] == 0 { + delete(bc.addressCounter, addr) + delete(bc.addressCost, addr) + delete(bc.addressNonce, addr) } } - return 0, true, nil + delete(bc.confirmedBlocks, hash) } -func (bc *BlockChain) GetChainLastConfirmedHeight(chainID uint32) (uint64, bool) { - bc.confirmedBlockMu.Lock() - defer bc.confirmedBlockMu.Unlock() +func (bc *BlockChain) GetConfirmedBlockByHash(hash coreCommon.Hash) *coreTypes.Block { + return bc.confirmedBlocks[hash].block +} - chainBlocks := bc.chainConfirmedBlocks[chainID] - size := len(chainBlocks) - if size == 0 { - return 0, true - } +func (bc *BlockChain) GetLastNonceInConfirmedBlocks(address common.Address) (uint64, bool) { + nonce, exist := bc.addressNonce[address] + return nonce, exist +} - return chainBlocks[size-1].Position.Height, false +func (bc *BlockChain) GetCostInConfirmedBlocks(address common.Address) (*big.Int, bool) { + cost, exist := bc.addressCost[address] + return cost, exist } -func (bc *BlockChain) GetConfirmedBlocksByChainID(chainID uint32) []*coreTypes.Block { - return bc.chainConfirmedBlocks[chainID] +func (bc *BlockChain) GetChainLastConfirmedHeight(chainID uint32) uint64 { + return bc.chainLastHeight[chainID] } // loadLastState loads the last known chain state from the database. This method @@ -1615,11 +1593,10 @@ func (bc *BlockChain) processPendingBlock(block *types.Block, witness *coreTypes proctime := time.Since(bstart) // commit state to refresh stateCache - root, err := pendingState.Commit(true) + _, err = pendingState.Commit(true) if err != nil { return 0, nil, nil, fmt.Errorf("pendingState commit error: %v", err) } - log.Info("Commit pending root", "hash", root) // add into pending blocks bc.pendingBlocks[block.NumberU64()] = struct { @@ -1641,7 +1618,6 @@ func (bc *BlockChain) processPendingBlock(block *types.Block, witness *coreTypes } // Write the block to the chain and get the status. - log.Debug("Insert pending block", "height", pendingHeight) status, err := bc.WriteBlockWithState(pendingIns.block, pendingIns.receipts, s) if err != nil { return 0, events, coalescedLogs, fmt.Errorf("WriteBlockWithState error: %v", err) diff --git a/core/types/block.go b/core/types/block.go index 9e42a776a..07fc55358 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -75,25 +75,26 @@ type WitnessData struct { // Header represents a block header in the Ethereum blockchain. type Header struct { - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` - Coinbase common.Address `json:"miner" gencodec:"required"` - Root common.Hash `json:"stateRoot" gencodec:"required"` - TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` - ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` - Bloom Bloom `json:"logsBloom" gencodec:"required"` - Difficulty *big.Int `json:"difficulty" gencodec:"required"` - Number *big.Int `json:"number" gencodec:"required"` - GasLimit uint64 `json:"gasLimit" gencodec:"required"` - GasUsed uint64 `json:"gasUsed" gencodec:"required"` - Time uint64 `json:"timestamp" gencodec:"required"` - Extra []byte `json:"extraData" gencodec:"required"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase common.Address `json:"miner" gencodec:"required"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *big.Int `json:"difficulty" gencodec:"required"` + Number *big.Int `json:"number" gencodec:"required"` + GasLimit uint64 `json:"gasLimit" gencodec:"required"` + GasUsed uint64 `json:"gasUsed" gencodec:"required"` + Time uint64 `json:"timestamp" gencodec:"required"` + Extra []byte `json:"extraData" gencodec:"required"` MixDigest common.Hash `json:"mixHash"` Nonce BlockNonce `json:"nonce"` - Randomness []byte `json:"randomness" gencodec:"required"` - Position coreTypes.Position `json:"position" gencodec:"required"` - Round uint64 `json:"round" gencodec:"required"` - DexconMeta []byte `json:"dexconMeta" gencodec:"required"` + Randomness []byte `json:"randomness" gencodec:"required"` + Position coreTypes.Position `json:"position" gencodec:"required"` + Round uint64 `json:"round" gencodec:"required"` + DexconMeta []byte `json:"dexconMeta" gencodec:"required"` + BlockReward *big.Int `json:"blockReward" gencodec:"required"` } // field type overrides for gencodec diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go index 31796bf5b..b49d48f14 100644 --- a/core/types/gen_header_json.go +++ b/core/types/gen_header_json.go @@ -17,25 +17,26 @@ var _ = (*headerMarshaling)(nil) // MarshalJSON marshals as JSON. func (h Header) MarshalJSON() ([]byte, error) { type Header struct { - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` - Coinbase common.Address `json:"miner" gencodec:"required"` - Root common.Hash `json:"stateRoot" gencodec:"required"` - TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` - ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` - Bloom Bloom `json:"logsBloom" gencodec:"required"` - Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` - Number *hexutil.Big `json:"number" gencodec:"required"` - GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` - Extra hexutil.Bytes `json:"extraData" gencodec:"required"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase common.Address `json:"miner" gencodec:"required"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra hexutil.Bytes `json:"extraData" gencodec:"required"` MixDigest common.Hash `json:"mixHash"` Nonce BlockNonce `json:"nonce"` - Randomness hexutil.Bytes `json:"randomness" gencodec:"required"` - Position types.Position `json:"position" gencodec:"required"` - Round hexutil.Uint64 `json:"round" gencodec:"required"` - DexconMeta hexutil.Bytes `json:"dexconMeta" gencodec:"required"` + Randomness hexutil.Bytes `json:"randomness" gencodec:"required"` + Position types.Position `json:"position" gencodec:"required"` + Round hexutil.Uint64 `json:"round" gencodec:"required"` + DexconMeta hexutil.Bytes `json:"dexconMeta" gencodec:"required"` + BlockReward *big.Int `json:"blockReward" gencodec:"required"` Hash common.Hash `json:"hash"` } var enc Header @@ -58,6 +59,7 @@ func (h Header) MarshalJSON() ([]byte, error) { enc.Position = h.Position enc.Round = hexutil.Uint64(h.Round) enc.DexconMeta = h.DexconMeta + enc.BlockReward = h.BlockReward enc.Hash = h.Hash() return json.Marshal(&enc) } @@ -65,25 +67,26 @@ func (h Header) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals from JSON. func (h *Header) UnmarshalJSON(input []byte) error { type Header struct { - ParentHash *common.Hash `json:"parentHash" gencodec:"required"` - UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` - Coinbase *common.Address `json:"miner" gencodec:"required"` - Root *common.Hash `json:"stateRoot" gencodec:"required"` - TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` - ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` - Bloom *Bloom `json:"logsBloom" gencodec:"required"` - Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` - Number *hexutil.Big `json:"number" gencodec:"required"` - GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` - Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase *common.Address `json:"miner" gencodec:"required"` + Root *common.Hash `json:"stateRoot" gencodec:"required"` + TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom *Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` MixDigest *common.Hash `json:"mixHash"` Nonce *BlockNonce `json:"nonce"` - Randomness *hexutil.Bytes `json:"randomness" gencodec:"required"` - Position *types.Position `json:"position" gencodec:"required"` - Round *hexutil.Uint64 `json:"round" gencodec:"required"` - DexconMeta *hexutil.Bytes `json:"dexconMeta" gencodec:"required"` + Randomness *hexutil.Bytes `json:"randomness" gencodec:"required"` + Position *types.Position `json:"position" gencodec:"required"` + Round *hexutil.Uint64 `json:"round" gencodec:"required"` + DexconMeta *hexutil.Bytes `json:"dexconMeta" gencodec:"required"` + BlockReward *big.Int `json:"blockReward" gencodec:"required"` } var dec Header if err := json.Unmarshal(input, &dec); err != nil { @@ -163,5 +166,9 @@ func (h *Header) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'dexconMeta' for Header") } h.DexconMeta = *dec.DexconMeta + if dec.BlockReward == nil { + return errors.New("missing required field 'blockReward' for Header") + } + h.BlockReward = dec.BlockReward return nil } diff --git a/dex/app.go b/dex/app.go index 867c10361..7730ba22f 100644 --- a/dex/app.go +++ b/dex/app.go @@ -47,12 +47,13 @@ type DexconApp struct { vmConfig vm.Config notifyChan map[uint64]*notify - mutex *sync.Mutex + notifyMu *sync.Mutex lastPendingHeight uint64 insertMu sync.Mutex - chainHeight map[uint32]uint64 + chainLocksInitMu *sync.Mutex + chainLocks map[uint32]*sync.Mutex } type notify struct { @@ -67,21 +68,22 @@ type witnessData struct { func NewDexconApp(txPool *core.TxPool, blockchain *core.BlockChain, gov *DexconGovernance, chainDB ethdb.Database, config *Config, vmConfig vm.Config) *DexconApp { return &DexconApp{ - txPool: txPool, - blockchain: blockchain, - gov: gov, - chainDB: chainDB, - config: config, - vmConfig: vmConfig, - notifyChan: make(map[uint64]*notify), - mutex: &sync.Mutex{}, - chainHeight: make(map[uint32]uint64), + txPool: txPool, + blockchain: blockchain, + gov: gov, + chainDB: chainDB, + config: config, + vmConfig: vmConfig, + notifyChan: make(map[uint64]*notify), + notifyMu: &sync.Mutex{}, + chainLocksInitMu: &sync.Mutex{}, + chainLocks: make(map[uint32]*sync.Mutex), } } func (d *DexconApp) addNotify(height uint64) <-chan uint64 { - d.mutex.Lock() - defer d.mutex.Unlock() + d.notifyMu.Lock() + defer d.notifyMu.Unlock() result := make(chan uint64) if n, exist := d.notifyChan[height]; exist { n.results = append(n.results, result) @@ -93,8 +95,8 @@ func (d *DexconApp) addNotify(height uint64) <-chan uint64 { } func (d *DexconApp) notify(height uint64) { - d.mutex.Lock() - defer d.mutex.Unlock() + d.notifyMu.Lock() + defer d.notifyMu.Unlock() for h, n := range d.notifyChan { if height >= h { for _, ch := range n.results { @@ -113,21 +115,12 @@ func (d *DexconApp) checkChain(address common.Address, chainSize, chainID *big.I // PreparePayload is called when consensus core is preparing payload for block. func (d *DexconApp) PreparePayload(position coreTypes.Position) (payload []byte, err error) { - d.insertMu.Lock() - defer d.insertMu.Unlock() + d.chainLock(position.ChainID) + defer d.chainUnlock(position.ChainID) if position.Height != 0 { - chainLastHeight, empty := d.blockchain.GetChainLastConfirmedHeight(position.ChainID) - if empty { - var exist bool - chainLastHeight, exist = d.chainHeight[position.ChainID] - if !exist { - log.Error("Something wrong") - return nil, fmt.Errorf("something wrong") - } - } - // check if chain block height is sequential + chainLastHeight := d.blockchain.GetChainLastConfirmedHeight(position.ChainID) if chainLastHeight != position.Height-1 { log.Error("Check confirmed block height fail", "chain", position.ChainID, "height", position.Height-1, "cache height", chainLastHeight) return nil, fmt.Errorf("check confirmed block height fail") @@ -170,11 +163,8 @@ addressMap: var expectNonce uint64 // get last nonce from confirmed blocks - lastConfirmedNonce, empty, err := d.blockchain.GetLastNonceFromConfirmedBlocks(position.ChainID, address) - if err != nil { - log.Error("Get last nonce from confirmed blocks", "error", err) - return nil, fmt.Errorf("get last nonce from confirmed blocks error: %v", err) - } else if empty { + lastConfirmedNonce, exist := d.blockchain.GetLastNonceInConfirmedBlocks(address) + if !exist { // get expect nonce from latest state when confirmed block is empty expectNonce = latestState.GetNonce(address) } else { @@ -182,20 +172,14 @@ addressMap: } if expectNonce != txs[0].Nonce() { - log.Warn("Nonce check error", "expect", expectNonce, "nonce", txs[0].Nonce()) + log.Debug("Nonce check error", "expect", expectNonce, "nonce", txs[0].Nonce()) continue } balance := latestState.GetBalance(address) - confirmedTxs, err := d.blockchain.GetConfirmedTxsByAddress(position.ChainID, address) - if err != nil { - return nil, fmt.Errorf("get confirmed txs error: %v", err) - } - - for _, tx := range confirmedTxs { - maxGasUsed := new(big.Int).Mul(new(big.Int).SetUint64(tx.Gas()), tx.GasPrice()) - balance = new(big.Int).Sub(balance, maxGasUsed) - balance = new(big.Int).Sub(balance, tx.Value()) + cost, exist := d.blockchain.GetCostInConfirmedBlocks(address) + if exist { + balance = new(big.Int).Sub(balance, cost) } for _, tx := range txs { @@ -210,8 +194,7 @@ addressMap: break } - balance = new(big.Int).Sub(balance, maxGasUsed) - balance = new(big.Int).Sub(balance, tx.Value()) + balance = new(big.Int).Sub(balance, tx.Cost()) if balance.Cmp(big.NewInt(0)) < 0 { log.Error("Tx fail", "reason", "not enough balance") break @@ -275,15 +258,13 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) coreTypes.BlockVerifySta return coreTypes.VerifyInvalidBlock } - log.Info("Ready to verify witness block", "height", block.Witness.Height) - - for i := 0; i < 3 && err != nil; i++ { + for i := 0; i < 6 && err != nil; i++ { // check witness root exist err = nil _, err = d.blockchain.StateAt(witnessData.Root) if err != nil { - log.Warn("Sleep 2 seconds and try again", "error", err) - time.Sleep(2 * time.Second) + log.Debug("Sleep 0.5 seconds and try again", "error", err) + time.Sleep(500 * time.Millisecond) } } if err != nil { @@ -291,23 +272,14 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) coreTypes.BlockVerifySta return coreTypes.VerifyRetryLater } - d.insertMu.Lock() - defer d.insertMu.Unlock() + d.chainLock(block.Position.ChainID) + defer d.chainUnlock(block.Position.ChainID) if block.Position.Height != 0 { - chainLastHeight, empty := d.blockchain.GetChainLastConfirmedHeight(block.Position.ChainID) - if empty { - var exist bool - chainLastHeight, exist = d.chainHeight[block.Position.ChainID] - if !exist { - log.Error("Something wrong") - return coreTypes.VerifyInvalidBlock - } - } - // check if chain block height is sequential + chainLastHeight := d.blockchain.GetChainLastConfirmedHeight(block.Position.ChainID) if chainLastHeight != block.Position.Height-1 { - log.Error("Check confirmed block height fail", "chain", block.Position.ChainID, "height", block.Position.Height-1) + log.Error("Check confirmed block height fail", "chain", block.Position.ChainID, "height", block.Position.Height-1, "cache height", chainLastHeight) return coreTypes.VerifyRetryLater } } @@ -353,11 +325,8 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) coreTypes.BlockVerifySta var expectNonce uint64 // get last nonce from confirmed blocks - lastConfirmedNonce, empty, err := d.blockchain.GetLastNonceFromConfirmedBlocks(block.Position.ChainID, address) - if err != nil { - log.Error("Get last nonce from confirmed blocks", "error", err) - return coreTypes.VerifyInvalidBlock - } else if empty { + lastConfirmedNonce, exist := d.blockchain.GetLastNonceInConfirmedBlocks(address) + if !exist { // get expect nonce from latest state when confirmed block is empty expectNonce = latestState.GetNonce(address) } else { @@ -373,37 +342,12 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) coreTypes.BlockVerifySta // get balance from state addressesBalance := map[common.Address]*big.Int{} for address := range addresses { - addressesBalance[address] = latestState.GetBalance(address) - } - - // replay confirmed block tx to correct balance - confirmedBlocks := d.blockchain.GetConfirmedBlocksByChainID(block.Position.ChainID) - for _, block := range confirmedBlocks { - var txs types.Transactions - err := rlp.Decode(bytes.NewReader(block.Payload), &txs) - if err != nil { - log.Error("Decode confirmed block", "error", err) - return coreTypes.VerifyInvalidBlock - } - - for _, tx := range txs { - msg, err := tx.AsMessage(types.MakeSigner(d.blockchain.Config(), new(big.Int))) - if err != nil { - log.Error("Tx to message", "error", err) - return coreTypes.VerifyInvalidBlock - } - - balance, exist := addressesBalance[msg.From()] - if exist { - maxGasUsed := new(big.Int).Mul(new(big.Int).SetUint64(msg.Gas()), msg.GasPrice()) - balance = new(big.Int).Sub(balance, maxGasUsed) - balance = new(big.Int).Sub(balance, msg.Value()) - if balance.Cmp(big.NewInt(0)) <= 0 { - log.Error("Replay confirmed tx fail", "reason", "not enough balance") - return coreTypes.VerifyInvalidBlock - } - addressesBalance[msg.From()] = balance - } + // replay confirmed block tx to correct balance + cost, exist := d.blockchain.GetCostInConfirmedBlocks(address) + if exist { + addressesBalance[address] = new(big.Int).Sub(latestState.GetBalance(address), cost) + } else { + addressesBalance[address] = latestState.GetBalance(address) } } @@ -428,8 +372,7 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) coreTypes.BlockVerifySta return coreTypes.VerifyInvalidBlock } - balance = new(big.Int).Sub(balance, maxGasUsed) - balance = new(big.Int).Sub(balance, msg.Value()) + balance = new(big.Int).Sub(balance, tx.Cost()) if balance.Cmp(big.NewInt(0)) < 0 { log.Error("Tx fail", "reason", "not enough balance") return coreTypes.VerifyInvalidBlock @@ -457,6 +400,9 @@ func (d *DexconApp) BlockDelivered(blockHash coreCommon.Hash, result coreTypes.F panic("Can not get confirmed block") } + d.chainLock(block.Position.ChainID) + defer d.chainUnlock(block.Position.ChainID) + var transactions types.Transactions err := rlp.Decode(bytes.NewReader(block.Payload), &transactions) if err != nil { @@ -489,15 +435,14 @@ func (d *DexconApp) BlockDelivered(blockHash coreCommon.Hash, result coreTypes.F } log.Info("Insert pending block success", "height", result.Height) - d.chainHeight[block.Position.ChainID] = block.Position.Height d.blockchain.RemoveConfirmedBlock(blockHash) d.notify(result.Height) } // BlockConfirmed is called when a block is confirmed and added to lattice. func (d *DexconApp) BlockConfirmed(block coreTypes.Block) { - d.insertMu.Lock() - defer d.insertMu.Unlock() + d.chainLock(block.Position.ChainID) + defer d.chainUnlock(block.Position.ChainID) d.blockchain.AddConfirmedBlock(&block) } @@ -525,3 +470,18 @@ func (d *DexconApp) validateNonce(txs types.Transactions) (map[common.Address]ui return addressFirstNonce, nil } + +func (d *DexconApp) chainLock(chainID uint32) { + d.chainLocksInitMu.Lock() + _, exist := d.chainLocks[chainID] + if !exist { + d.chainLocks[chainID] = &sync.Mutex{} + } + d.chainLocksInitMu.Unlock() + + d.chainLocks[chainID].Lock() +} + +func (d *DexconApp) chainUnlock(chainID uint32) { + d.chainLocks[chainID].Unlock() +} diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index f8af995f4..93906f35f 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -769,7 +769,7 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h hi = uint64(args.Gas) } else { // Retrieve the current pending block to act as the gas ceiling - block, err := s.b.BlockByNumber(ctx, rpc.PendingBlockNumber) + block, err := s.b.BlockByNumber(ctx, rpc.LatestBlockNumber) if err != nil { return 0, err } @@ -897,6 +897,7 @@ func RPCMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]inter "randomness": hexutil.Bytes(head.Randomness), "round": hexutil.Uint64(head.Round), "dexconMeta": hexutil.Bytes(head.DexconMeta), + "blockReward": (*hexutil.Big)(head.BlockReward), } if inclTx { -- cgit v1.2.3