aboutsummaryrefslogtreecommitdiffstats
path: root/dex
diff options
context:
space:
mode:
authorbojie <bojie@dexon.org>2019-03-07 17:35:31 +0800
committerWei-Ning Huang <w@byzantine-lab.io>2019-06-12 17:27:22 +0800
commit70a3d62d99f09b04f28c2f3542f90b502f4c2779 (patch)
treece27dc002a158b75c2d49ee0313ef5ef358902a5 /dex
parentdd570bf6d35cdfcbfe4df3aa52755cf2735bb63e (diff)
downloadgo-tangerine-70a3d62d99f09b04f28c2f3542f90b502f4c2779.tar
go-tangerine-70a3d62d99f09b04f28c2f3542f90b502f4c2779.tar.gz
go-tangerine-70a3d62d99f09b04f28c2f3542f90b502f4c2779.tar.bz2
go-tangerine-70a3d62d99f09b04f28c2f3542f90b502f4c2779.tar.lz
go-tangerine-70a3d62d99f09b04f28c2f3542f90b502f4c2779.tar.xz
go-tangerine-70a3d62d99f09b04f28c2f3542f90b502f4c2779.tar.zst
go-tangerine-70a3d62d99f09b04f28c2f3542f90b502f4c2779.zip
app: refactor app run as single chain (#226)
Diffstat (limited to 'dex')
-rw-r--r--dex/app.go274
1 files changed, 145 insertions, 129 deletions
diff --git a/dex/app.go b/dex/app.go
index e7fdbb4fe..eabdf52f5 100644
--- a/dex/app.go
+++ b/dex/app.go
@@ -18,6 +18,7 @@
package dex
import (
+ "bytes"
"context"
"fmt"
"math/big"
@@ -47,57 +48,31 @@ type DexconApp struct {
finalizedBlockFeed event.Feed
scope event.SubscriptionScope
- chainLocks sync.Map
- chainRoot sync.Map
+ appMu sync.RWMutex
+
+ confirmedBlocks map[coreCommon.Hash]*blockInfo
+ addressNonce map[common.Address]uint64
+ addressCost map[common.Address]*big.Int
+ addressCounter map[common.Address]uint64
+ undeliveredNum uint64
+ deliveredHeight uint64
}
func NewDexconApp(txPool *core.TxPool, blockchain *core.BlockChain, gov *DexconGovernance,
chainDB ethdb.Database, config *Config) *DexconApp {
return &DexconApp{
- txPool: txPool,
- blockchain: blockchain,
- gov: gov,
- chainDB: chainDB,
- config: config,
+ txPool: txPool,
+ blockchain: blockchain,
+ gov: gov,
+ chainDB: chainDB,
+ config: config,
+ confirmedBlocks: map[coreCommon.Hash]*blockInfo{},
+ addressNonce: map[common.Address]uint64{},
+ addressCost: map[common.Address]*big.Int{},
+ addressCounter: map[common.Address]uint64{},
}
}
-func (d *DexconApp) addrBelongsToChain(address common.Address, chainSize, chainID *big.Int) bool {
- return true
-}
-
-func (d *DexconApp) chainLock(chainID uint32) {
- v, ok := d.chainLocks.Load(chainID)
- if !ok {
- v, _ = d.chainLocks.LoadOrStore(chainID, &sync.RWMutex{})
- }
- v.(*sync.RWMutex).Lock()
-}
-
-func (d *DexconApp) chainUnlock(chainID uint32) {
- v, ok := d.chainLocks.Load(chainID)
- if !ok {
- panic(fmt.Errorf("chain %v is not init yet", chainID))
- }
- v.(*sync.RWMutex).Unlock()
-}
-
-func (d *DexconApp) chainRLock(chainID uint32) {
- v, ok := d.chainLocks.Load(chainID)
- if !ok {
- v, _ = d.chainLocks.LoadOrStore(chainID, &sync.RWMutex{})
- }
- v.(*sync.RWMutex).RLock()
-}
-
-func (d *DexconApp) chainRUnlock(chainID uint32) {
- v, ok := d.chainLocks.Load(chainID)
- if !ok {
- panic(fmt.Errorf("chain %v is not init yet", chainID))
- }
- v.(*sync.RWMutex).RUnlock()
-}
-
// validateNonce check if nonce is in order and return first nonce of every address.
func (d *DexconApp) validateNonce(txs types.Transactions) (map[common.Address]uint64, error) {
addressFirstNonce := map[common.Address]uint64{}
@@ -177,8 +152,8 @@ func (d *DexconApp) PreparePayload(position coreTypes.Position) (payload []byte,
func (d *DexconApp) preparePayload(ctx context.Context, position coreTypes.Position) (
payload []byte, err error) {
- d.chainRLock(uint32(0))
- defer d.chainRUnlock(uint32(0))
+ d.appMu.RLock()
+ defer d.appMu.RUnlock()
select {
// This case will hit if previous RLock took too much time.
case <-ctx.Done():
@@ -186,37 +161,27 @@ func (d *DexconApp) preparePayload(ctx context.Context, position coreTypes.Posit
default:
}
- if position.Height != 0 {
- // Check if chain block height is strictly increamental.
- chainLastHeight, ok := d.blockchain.GetChainLastConfirmedHeight(uint32(0))
- if !ok || chainLastHeight != position.Height-1 {
- log.Debug("Previous confirmed block not exists", "current pos", position.String(),
- "prev height", chainLastHeight, "ok", ok)
- return nil, fmt.Errorf("previous block not exists")
- }
- }
-
- root, exist := d.chainRoot.Load(uint32(0))
- if !exist {
- return nil, nil
+ // deliver height = position height + 1
+ if d.deliveredHeight+d.undeliveredNum != position.Height {
+ return nil, fmt.Errorf("expected height %d but get %d", d.deliveredHeight+d.undeliveredNum, position.Height)
}
- currentState, err := d.blockchain.StateAt(*root.(*common.Hash))
+ deliveredBlock := d.blockchain.GetBlockByNumber(d.deliveredHeight)
+ state, err := d.blockchain.StateAt(deliveredBlock.Root())
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("get state by root %v error: %v", deliveredBlock.Root(), err)
}
- log.Debug("Prepare payload", "chain", uint32(0), "height", position.Height)
+
+ log.Debug("Prepare payload", "height", position.Height)
txsMap, err := d.txPool.Pending()
if err != nil {
return
}
- chainID := new(big.Int).SetUint64(uint64(uint32(0)))
- chainNums := new(big.Int).SetUint64(uint64(d.gov.GetNumChains(position.Round)))
blockGasLimit := new(big.Int).SetUint64(d.gov.DexconConfiguration(position.Round).BlockGasLimit)
blockGasUsed := new(big.Int)
- allTxs := make([]*types.Transaction, 0, 3000)
+ allTxs := make([]*types.Transaction, 0, 10000)
addressMap:
for address, txs := range txsMap {
@@ -225,21 +190,17 @@ addressMap:
break addressMap
default:
}
- // TX hash need to be slot to the given chain in order to be included in the block.
- if !d.addrBelongsToChain(address, chainNums, chainID) {
- continue
- }
- balance := currentState.GetBalance(address)
- cost, exist := d.blockchain.GetCostInConfirmedBlocks(uint32(0), address)
+ balance := state.GetBalance(address)
+ cost, exist := d.addressCost[address]
if exist {
balance = new(big.Int).Sub(balance, cost)
}
var expectNonce uint64
- lastConfirmedNonce, exist := d.blockchain.GetLastNonceInConfirmedBlocks(uint32(0), address)
+ lastConfirmedNonce, exist := d.addressNonce[address]
if !exist {
- expectNonce = currentState.GetNonce(address)
+ expectNonce = state.GetNonce(address)
} else {
expectNonce = lastConfirmedNonce + 1
}
@@ -337,37 +298,25 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) coreTypes.BlockVerifySta
return coreTypes.VerifyInvalidBlock
}
- d.chainRLock(uint32(0))
- defer d.chainRUnlock(uint32(0))
+ d.appMu.RLock()
+ defer d.appMu.RUnlock()
- if block.Position.Height != 0 {
- // Check if target block is the next height to be verified, we can only
- // verify the next block in a given chain.
- chainLastHeight, ok := d.blockchain.GetChainLastConfirmedHeight(uint32(0))
- if !ok || chainLastHeight != block.Position.Height-1 {
- log.Debug("Previous confirmed block not exists", "current pos", block.Position.String(),
- "prev height", chainLastHeight, "ok", ok)
- return coreTypes.VerifyRetryLater
- }
+ // deliver height = position height + 1
+ if d.deliveredHeight+d.undeliveredNum != block.Position.Height {
+ return coreTypes.VerifyRetryLater
}
- // Get latest state with current chain.
- root, exist := d.chainRoot.Load(uint32(0))
- if !exist {
- return coreTypes.VerifyRetryLater
+ var transactions types.Transactions
+ if len(block.Payload) == 0 {
+ return coreTypes.VerifyOK
}
- currentState, err := d.blockchain.StateAt(*root.(*common.Hash))
- log.Debug("Verify block", "chain", uint32(0), "height", block.Position.Height)
+ deliveredBlock := d.blockchain.GetBlockByNumber(d.deliveredHeight)
+ state, err := d.blockchain.StateAt(deliveredBlock.Root())
if err != nil {
- log.Debug("Invalid state root", "root", *root.(*common.Hash), "err", err)
return coreTypes.VerifyInvalidBlock
}
- var transactions types.Transactions
- if len(block.Payload) == 0 {
- return coreTypes.VerifyOK
- }
err = rlp.DecodeBytes(block.Payload, &transactions)
if err != nil {
log.Error("Payload rlp decode", "error", err)
@@ -390,22 +339,13 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) coreTypes.BlockVerifySta
return coreTypes.VerifyInvalidBlock
}
- // Check if nonce is strictly increasing for every address.
- chainID := big.NewInt(int64(uint32(0)))
- chainNums := big.NewInt(int64(d.gov.GetNumChains(block.Position.Round)))
-
for address, firstNonce := range addressNonce {
- if !d.addrBelongsToChain(address, chainNums, chainID) {
- log.Error("Address does not belong to given chain ID", "address", address, "chainD", chainID)
- return coreTypes.VerifyInvalidBlock
- }
-
var expectNonce uint64
- lastConfirmedNonce, exist := d.blockchain.GetLastNonceInConfirmedBlocks(uint32(0), address)
+ nonce, exist := d.addressNonce[address]
if exist {
- expectNonce = lastConfirmedNonce + 1
+ expectNonce = nonce + 1
} else {
- expectNonce = currentState.GetNonce(address)
+ expectNonce = state.GetNonce(address)
}
if expectNonce != firstNonce {
@@ -417,11 +357,11 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) coreTypes.BlockVerifySta
// Calculate balance in last state (including pending state).
addressesBalance := map[common.Address]*big.Int{}
for address := range addressNonce {
- cost, exist := d.blockchain.GetCostInConfirmedBlocks(uint32(0), address)
+ cost, exist := d.addressCost[address]
if exist {
- addressesBalance[address] = new(big.Int).Sub(currentState.GetBalance(address), cost)
+ addressesBalance[address] = new(big.Int).Sub(state.GetBalance(address), cost)
} else {
- addressesBalance[address] = currentState.GetBalance(address)
+ addressesBalance[address] = state.GetBalance(address)
}
}
@@ -472,11 +412,10 @@ func (d *DexconApp) BlockDelivered(
log.Debug("DexconApp block deliver", "height", result.Height, "hash", blockHash, "position", blockPosition.String())
defer log.Debug("DexconApp block delivered", "height", result.Height, "hash", blockHash, "position", blockPosition.String())
- chainID := uint32(0)
- d.chainLock(chainID)
- defer d.chainUnlock(chainID)
+ d.appMu.Lock()
+ defer d.appMu.Unlock()
- block, txs := d.blockchain.GetConfirmedBlockByHash(chainID, blockHash)
+ block, txs := d.getConfirmedBlockByHash(blockHash)
if block == nil {
panic("Can not get confirmed block")
}
@@ -508,46 +447,123 @@ func (d *DexconApp) BlockDelivered(
Randomness: result.Randomness,
}, txs, nil, nil)
- h := d.blockchain.CurrentBlock().NumberU64() + 1
- var root *common.Hash
if block.IsEmpty() {
- root, err = d.blockchain.ProcessEmptyBlock(newBlock)
+ _, err = d.blockchain.ProcessEmptyBlock(newBlock)
if err != nil {
log.Error("Failed to process empty block", "error", err)
panic(err)
}
} else {
- root, err = d.blockchain.ProcessBlock(newBlock, &block.Witness)
+ _, err = d.blockchain.ProcessBlock(newBlock, &block.Witness)
if err != nil {
log.Error("Failed to process pending block", "error", err)
panic(err)
}
}
- d.chainRoot.Store(chainID, root)
- d.blockchain.RemoveConfirmedBlock(chainID, blockHash)
+ d.removeConfirmedBlock(blockHash)
+ d.deliveredHeight = result.Height
// New blocks are finalized, notify other components.
- newHeight := d.blockchain.CurrentBlock().NumberU64()
- for h <= newHeight {
- b := d.blockchain.GetBlockByNumber(h)
- go d.finalizedBlockFeed.Send(core.NewFinalizedBlockEvent{b})
- log.Debug("Send new finalized block event", "number", h)
- h++
- }
+ go d.finalizedBlockFeed.Send(core.NewFinalizedBlockEvent{Block: d.blockchain.CurrentBlock()})
}
// BlockConfirmed is called when a block is confirmed and added to lattice.
func (d *DexconApp) BlockConfirmed(block coreTypes.Block) {
- d.chainLock(uint32(0))
- defer d.chainUnlock(uint32(0))
+ d.appMu.Lock()
+ defer d.appMu.Unlock()
log.Debug("DexconApp block confirmed", "block", block.String())
- if err := d.blockchain.AddConfirmedBlock(&block); err != nil {
+ if err := d.addConfirmedBlock(&block); err != nil {
panic(err)
}
}
+type addressInfo struct {
+ cost *big.Int
+}
+
+type blockInfo struct {
+ addresses map[common.Address]*addressInfo
+ block *coreTypes.Block
+ txs types.Transactions
+}
+
+func (d *DexconApp) addConfirmedBlock(block *coreTypes.Block) error {
+ var transactions types.Transactions
+ if len(block.Payload) != 0 {
+ err := rlp.Decode(bytes.NewReader(block.Payload), &transactions)
+ if err != nil {
+ return err
+ }
+ _, err = types.GlobalSigCache.Add(types.NewEIP155Signer(d.blockchain.Config().ChainID), transactions)
+ if err != nil {
+ return err
+ }
+ }
+
+ addressMap := map[common.Address]*addressInfo{}
+ for _, tx := range transactions {
+ msg, err := tx.AsMessage(types.MakeSigner(d.blockchain.Config(), new(big.Int)))
+ if err != nil {
+ return err
+ }
+
+ if addrInfo, exist := addressMap[msg.From()]; !exist {
+ addressMap[msg.From()] = &addressInfo{cost: tx.Cost()}
+ } else {
+ addrInfo.cost = new(big.Int).Add(addrInfo.cost, tx.Cost())
+ }
+
+ // get latest nonce in block
+ d.addressNonce[msg.From()] = msg.Nonce()
+
+ // calculate max cost in confirmed blocks
+ if d.addressCost[msg.From()] == nil {
+ d.addressCost[msg.From()] = big.NewInt(0)
+ }
+ d.addressCost[msg.From()] = new(big.Int).Add(d.addressCost[msg.From()], tx.Cost())
+ }
+
+ for addr := range addressMap {
+ d.addressCounter[addr]++
+ }
+
+ d.confirmedBlocks[block.Hash] = &blockInfo{
+ addresses: addressMap,
+ block: block,
+ txs: transactions,
+ }
+
+ d.undeliveredNum++
+ return nil
+}
+
+func (d *DexconApp) removeConfirmedBlock(hash coreCommon.Hash) {
+ blockInfo := d.confirmedBlocks[hash]
+ for addr, info := range blockInfo.addresses {
+ d.addressCounter[addr]--
+ d.addressCost[addr] = new(big.Int).Sub(d.addressCost[addr], info.cost)
+ if d.addressCounter[addr] == 0 {
+ delete(d.addressCounter, addr)
+ delete(d.addressCost, addr)
+ delete(d.addressNonce, addr)
+ }
+ }
+
+ delete(d.confirmedBlocks, hash)
+ d.undeliveredNum--
+}
+
+func (d *DexconApp) getConfirmedBlockByHash(hash coreCommon.Hash) (*coreTypes.Block, types.Transactions) {
+ info, exist := d.confirmedBlocks[hash]
+ if !exist {
+ return nil, nil
+ }
+
+ return info.block, info.txs
+}
+
func (d *DexconApp) SubscribeNewFinalizedBlockEvent(
ch chan<- core.NewFinalizedBlockEvent) event.Subscription {
return d.scope.Track(d.finalizedBlockFeed.Subscribe(ch))