diff options
author | Bojie Wu <bojie@dexon.org> | 2018-10-09 13:28:45 +0800 |
---|---|---|
committer | Wei-Ning Huang <w@dexon.org> | 2019-04-09 21:32:50 +0800 |
commit | 0cf107b225f25602527ddda3f1897f182ebb205a (patch) | |
tree | 536f6c1f1656cd71e337592d4be3ae5f71bbd033 /dex | |
parent | 72342301371f9f4d7a9fe256efa072c5c41d00d3 (diff) | |
download | dexon-0cf107b225f25602527ddda3f1897f182ebb205a.tar.gz dexon-0cf107b225f25602527ddda3f1897f182ebb205a.tar.zst dexon-0cf107b225f25602527ddda3f1897f182ebb205a.zip |
app: implement verify block logic
Diffstat (limited to 'dex')
-rw-r--r-- | dex/app.go | 217 |
1 files changed, 195 insertions, 22 deletions
diff --git a/dex/app.go b/dex/app.go index ab23301d5..40366c25f 100644 --- a/dex/app.go +++ b/dex/app.go @@ -49,8 +49,10 @@ type DexconApp struct { notifyChan map[uint64]*notify mutex *sync.Mutex - lastHeight uint64 - insertMu sync.Mutex + lastPendingHeight uint64 + insertMu sync.Mutex + + chainHeight map[uint32]uint64 } type notify struct { @@ -65,14 +67,15 @@ 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{}, + 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), } } @@ -100,7 +103,7 @@ func (d *DexconApp) notify(height uint64) { delete(d.notifyChan, h) } } - d.lastHeight = height + d.lastPendingHeight = height } func (d *DexconApp) checkChain(address common.Address, chainSize, chainID *big.Int) bool { @@ -118,17 +121,17 @@ func (d *DexconApp) PreparePayload(position coreTypes.Position) (payload []byte, } chainID := new(big.Int).SetUint64(uint64(position.ChainID)) - chainSize := new(big.Int).SetUint64(uint64(d.gov.GetNumChains(position.Round))) + chainNums := new(big.Int).SetUint64(uint64(d.gov.GetNumChains(position.Round))) var allTxs types.Transactions for addr, txs := range txsMap { // every address's transactions will appear in fixed chain - if !d.checkChain(addr, chainSize, chainID) { + if !d.checkChain(addr, chainNums, chainID) { continue } var stateDB *state.StateDB - if d.lastHeight > 0 { - stateDB, err = d.blockchain.StateAt(d.blockchain.GetPendingBlockByHeight(d.lastHeight).Root()) + if d.lastPendingHeight > 0 { + stateDB, err = d.blockchain.StateAt(d.blockchain.GetPendingBlockByHeight(d.lastPendingHeight).Root()) if err != nil { return nil, fmt.Errorf("PreparePayload d.blockchain.StateAt err %v", err) } @@ -158,12 +161,11 @@ func (d *DexconApp) PreparePayload(position coreTypes.Position) (payload []byte, // PrepareWitness will return the witness data no lower than consensusHeight. func (d *DexconApp) PrepareWitness(consensusHeight uint64) (witness coreTypes.Witness, err error) { - // TODO(bojie): the witness logic need to correct var witnessBlock *types.Block - if d.lastHeight == 0 && consensusHeight == 0 { + if d.lastPendingHeight == 0 && consensusHeight == 0 { witnessBlock = d.blockchain.CurrentBlock() - } else if d.lastHeight >= consensusHeight { - witnessBlock = d.blockchain.GetPendingBlockByHeight(d.lastHeight) + } else if d.lastPendingHeight >= consensusHeight { + witnessBlock = d.blockchain.GetPendingBlockByHeight(d.lastPendingHeight) } else if h := <-d.addNotify(consensusHeight); h >= consensusHeight { witnessBlock = d.blockchain.GetPendingBlockByHeight(h) } else { @@ -189,7 +191,152 @@ func (d *DexconApp) PrepareWitness(consensusHeight uint64) (witness coreTypes.Wi // VerifyBlock verifies if the payloads are valid. func (d *DexconApp) VerifyBlock(block *coreTypes.Block) bool { - // TODO(bojie): implement this + d.insertMu.Lock() + defer d.insertMu.Unlock() + + var witnessData witnessData + err := rlp.Decode(bytes.NewReader(block.Witness.Data), &witnessData) + if err != nil { + log.Error("Witness rlp decode", "error", err) + return false + } + + // check witness root exist + _, err = d.blockchain.StateAt(witnessData.Root) + if err != nil { + log.Error("Get state root error", "err", err) + return false + } + + 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 false + } + } + + // check if chain block height is sequential + if chainLastHeight != block.Position.Height-1 { + log.Error("Check confirmed block height fail", "chain", block.Position.ChainID, "height", block.Position.Height-1) + return false + } + } + + // set state to the pending height + var latestState *state.StateDB + if d.lastPendingHeight == 0 { + latestState, err = d.blockchain.State() + if err != nil { + log.Error("Get current state", "error", err) + return false + } + } else { + latestState, err = d.blockchain.StateAt(d.blockchain.GetPendingBlockByHeight(d.lastPendingHeight).Root()) + if err != nil { + log.Error("Get pending state", "error", err) + return false + } + } + + var transactions types.Transactions + err = rlp.Decode(bytes.NewReader(block.Payload), &transactions) + if err != nil { + log.Error("Payload rlp decode", "error", err) + return false + } + + // check if nonce is sequential and return first nonce of every address + addresses, err := d.validateNonce(transactions) + if err != nil { + log.Error("Get address nonce", "error", err) + return false + } + + // check all address nonce + chainID := big.NewInt(int64(block.Position.ChainID)) + chainNums := new(big.Int).SetUint64(uint64(d.gov.GetNumChains(block.Position.Round))) + for address, firstNonce := range addresses { + if !d.checkChain(address, chainNums, chainID) { + log.Error("check chain fail", "address", address) + return false + } + + 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 false + } else if empty { + // get expect nonce from latest state when confirmed block is empty + expectNonce = latestState.GetNonce(address) + } else { + expectNonce = lastConfirmedNonce + 1 + } + + if expectNonce != firstNonce { + log.Error("Nonce check error", "expect", expectNonce, "firstNonce", firstNonce) + return false + } + } + + // 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 false + } + + 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 false + } + + 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) + if balance.Cmp(big.NewInt(0)) <= 0 { + log.Error("Replay confirmed tx fail", "reason", "not enough balance") + return false + } + addressesBalance[msg.From()] = balance + } + } + } + + // validate tx to check available balance + for _, tx := range transactions { + msg, err := tx.AsMessage(types.MakeSigner(d.blockchain.Config(), new(big.Int))) + if err != nil { + log.Error("Tx to message", "error", err) + return false + } + balance, _ := addressesBalance[msg.From()] + maxGasUsed := new(big.Int).Mul(new(big.Int).SetUint64(msg.Gas()), msg.GasPrice()) + balance = new(big.Int).Sub(balance, maxGasUsed) + if balance.Cmp(big.NewInt(0)) <= 0 { + log.Error("Tx fail", "reason", "not enough balance") + return false + } + addressesBalance[msg.From()] = balance + } + return true } @@ -227,19 +374,21 @@ func (d *DexconApp) BlockDelivered(blockHash coreCommon.Hash, result coreTypes.F WitnessHeight: block.Witness.Height, WitnessRoot: witnessData.Root, WitnessReceiptHash: witnessData.ReceiptHash, + ChainID: block.Position.ChainID, + ChainBlockHeight: block.Position.Height, // TODO(bojie): fix it GasLimit: 8000000, Difficulty: big.NewInt(1), }, transactions, nil, nil) - _, err = d.blockchain.InsertPendingBlock([]*types.Block{newBlock}) + _, err = d.blockchain.InsertPendingBlocks([]*types.Block{newBlock}) if err != nil { log.Error("Insert chain", "error", err) return } log.Debug("Insert pending block success", "height", result.Height) - + d.chainHeight[block.Position.ChainID] = block.Position.Height d.blockchain.RemoveConfirmedBlock(blockHash) d.notify(result.Height) } @@ -248,3 +397,27 @@ func (d *DexconApp) BlockDelivered(blockHash coreCommon.Hash, result coreTypes.F func (d *DexconApp) BlockConfirmed(block coreTypes.Block) { d.blockchain.AddConfirmedBlock(&block) } + +func (d *DexconApp) validateNonce(txs types.Transactions) (map[common.Address]uint64, error) { + addressFirstNonce := map[common.Address]uint64{} + addressNonce := map[common.Address]uint64{} + for _, tx := range txs { + msg, err := tx.AsMessage(types.MakeSigner(d.blockchain.Config(), new(big.Int))) + if err != nil { + return nil, err + } + + if _, exist := addressFirstNonce[msg.From()]; exist { + if addressNonce[msg.From()]+1 != msg.Nonce() { + return nil, fmt.Errorf("address nonce check error: expect %v actual %v", addressNonce[msg.From()]+1, msg.Nonce()) + } + addressNonce[msg.From()] = msg.Nonce() + continue + } else { + addressNonce[msg.From()] = msg.Nonce() + addressFirstNonce[msg.From()] = msg.Nonce() + } + } + + return addressFirstNonce, nil +} |