diff options
-rw-r--r-- | core/blockchain.go | 56 | ||||
-rw-r--r-- | dex/app.go | 131 |
2 files changed, 160 insertions, 27 deletions
diff --git a/core/blockchain.go b/core/blockchain.go index 6a196d78d..9a5f35037 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -18,6 +18,7 @@ package core import ( + "bytes" "errors" "fmt" "io" @@ -27,6 +28,9 @@ import ( "sync/atomic" "time" + coreCommon "github.com/dexon-foundation/dexon-consensus-core/common" + coreTypes "github.com/dexon-foundation/dexon-consensus-core/core/types" + "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/common/mclock" "github.com/dexon-foundation/dexon/common/prque" @@ -136,6 +140,10 @@ 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 + confirmedBlock map[coreCommon.Hash]*coreTypes.Block + filteredConfirmedBlock map[uint32]map[coreCommon.Hash]*coreTypes.Block } // NewBlockChain returns a fully initialised block chain using information @@ -215,6 +223,54 @@ func (bc *BlockChain) GetVMConfig() *vm.Config { return &bc.vmConfig } +func (bc *BlockChain) AddConfirmedBlock(block *coreTypes.Block) { + bc.confirmedBlockMu.Lock() + defer bc.confirmedBlockMu.Unlock() + + bc.confirmedBlock[block.Hash] = block + bc.filteredConfirmedBlock[block.Position.ChainID][block.Hash] = block +} + +func (bc *BlockChain) RemoveConfirmedBlock(hash coreCommon.Hash) { + bc.confirmedBlockMu.Lock() + defer bc.confirmedBlockMu.Unlock() + + block := bc.confirmedBlock[hash] + delete(bc.filteredConfirmedBlock[block.Position.ChainID], block.Hash) + delete(bc.confirmedBlock, block.Hash) +} + +func (bc *BlockChain) GetConfirmedBlockByHash(hash coreCommon.Hash) *coreTypes.Block { + return bc.confirmedBlock[hash] +} + +func (bc *BlockChain) GetNonceInConfirmedBlock(chainID uint32, address common.Address) (uint64, bool, error) { + var nonce uint64 + var init bool + for _, block := range bc.filteredConfirmedBlock[chainID] { + var transactions types.Transactions + err := rlp.Decode(bytes.NewReader(block.Payload), &transactions) + if err != nil { + return 0, init, err + } + + for _, tx := range transactions { + msg, err := tx.AsMessage(types.MakeSigner(bc.chainConfig, new(big.Int))) + if err != nil { + return 0, init, err + } + + if msg.From() == address && (tx.Nonce() > nonce || !init) { + if !init { + init = true + } + nonce = tx.Nonce() + } + } + } + return nonce, init, nil +} + // 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 b34c9f553..521f2233e 100644 --- a/dex/app.go +++ b/dex/app.go @@ -19,12 +19,11 @@ package dex import ( "bytes" + "github.com/dexon-foundation/dexon/core/rawdb" "math/big" "sync" "time" - "github.com/dexon-foundation/dexon/core/rawdb" - coreCommon "github.com/dexon-foundation/dexon-consensus-core/common" coreTypes "github.com/dexon-foundation/dexon-consensus-core/core/types" @@ -119,12 +118,43 @@ func (d *DexconApp) PreparePayload(position coreTypes.Position) (payload []byte) continue } - for _, tx := range txs { + var nonce uint64 + for i, tx := range txs { + // validate start nonce + // check undelivered block first + // or else check compaction chain state + if i == 0 { + nonce = tx.Nonce() + msg, err := tx.AsMessage(types.MakeSigner(nil, currentBlock.Header().Number)) + if err != nil { + return + } + undeliveredNonce, exist, err := d.blockchain.GetNonceInConfirmedBlock(position.ChainID, msg.From()) + if err != nil { + return + } else if exist { + if msg.Nonce() != undeliveredNonce+1 { + break + } + } else { + stateDB, err := d.blockchain.State() + if err != nil { + return + } + if msg.Nonce() != stateDB.GetNonce(msg.From())+1 { + break + } + } + } else if tx.Nonce() != nonce+1 { // check if nonce is sequential + break + } + core.ApplyTransaction(d.blockchain.Config(), d.blockchain, nil, gp, stateDB, currentBlock.Header(), tx, &gasUsed, d.vmConfig) if gasUsed > gasLimit { break } allTxs = append(allTxs, tx) + nonce = tx.Nonce() } } payload, err = rlp.EncodeToBytes(&allTxs) @@ -179,14 +209,54 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) bool { return false } + currentBlock := d.blockchain.CurrentBlock() + // verify transactions + addressNonce := map[common.Address]*struct { + Min uint64 + Max uint64 + }{} for _, transaction := range transactions { if d.txPool.ValidateTx(transaction, false) != nil { return false } + + msg, err := transaction.AsMessage(types.MakeSigner(nil, currentBlock.Header().Number)) + if err != nil { + return false + } + nonce, exist := addressNonce[msg.From()] + if !exist { + addressNonce[msg.From()] = &struct { + Min uint64 + Max uint64 + }{Min: msg.Nonce(), Max: msg.Nonce()} + } else if msg.Nonce() != nonce.Max+1 { + // address nonce is not sequential + return false + } + nonce.Max++ + } + + for address, nonce := range addressNonce { + undeliveredNonce, exist, err := d.blockchain.GetNonceInConfirmedBlock(block.Position.ChainID, address) + if err != nil { + return false + } else if exist { + if nonce.Min != undeliveredNonce+1 { + return false + } + } else { + stateDB, err := d.blockchain.State() + if err != nil { + return false + } + if nonce.Min != stateDB.GetNonce(address)+1 { + return false + } + } } - currentBlock := d.blockchain.CurrentBlock() gasLimit := core.CalcGasLimit(currentBlock, d.config.GasFloor, d.config.GasCeil) gp := new(core.GasPool).AddGas(gasLimit) @@ -234,31 +304,38 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) bool { return true } -func (d *DexconApp) BlockConfirmed(block coreTypes.Block) { -} - // BlockDelivered is called when a block is add to the compaction chain. func (d *DexconApp) BlockDelivered(blockHash coreCommon.Hash, result coreTypes.FinalizationResult) { - /* - var transactions types.Transactions - err := rlp.Decode(bytes.NewReader(block.Payload), &transactions) - if err != nil { - return - } + block := d.blockchain.GetConfirmedBlockByHash(blockHash) + if block == nil { + // do something + return + } - _, err = d.blockchain.InsertChain( - []*types.Block{types.NewBlock(&types.Header{ - ParentHash: common.Hash(block.ParentHash), - Number: new(big.Int).SetUint64(result.Height), - Time: new(big.Int).SetInt64(result.Timestamp.Unix()), - TxHash: types.DeriveSha(transactions), - Coinbase: common.BytesToAddress(block.ProposerID.Hash[:]), - }, transactions, nil, nil)}) - if err != nil { - // do something - return - } + var transactions types.Transactions + err := rlp.Decode(bytes.NewReader(block.Payload), &transactions) + if err != nil { + return + } - d.notify(result.Height) - */ + _, err = d.blockchain.InsertChain( + []*types.Block{types.NewBlock(&types.Header{ + ParentHash: common.Hash(block.ParentHash), + Number: new(big.Int).SetUint64(result.Height), + Time: new(big.Int).SetInt64(result.Timestamp.Unix()), + TxHash: types.DeriveSha(transactions), + Coinbase: common.BytesToAddress(block.ProposerID.Hash[:]), + }, transactions, nil, nil)}) + if err != nil { + // do something + return + } + + 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.blockchain.AddConfirmedBlock(&block) } |