aboutsummaryrefslogtreecommitdiffstats
path: root/dex/app.go
diff options
context:
space:
mode:
Diffstat (limited to 'dex/app.go')
-rw-r--r--dex/app.go223
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] = &notify{}
+ 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)
}