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:49 +0800 |
commit | 9b6a659b4de7c9b1c2f331b880a1397159181600 (patch) | |
tree | a79f3da3c2fdb8bf32d20faffa2d0cc6f9424de2 /dex/app.go | |
parent | d7127cb517f29e727a48a588484834d7f242aadb (diff) | |
download | dexon-9b6a659b4de7c9b1c2f331b880a1397159181600.tar.gz dexon-9b6a659b4de7c9b1c2f331b880a1397159181600.tar.zst dexon-9b6a659b4de7c9b1c2f331b880a1397159181600.zip |
dex: implement dexon application interface
Diffstat (limited to 'dex/app.go')
-rw-r--r-- | dex/app.go | 223 |
1 files changed, 210 insertions, 13 deletions
diff --git a/dex/app.go b/dex/app.go index a8b04577d..80384ddc9 100644 --- a/dex/app.go +++ b/dex/app.go @@ -18,37 +18,234 @@ package dex import ( - "github.com/dexon-foundation/dexon-consensus-core/core/types" + "bytes" + "math/big" + "sync" + "time" + coreTypes "github.com/dexon-foundation/dexon-consensus-core/core/types" + + "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/core" + "github.com/dexon-foundation/dexon/core/rawdb" + "github.com/dexon-foundation/dexon/core/state" + "github.com/dexon-foundation/dexon/core/types" + "github.com/dexon-foundation/dexon/core/vm" + "github.com/dexon-foundation/dexon/ethdb" + "github.com/dexon-foundation/dexon/rlp" ) // DexconApp implementes the DEXON consensus core application interface. type DexconApp struct { - txPool *core.TxPool + txPool *core.TxPool + blockchain *core.BlockChain + gov *DexconGovernance + chainDB ethdb.Database + config *Config + vmConfig vm.Config + + notifyChan map[uint64]*notify + mutex *sync.Mutex +} + +type notify struct { + results []chan bool } -func NewDexconApp(txPool *core.TxPool) *DexconApp { +func NewDexconApp(txPool *core.TxPool, blockchain *core.BlockChain, gov *DexconGovernance, chainDB ethdb.Database, config *Config, vmConfig vm.Config) *DexconApp { return &DexconApp{ - txPool: txPool, + txPool: txPool, + blockchain: blockchain, + gov: gov, + chainDB: chainDB, + config: config, + vmConfig: vmConfig, + notifyChan: make(map[uint64]*notify), + mutex: &sync.Mutex{}, } } -// PreparePayload is called when consensus core is preparing a block. -func (d *DexconApp) PreparePayload(position types.Position) []byte { - return nil +func (d *DexconApp) addNotify(height uint64) <-chan bool { + d.mutex.Lock() + defer d.mutex.Unlock() + result := make(chan bool) + if n, exist := d.notifyChan[height]; exist { + n.results = append(n.results, result) + } else { + d.notifyChan[height] = ¬ify{} + d.notifyChan[height].results = append(d.notifyChan[height].results, result) + } + return result } -// PrepareWitness will return the witness data no lower than consensusHeight. -func (d *DexconApp) PrepareWitness(consensusHeight uint64) types.Witness { - return types.Witness{} +func (d *DexconApp) notify(height uint64) { + d.mutex.Lock() + defer d.mutex.Unlock() + for h, n := range d.notifyChan { + if height >= h { + for _, ch := range n.results { + ch <- true + } + delete(d.notifyChan, h) + } + } } -// VerifyPayload verifies if the payloads are valid. -func (d *DexconApp) VerifyBlock(block *types.Block) bool { +// PreparePayload is called when consensus core is preparing payload for block. +func (d *DexconApp) PreparePayload(position coreTypes.Position) (payload []byte) { + txsMap, err := d.txPool.Pending() + if err != nil { + return + } + + currentBlock := d.blockchain.CurrentBlock() + gasLimit := core.CalcGasLimit(currentBlock, d.config.GasFloor, d.config.GasCeil) + gp := new(core.GasPool).AddGas(gasLimit) + + stateDB, err := state.New(currentBlock.Root(), state.NewDatabase(d.chainDB)) + if err != nil { + return + } + + chainID := new(big.Int).SetUint64(uint64(position.ChainID)) + chainSize := new(big.Int).SetUint64(uint64(d.gov.Configuration(position.Round).NumChains)) + var allTxs types.Transactions + var gasUsed uint64 + for addr, txs := range txsMap { + addrModChainSize := new(big.Int) + if addrModChainSize.Mod(addr.Big(), chainSize).Cmp(chainID) != 0 { + continue + } + + for _, tx := range txs { + core.ApplyTransaction(d.blockchain.Config(), d.blockchain, nil, gp, stateDB, currentBlock.Header(), tx, &gasUsed, d.vmConfig) + if gasUsed > gasLimit { + break + } + allTxs = append(allTxs, tx) + } + } + payload, err = rlp.EncodeToBytes(&allTxs) + if err != nil { + // do something + return + } + + return +} + +type WitnessData struct { + Root common.Hash + TxHash common.Hash + ReceiptHash common.Hash +} + +func (d *DexconApp) PrepareWitness(consensusHeight uint64) (witness coreTypes.Witness) { + var currentBlock *types.Block + currentBlock = d.blockchain.CurrentBlock() + if currentBlock.NumberU64() < consensusHeight { + // wait notification + if <-d.addNotify(consensusHeight) { + currentBlock = d.blockchain.CurrentBlock() + } else { + // do something if notify fail + } + } + + witnessData, err := rlp.EncodeToBytes(&WitnessData{ + Root: currentBlock.Root(), + TxHash: currentBlock.TxHash(), + ReceiptHash: currentBlock.ReceiptHash(), + }) + if err != nil { + return + } + + return coreTypes.Witness{ + Timestamp: time.Unix(currentBlock.Time().Int64(), 0), + Height: currentBlock.NumberU64(), + Data: witnessData, + } +} + +// VerifyBlock verifies if the payloads are valid. +func (d *DexconApp) VerifyBlock(block *coreTypes.Block) bool { + // decode payload to transactions + var transactions types.Transactions + err := rlp.Decode(bytes.NewReader(block.Payload), &transactions) + if err != nil { + return false + } + + // verify transactions + for _, transaction := range transactions { + tx, _, _, _ := rawdb.ReadTransaction(d.chainDB, transaction.Hash()) + if tx == nil || d.txPool.ValidateTx(transaction, false) != nil { + return false + } + } + + currentBlock := d.blockchain.CurrentBlock() + gasLimit := core.CalcGasLimit(currentBlock, d.config.GasFloor, d.config.GasCeil) + gp := new(core.GasPool).AddGas(gasLimit) + + stateDB, err := state.New(currentBlock.Root(), state.NewDatabase(d.chainDB)) + if err != nil { + return false + } + + var gasUsed uint64 + for _, tx := range transactions { + core.ApplyTransaction(d.blockchain.Config(), d.blockchain, nil, gp, stateDB, currentBlock.Header(), tx, &gasUsed, d.vmConfig) + } + + if gasUsed > gasLimit+d.config.GasLimitTolerance { + return false + } + + witnessData := WitnessData{} + err = rlp.Decode(bytes.NewReader(block.Witness.Data), &witnessData) + if err != nil { + return false + } + + witnessBlock := d.blockchain.GetBlockByNumber(block.Witness.Height) + if witnessBlock == nil { + return false + } else if witnessBlock.Root() != witnessData.Root { + // invalid state root of witness data + return false + } else if witnessBlock.ReceiptHash() != witnessData.ReceiptHash { + // invalid receipt root of witness data + return false + } else if witnessBlock.TxHash() != witnessData.ReceiptHash { + // invalid tx root of witness data + return false + } + return true } // BlockDelivered is called when a block is add to the compaction chain. -func (d *DexconApp) BlockDelivered(block types.Block) { +func (d *DexconApp) BlockDelivered(block coreTypes.Block) { + var transactions types.Transactions + err := rlp.Decode(bytes.NewReader(block.Payload), &transactions) + if err != nil { + return + } + + _, err = d.blockchain.InsertChain( + []*types.Block{types.NewBlock(&types.Header{ + ParentHash: common.Hash(block.ParentHash), + Number: new(big.Int).SetUint64(block.ConsensusHeight), + Time: new(big.Int).SetInt64(block.ConsensusTimestamp.Unix()), + TxHash: types.DeriveSha(transactions), + Coinbase: common.BytesToAddress(block.ProposerID.Hash[:]), + }, transactions, nil, nil)}) + if err != nil { + // do something + return + } + + d.notify(block.ConsensusHeight) } |