aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/blockchain.go50
-rw-r--r--core/types/block.go2
-rw-r--r--dex/app.go217
3 files changed, 246 insertions, 23 deletions
diff --git a/core/blockchain.go b/core/blockchain.go
index b66c0928f..e59c36653 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -254,6 +254,9 @@ func (bc *BlockChain) RemoveConfirmedBlock(hash coreCommon.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) GetConfirmedBlockByHash(hash coreCommon.Hash) *coreTypes.Block {
@@ -283,6 +286,51 @@ func (bc *BlockChain) GetConfirmedTxsByAddress(chainID uint32, address common.Ad
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 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
+ }
+
+ if msg.From() == address {
+ return msg.Nonce(), false, nil
+ }
+ }
+ }
+
+ return 0, true, nil
+}
+
+func (bc *BlockChain) GetChainLastConfirmedHeight(chainID uint32) (uint64, bool) {
+ bc.confirmedBlockMu.Lock()
+ defer bc.confirmedBlockMu.Unlock()
+
+ chainBlocks := bc.chainConfirmedBlocks[chainID]
+ size := len(chainBlocks)
+ if size == 0 {
+ return 0, true
+ }
+
+ return chainBlocks[size-1].Position.Height, false
+}
+
+func (bc *BlockChain) GetConfirmedBlocksByChainID(chainID uint32) []*coreTypes.Block {
+ return bc.chainConfirmedBlocks[chainID]
+}
+
// 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 {
@@ -1459,7 +1507,7 @@ func (bc *BlockChain) insertSidechain(block *types.Block, it *insertIterator) (i
return 0, nil, nil, nil
}
-func (bc *BlockChain) InsertPendingBlock(chain types.Blocks) (int, error) {
+func (bc *BlockChain) InsertPendingBlocks(chain types.Blocks) (int, error) {
n, events, logs, err := bc.insertPendingBlocks(chain)
bc.PostChainEvents(events, logs)
return n, err
diff --git a/core/types/block.go b/core/types/block.go
index b814bd975..02d52f30f 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -90,6 +90,8 @@ type Header struct {
WitnessHeight uint64 `json:"witnessHeight" gencodec:"required"`
WitnessRoot common.Hash `json:"WitnessRoot" gencodec:"required"`
WitnessReceiptHash common.Hash `json:"WitnessReceiptHash" gencodec:"required"`
+ ChainID uint32 `json:"chainID" gencodec:"required"`
+ ChainBlockHeight uint64 `json:"chainBlockHeight" gencodec:"required"`
}
// field type overrides for gencodec
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
+}