aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBojie Wu <bojie@dexon.org>2018-10-09 13:28:45 +0800
committerWei-Ning Huang <w@dexon.org>2019-04-09 21:32:49 +0800
commit9b6a659b4de7c9b1c2f331b880a1397159181600 (patch)
treea79f3da3c2fdb8bf32d20faffa2d0cc6f9424de2
parentd7127cb517f29e727a48a588484834d7f242aadb (diff)
downloaddexon-9b6a659b4de7c9b1c2f331b880a1397159181600.tar.gz
dexon-9b6a659b4de7c9b1c2f331b880a1397159181600.tar.zst
dexon-9b6a659b4de7c9b1c2f331b880a1397159181600.zip
dex: implement dexon application interface
-rw-r--r--consensus/dexcon/dexcon.go7
-rw-r--r--core/tx_pool.go4
-rw-r--r--dex/app.go223
-rw-r--r--dex/backend.go70
-rw-r--r--dex/config.go9
5 files changed, 295 insertions, 18 deletions
diff --git a/consensus/dexcon/dexcon.go b/consensus/dexcon/dexcon.go
index 9004106aa..919e920dc 100644
--- a/consensus/dexcon/dexcon.go
+++ b/consensus/dexcon/dexcon.go
@@ -27,6 +27,8 @@ import (
"github.com/dexon-foundation/dexon/rpc"
)
+var blockReward = big.NewInt(5e+18)
+
// Config is the configuration for DEXON consensus.
type Config struct {
}
@@ -99,7 +101,10 @@ func (d *Dexcon) Prepare(chain consensus.ChainReader, header *types.Header) erro
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
// rewards given, and returns the final block.
func (d *Dexcon) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
- return nil, nil
+ state.AddBalance(header.Coinbase, blockReward)
+ header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
+
+ return types.NewBlock(header, txs, uncles, receipts), nil
}
// Seal implements consensus.Engine, attempting to create a sealed block using
diff --git a/core/tx_pool.go b/core/tx_pool.go
index eccf82e93..d79512d9c 100644
--- a/core/tx_pool.go
+++ b/core/tx_pool.go
@@ -1157,6 +1157,10 @@ func (pool *TxPool) demoteUnexecutables() {
}
}
+func (pool *TxPool) ValidateTx(tx *types.Transaction, local bool) error {
+ return pool.validateTx(tx, local)
+}
+
// addressByHeartbeat is an account address tagged with its last activity timestamp.
type addressByHeartbeat struct {
address common.Address
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)
}
diff --git a/dex/backend.go b/dex/backend.go
index 707b1abbb..b74636de4 100644
--- a/dex/backend.go
+++ b/dex/backend.go
@@ -18,17 +18,23 @@
package dex
import (
+ "fmt"
+
dexCore "github.com/dexon-foundation/dexon-consensus-core/core"
"github.com/dexon-foundation/dexon-consensus-core/core/blockdb"
"github.com/dexon-foundation/dexon-consensus-core/core/crypto/ecdsa"
"github.com/dexon-foundation/dexon/accounts"
"github.com/dexon-foundation/dexon/consensus"
+ "github.com/dexon-foundation/dexon/consensus/dexcon"
"github.com/dexon-foundation/dexon/core"
"github.com/dexon-foundation/dexon/core/bloombits"
+ "github.com/dexon-foundation/dexon/core/rawdb"
+ "github.com/dexon-foundation/dexon/core/vm"
"github.com/dexon-foundation/dexon/ethdb"
"github.com/dexon-foundation/dexon/event"
"github.com/dexon-foundation/dexon/internal/ethapi"
+ "github.com/dexon-foundation/dexon/log"
"github.com/dexon-foundation/dexon/node"
"github.com/dexon-foundation/dexon/p2p"
"github.com/dexon-foundation/dexon/params"
@@ -72,7 +78,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Dexon, error) {
if err != nil {
panic(err)
}
- app := NewDexconApp(nil)
gov := NewDexconGovernance()
network := NewDexconNetwork()
@@ -81,8 +86,26 @@ func New(ctx *node.ServiceContext, config *Config) (*Dexon, error) {
if err != nil {
panic(err)
}
- consensus := dexCore.NewConsensus(app, gov, db, network, privKey)
+ chainDb, err := CreateDB(ctx, config, "chaindata")
+ if err != nil {
+ return nil, err
+ }
+ chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb,
+ config.Genesis)
+ if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
+ return nil, genesisErr
+ }
+ log.Info("Initialised chain configuration", "config", chainConfig)
+
+ if !config.SkipBcVersionCheck {
+ bcVersion := rawdb.ReadDatabaseVersion(chainDb)
+ if bcVersion != nil && *bcVersion != core.BlockChainVersion {
+ return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d).\n",
+ bcVersion, core.BlockChainVersion)
+ }
+ rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)
+ }
dex := &Dexon{
config: config,
eventMux: ctx.EventMux,
@@ -90,12 +113,39 @@ func New(ctx *node.ServiceContext, config *Config) (*Dexon, error) {
shutdownChan: make(chan bool),
networkID: config.NetworkId,
bloomRequests: make(chan chan *bloombits.Retrieval),
- app: app,
governance: gov,
network: network,
blockdb: db,
- consensus: consensus,
+ engine: dexcon.New(&params.DexconConfig{}),
+ }
+
+ var (
+ vmConfig = vm.Config{
+ EnablePreimageRecording: config.EnablePreimageRecording,
+ EWASMInterpreter: config.EWASMInterpreter,
+ EVMInterpreter: config.EVMInterpreter,
+ }
+ cacheConfig = &core.CacheConfig{Disabled: config.NoPruning, TrieCleanLimit: config.TrieCleanCache, TrieDirtyLimit: config.TrieDirtyCache, TrieTimeLimit: config.TrieTimeout}
+ )
+ dex.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, dex.chainConfig, dex.engine, vmConfig, nil)
+
+ // Rewind the chain in case of an incompatible config upgrade.
+ if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
+ log.Warn("Rewinding chain to upgrade configuration", "err", compat)
+ dex.blockchain.SetHead(compat.RewindTo)
+ rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig)
+ }
+ dex.bloomIndexer.Start(dex.blockchain)
+
+ if config.TxPool.Journal != "" {
+ config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal)
}
+ dex.txPool = core.NewTxPool(config.TxPool, dex.chainConfig, dex.blockchain)
+
+ dex.app = NewDexconApp(dex.txPool, dex.blockchain, gov, chainDb, config, vmConfig)
+
+ dex.consensus = dexCore.NewConsensus(dex.app, gov, db, network, privKey)
+
return dex, nil
}
@@ -114,3 +164,15 @@ func (s *Dexon) Start(server *p2p.Server) error {
func (s *Dexon) Stop() error {
return nil
}
+
+// CreateDB creates the chain database.
+func CreateDB(ctx *node.ServiceContext, config *Config, name string) (ethdb.Database, error) {
+ db, err := ctx.OpenDatabase(name, config.DatabaseCache, config.DatabaseHandles)
+ if err != nil {
+ return nil, err
+ }
+ if db, ok := db.(*ethdb.LDBDatabase); ok {
+ db.Meter("eth/db/chaindata/")
+ }
+ return db, nil
+}
diff --git a/dex/config.go b/dex/config.go
index fa9988bfb..5a43496ab 100644
--- a/dex/config.go
+++ b/dex/config.go
@@ -45,6 +45,10 @@ var DefaultConfig = Config{
Blocks: 20,
Percentile: 60,
},
+
+ GasFloor: 8000000,
+ GasCeil: 8000000,
+ GasLimitTolerance: 1000000,
}
func init() {
@@ -86,6 +90,11 @@ type Config struct {
TrieDirtyCache int
TrieTimeout time.Duration
+ // For calculate gas limit
+ GasFloor uint64
+ GasCeil uint64
+ GasLimitTolerance uint64
+
// Dexcon options
Dexcon dexcon.Config