Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions core/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,86 @@ func (t *TransactionsByPriceAndNonce) Pop() {
heap.Pop(&t.heads)
}

type TxByHash Transactions

func (s TxByHash) Len() int { return len(s) }
func (s TxByHash) Less(i, j int) bool {
return s[i].Hash().Big().Cmp(s[j].Hash().Big()) > 0
}
func (s TxByHash) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

func (s *TxByHash) Push(x interface{}) {
*s = append(*s, x.(*Transaction))
}

func (s *TxByHash) Pop() interface{} {
old := *s
n := len(old)
x := old[n-1]
*s = old[0 : n-1]
return x
}

type TxHeap interface {
Peek() *Transaction
Shift()
Pop()
}

type TransactionsByHashAndNonce struct {
txs map[common.Address]Transactions // Per account nonce-sorted list of transactions
heads TxByHash // Next transaction for each unique account (price heap)
signer Signer // Signer for the set of transactions
}

func NewTransactionsByHashAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByHashAndNonce {
// Initialize a price and received time based heap with the head transactions
heads := make(TxByHash, 0, len(txs))
for from, accTxs := range txs {
// Ensure the sender address is from the signer
if acc, _ := Sender(signer, accTxs[0]); acc != from {
delete(txs, from)
continue
}
heads = append(heads, accTxs[0])
txs[from] = accTxs[1:]
}
heap.Init(&heads)

// Assemble and return the transaction set
return &TransactionsByHashAndNonce{
txs: txs,
heads: heads,
signer: signer,
}
}

// Peek returns the next transaction by price.
func (t *TransactionsByHashAndNonce) Peek() *Transaction {
if len(t.heads) == 0 {
return nil
}
return t.heads[0]
}

// Shift replaces the current best head with the next one from the same account.
func (t *TransactionsByHashAndNonce) Shift() {
acc, _ := Sender(t.signer, t.heads[0])
if txs, ok := t.txs[acc]; ok && len(txs) > 0 {
t.heads[0], t.txs[acc] = txs[0], txs[1:]
heap.Fix(&t.heads, 0)
} else {
heap.Pop(&t.heads)
}
}

// Pop removes the best transaction, *not* replacing it with the next one from
// the same account. This should be used when a transaction cannot be executed
// and hence all subsequent ones should be discarded from the same account.
func (t *TransactionsByHashAndNonce) Pop() {
heap.Pop(&t.heads)
}

// Message is a fully derived transaction and implements core.Message
//
// NOTE: In a future PR this will be removed.
Expand Down
48 changes: 48 additions & 0 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"bytes"
"errors"
"math/big"
"math/rand"
"sync"
"sync/atomic"
"time"
Expand Down Expand Up @@ -747,6 +748,50 @@ func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Addres
return receipt.Logs, nil
}

func reorderTransactions(w *worker, statedb *state.StateDB, header *types.Header, coinbase common.Address, uncles []*types.Header) {
txs := w.current.txs
for i := 0; i < 3; i++ {
var (
perm = rand.Perm(len(txs))
currentState = statedb.Copy()
gasPool = new(core.GasPool).AddGas(header.GasLimit)
currentTxs []*types.Transaction
currentReceipts []*types.Receipt
currentHeader = *header
start = time.Now()
)

// Add permutated transactions
for _, idx := range perm {
if gasPool.Gas() < params.TxGas {
log.Trace("Not enough gas for further transactions in reorder", "have", gasPool, "want", params.TxGas)
break
}

snap := currentState.Snapshot()
tx := txs[idx]
receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, gasPool, currentState, &currentHeader, tx, &currentHeader.GasUsed, *w.chain.GetVMConfig())
if err != nil {
currentState.RevertToSnapshot(snap)
log.Warn("Reordered transaction reverted", "error", err)
continue
}
currentTxs = append(currentTxs, tx)
currentReceipts = append(currentReceipts, receipt)
}
// finalize the block
block, err := w.engine.FinalizeAndAssemble(w.chain, &currentHeader, currentState, currentTxs, uncles, currentReceipts)
if err != nil {
log.Warn("Reordered block assembly reverted", "error", err)
return
}
log.Info("Reordering would commit new mining work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()),
"uncles", len(uncles), "txs", len(currentTxs),
"gas", block.GasUsed(), "fees", totalFees(block, currentReceipts),
"elapsed", common.PrettyDuration(time.Since(start)))
}
}

func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt *int32) bool {
// Short circuit if current is nil
if w.current == nil {
Expand Down Expand Up @@ -972,6 +1017,7 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64)
localTxs[account] = txs
}
}
oldState := w.current.state.Copy()
if len(localTxs) > 0 {
txs := types.NewTransactionsByPriceAndNonce(w.current.signer, localTxs)
if w.commitTransactions(txs, w.coinbase, interrupt) {
Expand All @@ -984,6 +1030,8 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64)
return
}
}

go reorderTransactions(w, oldState, header, w.coinbase, uncles)
w.commit(uncles, w.fullTaskHook, true, tstart)
}

Expand Down