Skip to content

Commit

Permalink
Merge pull request ethereum#601 from thanhnguyennguyen/batch-multiple…
Browse files Browse the repository at this point in the history
…-TxDataMatches

Batch multiple TxDataMatches into one tx
  • Loading branch information
ngtuna authored Aug 5, 2019
2 parents 2ed3184 + a89b14a commit 4834cac
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 140 deletions.
129 changes: 76 additions & 53 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package core

import (
"encoding/json"
"encoding/hex"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
Expand All @@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/tomox"
"sort"
)

// BlockValidator is responsible for validating block headers, uncles and
Expand All @@ -52,58 +53,74 @@ func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engin
// ValidateBody validates the given block's uncles and verifies the the block
// header's transaction and uncle roots. The headers are assumed to be already
// validated at this point.
func (v *BlockValidator) ValidateBody(block *types.Block) (error, []common.Hash) {
func (v *BlockValidator) ValidateBody(block *types.Block) error {
// Check whether the block's known, and if not, that it's linkable
if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) {
return ErrKnownBlock, []common.Hash{}
return ErrKnownBlock
}
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
return consensus.ErrUnknownAncestor, []common.Hash{}
return consensus.ErrUnknownAncestor
}
return consensus.ErrPrunedAncestor, []common.Hash{}
return consensus.ErrPrunedAncestor
}
// Header validity is known at this point, check the uncles and transactions
header := block.Header()
if err := v.engine.VerifyUncles(v.bc, block); err != nil {
return err, []common.Hash{}
return err
}
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash), []common.Hash{}
return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash)
}
if hash := types.DeriveSha(block.Transactions()); hash != header.TxHash {
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash), []common.Hash{}
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
}

engine, _ := v.engine.(*posv.Posv)
tomoXService := engine.GetTomoXService()

currentState, err := v.bc.State()
if err != nil {
return err, []common.Hash{}
return err
}

// validate matchedOrder txs
processedHashes := []common.Hash{}
processedHashes := []common.Hash{}
// clear the previous dry-run cache
if tomoXService != nil {
tomoXService.GetDB().InitDryRunMode()
}
txs := []*types.Transaction{}

for _, tx := range block.Transactions() {
if tx.IsMatchingTransaction() {
txs = append(txs, tx)
}
}

sort.Slice(txs, func(i, j int) bool {
return txs[i].Timestamp() <= txs[j].Timestamp()
})

for _, tx := range txs {
if tx.IsMatchingTransaction() {
if tomoXService == nil {
log.Error("tomox not found")
return tomox.ErrTomoXServiceNotFound, []common.Hash{}
return tomox.ErrTomoXServiceNotFound
}
log.Debug("process tx match")
hash, err := v.validateMatchedOrder(tomoXService, currentState, tx)
log.Debug("verify matching transaction")
hashes, err := v.validateMatchingOrder(tomoXService, currentState, tx)
if err != nil {
return err, []common.Hash{}
return err
}
processedHashes = append(processedHashes, hash)
processedHashes = append(processedHashes, hashes...)
}
}
return nil, processedHashes
hashNoValidator := block.HashNoValidator()
if _, ok := v.bc.processedOrderHashes.Get(hashNoValidator); !ok && len(processedHashes) > 0 {
v.bc.processedOrderHashes.Add(block.HashNoValidator(), processedHashes)
}
return nil
}

// ValidateState validates the various changes that happen after a state
Expand Down Expand Up @@ -134,54 +151,60 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
return nil
}

// return values
// hash: for removing from pending list and add to processed list
// orderbook according to the order
func (v *BlockValidator) validateMatchedOrder(tomoXService *tomox.TomoX, currentState *state.StateDB, tx *types.Transaction) (common.Hash, error) {
txMatch := &tomox.TxDataMatch{}
if err := json.Unmarshal(tx.Data(), txMatch); err != nil {
return common.Hash{}, fmt.Errorf("transaction match is corrupted. Failed unmarshal. Error: %s", err.Error())
}

// verify orderItem
order, err := txMatch.DecodeOrder()
func (v *BlockValidator) validateMatchingOrder(tomoXService *tomox.TomoX, currentState *state.StateDB, tx *types.Transaction) ([]common.Hash, error) {
txMatches, err := tomox.DecodeTxMatchesBatch(tx.Data())
if err != nil {
return common.Hash{},fmt.Errorf("transaction match is corrupted. Failed decode order. Error: %s ", err.Error())
return []common.Hash{}, fmt.Errorf("transaction match is corrupted. Failed to decode txMatchesBatch. Error: %s", err.Error())
}
processedHashes := []common.Hash{}
log.Debug("verify matching transaction found a TxMatches Batch", "numTxMatches", len(txMatches))

// SDK node doesn't need to run ME
if tomoXService.IsSDKNode() {
log.Debug("SDK node ignore running matching engine")
return order.Hash, nil
}
for _, txMatch := range txMatches {
// verify orderItem
order, err := txMatch.DecodeOrder()
if err != nil {
return []common.Hash{}, fmt.Errorf("transaction match is corrupted. Failed decode order. Error: %s ", err.Error())
}
if !tomoXService.ExistProcessedOrderHash(order.Hash) {
return []common.Hash{}, fmt.Errorf("This order has been processed: Hash: %s ", hex.EncodeToString(order.Hash.Bytes()))
}
log.Debug("process tx match", "order", order)

if err := order.VerifyMatchedOrder(currentState); err != nil {
return common.Hash{},err
}
processedHashes = append(processedHashes, order.Hash)

ob, err := tomoXService.GetOrderBook(order.PairName, true)
// if orderbook of this pairName has been updated by previous tx in this block, use it
// SDK node doesn't need to run ME
if tomoXService.IsSDKNode() {
log.Debug("SDK node ignore running matching engine")
continue
}
if err := order.VerifyMatchedOrder(currentState); err != nil {
return []common.Hash{}, err
}

if err != nil {
return common.Hash{}, err
}
ob, err := tomoXService.GetOrderBook(order.PairName, true)
// if orderbook of this pairName has been updated by previous tx in this block, use it

// verify old state: orderbook hash, bidTree hash, askTree hash
if err := txMatch.VerifyOldTomoXState(ob); err != nil {
return common.Hash{}, err
}
if err != nil {
return []common.Hash{}, err
}

// process Matching Engine
if _, _, err := ob.ProcessOrder(order, true, true); err != nil {
return common.Hash{}, err
}
// verify old state: orderbook hash, bidTree hash, askTree hash
if err := txMatch.VerifyOldTomoXState(ob); err != nil {
return []common.Hash{}, err
}

// verify new state
if err := txMatch.VerifyNewTomoXState(ob); err != nil {
return common.Hash{}, err
// process Matching Engine
if _, _, err := ob.ProcessOrder(order, true, true); err != nil {
return []common.Hash{}, err
}

// verify new state
if err := txMatch.VerifyNewTomoXState(ob); err != nil {
return []common.Hash{}, err
}
}

return order.Hash, nil
return processedHashes, nil
}

// CalcGasLimit computes the gas limit of the next block after parent.
Expand Down
Loading

0 comments on commit 4834cac

Please sign in to comment.