mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 02:12:23 +00:00
Merge 76d6c27515 into 998f6564b2
This commit is contained in:
commit
401569932a
8 changed files with 259 additions and 40 deletions
|
|
@ -37,6 +37,7 @@ const (
|
||||||
var (
|
var (
|
||||||
hashT = reflect.TypeOf(Hash{})
|
hashT = reflect.TypeOf(Hash{})
|
||||||
addressT = reflect.TypeOf(Address{})
|
addressT = reflect.TypeOf(Address{})
|
||||||
|
NullSenderAddr = HexToAddress("0xffffffffffffffffffffffffffffffffffffffff")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
||||||
|
|
@ -195,6 +196,11 @@ func (a Address) Format(s fmt.State, c rune) {
|
||||||
fmt.Fprintf(s, "%"+string(c), a[:])
|
fmt.Fprintf(s, "%"+string(c), a[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsNullSender returns true if this address is NullSenderAddr
|
||||||
|
func (a Address) IsNullSender() bool {
|
||||||
|
return a == NullSenderAddr
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the address to the value of b. If b is larger than len(a) it will panic
|
// Sets the address to the value of b. If b is larger than len(a) it will panic
|
||||||
func (a *Address) SetBytes(b []byte) {
|
func (a *Address) SetBytes(b []byte) {
|
||||||
if len(b) > len(a) {
|
if len(b) > len(a) {
|
||||||
|
|
|
||||||
|
|
@ -32,4 +32,7 @@ var (
|
||||||
// ErrNonceTooHigh is returned if the nonce of a transaction is higher than the
|
// ErrNonceTooHigh is returned if the nonce of a transaction is higher than the
|
||||||
// next one expected based on the local chain.
|
// next one expected based on the local chain.
|
||||||
ErrNonceTooHigh = errors.New("nonce too high")
|
ErrNonceTooHigh = errors.New("nonce too high")
|
||||||
|
|
||||||
|
// ErrFailedVote is returned if a vote failed in the Casper contract.
|
||||||
|
ErrFailedVote = errors.New("casper vote failed")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,14 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
isVoteTx := msg.From().IsNullSender()
|
||||||
|
if !isVoteTx {
|
||||||
|
// Gas used for votes does not count towards accumulated gas used
|
||||||
|
*usedGas += gas
|
||||||
|
} else if failed {
|
||||||
|
return nil, 0, ErrFailedVote
|
||||||
|
}
|
||||||
|
|
||||||
// Update the state with pending changes
|
// Update the state with pending changes
|
||||||
var root []byte
|
var root []byte
|
||||||
if config.IsByzantium(header.Number) {
|
if config.IsByzantium(header.Number) {
|
||||||
|
|
@ -107,7 +115,6 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
|
||||||
} else {
|
} else {
|
||||||
root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
|
root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
|
||||||
}
|
}
|
||||||
*usedGas += gas
|
|
||||||
|
|
||||||
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
||||||
// based on the eip phase, we're passing wether the root touch-delete accounts.
|
// based on the eip phase, we're passing wether the root touch-delete accounts.
|
||||||
|
|
|
||||||
100
core/tx_pool.go
100
core/tx_pool.go
|
|
@ -76,6 +76,12 @@ var (
|
||||||
// than some meaningful limit a user might use. This is not a consensus error
|
// than some meaningful limit a user might use. This is not a consensus error
|
||||||
// making the transaction invalid, rather a DOS protection.
|
// making the transaction invalid, rather a DOS protection.
|
||||||
ErrOversizedData = errors.New("oversized data")
|
ErrOversizedData = errors.New("oversized data")
|
||||||
|
|
||||||
|
// ErrInvalidVote is returned if the vote is invalid per Casper contract.
|
||||||
|
ErrInvalidVote = errors.New("invalid vote")
|
||||||
|
|
||||||
|
// ErrTooManyVote is returned if the vote is invalid per Casper contract.
|
||||||
|
ErrTooManyVote = errors.New("too many vote")
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -99,6 +105,8 @@ var (
|
||||||
// General tx metrics
|
// General tx metrics
|
||||||
invalidTxCounter = metrics.NewRegisteredCounter("txpool/invalid", nil)
|
invalidTxCounter = metrics.NewRegisteredCounter("txpool/invalid", nil)
|
||||||
underpricedTxCounter = metrics.NewRegisteredCounter("txpool/underpriced", nil)
|
underpricedTxCounter = metrics.NewRegisteredCounter("txpool/underpriced", nil)
|
||||||
|
invalidVoteCounter = metrics.NewRegisteredCounter("txpool/invalidvote", nil)
|
||||||
|
overflowVoteCounter = metrics.NewRegisteredCounter("txpool/overflowvote", nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
// TxStatus is the current status of a transaction as seen by the pool.
|
// TxStatus is the current status of a transaction as seen by the pool.
|
||||||
|
|
@ -205,6 +213,7 @@ type TxPool struct {
|
||||||
beats map[common.Address]time.Time // Last heartbeat from each known account
|
beats map[common.Address]time.Time // Last heartbeat from each known account
|
||||||
all *txLookup // All transactions to allow lookups
|
all *txLookup // All transactions to allow lookups
|
||||||
priced *txPricedList // All transactions sorted by price
|
priced *txPricedList // All transactions sorted by price
|
||||||
|
votes *txLookup // Votes transactions to allow lookups
|
||||||
|
|
||||||
wg sync.WaitGroup // for shutdown sync
|
wg sync.WaitGroup // for shutdown sync
|
||||||
|
|
||||||
|
|
@ -222,17 +231,18 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
|
||||||
config: config,
|
config: config,
|
||||||
chainconfig: chainconfig,
|
chainconfig: chainconfig,
|
||||||
chain: chain,
|
chain: chain,
|
||||||
signer: types.NewEIP155Signer(chainconfig.ChainId),
|
signer: types.NewEIP1011Signer(chainconfig),
|
||||||
pending: make(map[common.Address]*txList),
|
pending: make(map[common.Address]*txList),
|
||||||
queue: make(map[common.Address]*txList),
|
queue: make(map[common.Address]*txList),
|
||||||
beats: make(map[common.Address]time.Time),
|
beats: make(map[common.Address]time.Time),
|
||||||
all: newTxLookup(),
|
all: newTxLookup(),
|
||||||
|
votes: newTxLookup(),
|
||||||
chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
|
chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
|
||||||
gasPrice: new(big.Int).SetUint64(config.PriceLimit),
|
gasPrice: new(big.Int).SetUint64(config.PriceLimit),
|
||||||
}
|
}
|
||||||
pool.locals = newAccountSet(pool.signer)
|
pool.locals = newAccountSet(pool.signer)
|
||||||
pool.priced = newTxPricedList(pool.all)
|
pool.priced = newTxPricedList(pool.all)
|
||||||
pool.reset(nil, chain.CurrentBlock().Header())
|
pool.reset(nil, chain.CurrentBlock().Header(), nil)
|
||||||
|
|
||||||
// If local transactions and journaling is enabled, load from disk
|
// If local transactions and journaling is enabled, load from disk
|
||||||
if !config.NoLocals && config.Journal != "" {
|
if !config.NoLocals && config.Journal != "" {
|
||||||
|
|
@ -286,7 +296,7 @@ func (pool *TxPool) loop() {
|
||||||
if pool.chainconfig.IsHomestead(ev.Block.Number()) {
|
if pool.chainconfig.IsHomestead(ev.Block.Number()) {
|
||||||
pool.homestead = true
|
pool.homestead = true
|
||||||
}
|
}
|
||||||
pool.reset(head.Header(), ev.Block.Header())
|
pool.reset(head.Header(), ev.Block.Header(), ev.Block.Transactions())
|
||||||
head = ev.Block
|
head = ev.Block
|
||||||
|
|
||||||
pool.mu.Unlock()
|
pool.mu.Unlock()
|
||||||
|
|
@ -343,14 +353,14 @@ func (pool *TxPool) lockedReset(oldHead, newHead *types.Header) {
|
||||||
pool.mu.Lock()
|
pool.mu.Lock()
|
||||||
defer pool.mu.Unlock()
|
defer pool.mu.Unlock()
|
||||||
|
|
||||||
pool.reset(oldHead, newHead)
|
pool.reset(oldHead, newHead, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset retrieves the current state of the blockchain and ensures the content
|
// reset retrieves the current state of the blockchain and ensures the content
|
||||||
// of the transaction pool is valid with regard to the chain state.
|
// of the transaction pool is valid with regard to the chain state.
|
||||||
func (pool *TxPool) reset(oldHead, newHead *types.Header) {
|
func (pool *TxPool) reset(oldHead, newHead *types.Header, txs types.Transactions) {
|
||||||
// If we're reorging an old state, reinject all dropped transactions
|
// If we're reorging an old state, reinject all dropped transactions
|
||||||
var reinject types.Transactions
|
var reinject, included types.Transactions
|
||||||
|
|
||||||
if oldHead != nil && oldHead.Hash() != newHead.ParentHash {
|
if oldHead != nil && oldHead.Hash() != newHead.ParentHash {
|
||||||
// If the reorg is too deep, avoid doing it (will happen during fast sync)
|
// If the reorg is too deep, avoid doing it (will happen during fast sync)
|
||||||
|
|
@ -361,7 +371,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
|
||||||
log.Debug("Skipping deep transaction reorg", "depth", depth)
|
log.Debug("Skipping deep transaction reorg", "depth", depth)
|
||||||
} else {
|
} else {
|
||||||
// Reorg seems shallow enough to pull in all transactions into memory
|
// Reorg seems shallow enough to pull in all transactions into memory
|
||||||
var discarded, included types.Transactions
|
var discarded types.Transactions
|
||||||
|
|
||||||
var (
|
var (
|
||||||
rem = pool.chain.GetBlock(oldHead.Hash(), oldHead.Number.Uint64())
|
rem = pool.chain.GetBlock(oldHead.Hash(), oldHead.Number.Uint64())
|
||||||
|
|
@ -427,6 +437,13 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
|
||||||
// Check the queue and move transactions over to the pending if possible
|
// Check the queue and move transactions over to the pending if possible
|
||||||
// or remove those that have become invalid
|
// or remove those that have become invalid
|
||||||
pool.promoteExecutables(nil)
|
pool.promoteExecutables(nil)
|
||||||
|
|
||||||
|
// De-dup vote transactions
|
||||||
|
if included != nil {
|
||||||
|
pool.dedupCasperVotes(included)
|
||||||
|
} else {
|
||||||
|
pool.dedupCasperVotes(txs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop terminates the transaction pool.
|
// Stop terminates the transaction pool.
|
||||||
|
|
@ -533,6 +550,12 @@ func (pool *TxPool) Pending() (map[common.Address]types.Transactions, error) {
|
||||||
return pending, nil
|
return pending, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Votes returns all currently pooled vote transactions.
|
||||||
|
// The returned votes is a copy and can be freely modified by calling code.
|
||||||
|
func (pool *TxPool) Votes() types.Transactions {
|
||||||
|
return pool.votes.Flatten()
|
||||||
|
}
|
||||||
|
|
||||||
// local retrieves all currently known local transactions, groupped by origin
|
// local retrieves all currently known local transactions, groupped by origin
|
||||||
// account and sorted by nonce. The returned transaction set is a copy and can be
|
// account and sorted by nonce. The returned transaction set is a copy and can be
|
||||||
// freely modified by calling code.
|
// freely modified by calling code.
|
||||||
|
|
@ -570,6 +593,14 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrInvalidSender
|
return ErrInvalidSender
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip checking for gas price, nonce, and acct balance for null senders (Casper votes)
|
||||||
|
if from.IsNullSender() {
|
||||||
|
if err := pool.validateVote(tx); err != nil {
|
||||||
|
invalidVoteCounter.Inc(1)
|
||||||
|
return ErrInvalidVote
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// Drop non-local transactions under our own minimal accepted gas price
|
// Drop non-local transactions under our own minimal accepted gas price
|
||||||
local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network
|
local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network
|
||||||
if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 {
|
if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 {
|
||||||
|
|
@ -584,6 +615,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||||
if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
|
if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
|
||||||
return ErrInsufficientFunds
|
return ErrInsufficientFunds
|
||||||
}
|
}
|
||||||
|
}
|
||||||
intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)
|
intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -594,6 +626,13 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validateVote checks whether a vote transaction is valid according the
|
||||||
|
// Casper contract.
|
||||||
|
func (pool *TxPool) validateVote(tx *types.Transaction) error {
|
||||||
|
// Call Casper Contract validate_vote_signature and votable
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// add validates a transaction and inserts it into the non-executable queue for
|
// add validates a transaction and inserts it into the non-executable queue for
|
||||||
// later pending promotion and execution. If the transaction is a replacement for
|
// later pending promotion and execution. If the transaction is a replacement for
|
||||||
// an already pending or queued one, it overwrites the previous and returns this
|
// an already pending or queued one, it overwrites the previous and returns this
|
||||||
|
|
@ -615,8 +654,20 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
|
||||||
invalidTxCounter.Inc(1)
|
invalidTxCounter.Inc(1)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
from, _ := types.Sender(pool.signer, tx) // already validated
|
||||||
|
txCap := pool.config.GlobalSlots + pool.config.GlobalQueue
|
||||||
|
if from.IsNullSender() {
|
||||||
|
if uint64(pool.votes.Count()) >= txCap {
|
||||||
|
overflowVoteCounter.Inc(1)
|
||||||
|
return false, ErrTooManyVote
|
||||||
|
}
|
||||||
|
pool.enqueueCasperVote(hash, tx, local)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// If the transaction pool is full, discard underpriced transactions
|
// If the transaction pool is full, discard underpriced transactions
|
||||||
if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue {
|
if uint64(pool.all.Count()) >= txCap {
|
||||||
// If the new transaction is underpriced, don't accept it
|
// If the new transaction is underpriced, don't accept it
|
||||||
if !local && pool.priced.Underpriced(tx, pool.locals) {
|
if !local && pool.priced.Underpriced(tx, pool.locals) {
|
||||||
log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice())
|
log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice())
|
||||||
|
|
@ -632,7 +683,6 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the transaction is replacing an already pending one, do directly
|
// If the transaction is replacing an already pending one, do directly
|
||||||
from, _ := types.Sender(pool.signer, tx) // already validated
|
|
||||||
if list := pool.pending[from]; list != nil && list.Overlaps(tx) {
|
if list := pool.pending[from]; list != nil && list.Overlaps(tx) {
|
||||||
// Nonce already pending, check if required price bump is met
|
// Nonce already pending, check if required price bump is met
|
||||||
inserted, old := list.Add(tx, pool.config.PriceBump)
|
inserted, old := list.Add(tx, pool.config.PriceBump)
|
||||||
|
|
@ -672,6 +722,26 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
|
||||||
return replace, nil
|
return replace, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dedupCasperVotes removes txs from the vote collection.
|
||||||
|
func (pool *TxPool) dedupCasperVotes(txs types.Transactions) {
|
||||||
|
for _, tx := range txs {
|
||||||
|
pool.votes.Remove(tx.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// enqueueCasperVote inserts a new Casper vote into the vote collection.
|
||||||
|
//
|
||||||
|
// Note, this method assumes the pool lock is held!
|
||||||
|
func (pool *TxPool) enqueueCasperVote(hash common.Hash, tx *types.Transaction, local bool) {
|
||||||
|
pool.votes.Add(tx)
|
||||||
|
// Mark local addresses and journal local transactions
|
||||||
|
if local {
|
||||||
|
pool.locals.add(common.NullSenderAddr)
|
||||||
|
}
|
||||||
|
pool.journalTx(common.NullSenderAddr, tx)
|
||||||
|
log.Trace("Pooled new casper vote", "hash", hash, "local", local)
|
||||||
|
}
|
||||||
|
|
||||||
// enqueueTx inserts a new transaction into the non-executable transaction queue.
|
// enqueueTx inserts a new transaction into the non-executable transaction queue.
|
||||||
//
|
//
|
||||||
// Note, this method assumes the pool lock is held!
|
// Note, this method assumes the pool lock is held!
|
||||||
|
|
@ -1234,3 +1304,15 @@ func (t *txLookup) Remove(hash common.Hash) {
|
||||||
|
|
||||||
delete(t.all, hash)
|
delete(t.all, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flatten returns a flattened list of all transactions in the lookup.
|
||||||
|
func (t *txLookup) Flatten() types.Transactions {
|
||||||
|
t.lock.RLock()
|
||||||
|
defer t.lock.RUnlock()
|
||||||
|
|
||||||
|
var txs types.Transactions
|
||||||
|
for _, value := range t.all {
|
||||||
|
txs = append(txs, value)
|
||||||
|
}
|
||||||
|
return txs
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"container/heap"
|
"container/heap"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -33,6 +34,7 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrInvalidSig = errors.New("invalid transaction v, r, s values")
|
ErrInvalidSig = errors.New("invalid transaction v, r, s values")
|
||||||
|
CasperVoteFunctionByte = common.Hex2Bytes("e9dc0614")
|
||||||
)
|
)
|
||||||
|
|
||||||
// deriveSigner makes a *best* guess about which signer to use.
|
// deriveSigner makes a *best* guess about which signer to use.
|
||||||
|
|
@ -237,6 +239,22 @@ func (tx *Transaction) AsMessage(s Signer) (Message, error) {
|
||||||
return msg, err
|
return msg, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsCasperVote returns true if all the following conditions are true:
|
||||||
|
// - it's sending to Casper's contract address
|
||||||
|
// - it's calling contract method "vote"
|
||||||
|
// - its signature has R == S == 0, V = ChainId
|
||||||
|
// - value == nonce == gasprice == 0
|
||||||
|
func (tx *Transaction) IsCasperVote(chainId *big.Int, casperAddr common.Address) bool {
|
||||||
|
return *tx.To() == casperAddr &&
|
||||||
|
bytes.Equal(tx.Data()[0:4], CasperVoteFunctionByte) &&
|
||||||
|
tx.data.R.Cmp(common.Big0) == 0 &&
|
||||||
|
tx.data.S.Cmp(common.Big0) == 0 &&
|
||||||
|
tx.data.V.Cmp(chainId) == 0 &&
|
||||||
|
tx.Nonce() == 0 &&
|
||||||
|
tx.GasPrice().Cmp(common.Big0) == 0 &&
|
||||||
|
tx.Value().Cmp(common.Big0) == 0
|
||||||
|
}
|
||||||
|
|
||||||
// WithSignature returns a new transaction with the given signature.
|
// WithSignature returns a new transaction with the given signature.
|
||||||
// This signature needs to be formatted as described in the yellow paper (v+27).
|
// This signature needs to be formatted as described in the yellow paper (v+27).
|
||||||
func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
|
func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@ type sigCache struct {
|
||||||
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
|
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
|
||||||
var signer Signer
|
var signer Signer
|
||||||
switch {
|
switch {
|
||||||
|
case config.IsCasper(blockNumber):
|
||||||
|
signer = NewEIP1011Signer(config)
|
||||||
case config.IsEIP155(blockNumber):
|
case config.IsEIP155(blockNumber):
|
||||||
signer = NewEIP155Signer(config.ChainId)
|
signer = NewEIP155Signer(config.ChainId)
|
||||||
case config.IsHomestead(blockNumber):
|
case config.IsHomestead(blockNumber):
|
||||||
|
|
@ -102,6 +104,26 @@ type Signer interface {
|
||||||
Equal(Signer) bool
|
Equal(Signer) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EIP1011Signer is a hybrid signer that understands both CasperFFG and EIP155Signer
|
||||||
|
type EIP1011Signer struct {
|
||||||
|
EIP155Signer
|
||||||
|
ChainConfig *params.ChainConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEIP1011Signer(config *params.ChainConfig) Signer {
|
||||||
|
return &EIP1011Signer{
|
||||||
|
EIP155Signer: NewEIP155Signer(config.ChainId),
|
||||||
|
ChainConfig: config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s EIP1011Signer) Sender(tx *Transaction) (common.Address, error) {
|
||||||
|
if s.ChainConfig.Casper != nil && tx.IsCasperVote(s.ChainConfig.ChainId, s.ChainConfig.Casper.ContractAddr) {
|
||||||
|
return common.NullSenderAddr, nil
|
||||||
|
}
|
||||||
|
return s.EIP155Signer.Sender(tx)
|
||||||
|
}
|
||||||
|
|
||||||
// EIP155Transaction implements Signer using the EIP155 rules.
|
// EIP155Transaction implements Signer using the EIP155 rules.
|
||||||
type EIP155Signer struct {
|
type EIP155Signer struct {
|
||||||
chainId, chainIdMul *big.Int
|
chainId, chainIdMul *big.Int
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ type Work struct {
|
||||||
uncles *set.Set // uncle set
|
uncles *set.Set // uncle set
|
||||||
tcount int // tx count in cycle
|
tcount int // tx count in cycle
|
||||||
gasPool *core.GasPool // available gas used to pack transactions
|
gasPool *core.GasPool // available gas used to pack transactions
|
||||||
|
vGasPool *core.GasPool // available gas used to pack Casper votes
|
||||||
|
|
||||||
Block *types.Block // the new block
|
Block *types.Block // the new block
|
||||||
|
|
||||||
|
|
@ -269,12 +270,17 @@ func (self *worker) update() {
|
||||||
if atomic.LoadInt32(&self.mining) == 0 {
|
if atomic.LoadInt32(&self.mining) == 0 {
|
||||||
self.currentMu.Lock()
|
self.currentMu.Lock()
|
||||||
txs := make(map[common.Address]types.Transactions)
|
txs := make(map[common.Address]types.Transactions)
|
||||||
|
var votes types.Transactions
|
||||||
for _, tx := range ev.Txs {
|
for _, tx := range ev.Txs {
|
||||||
acc, _ := types.Sender(self.current.signer, tx)
|
acc, _ := types.Sender(self.current.signer, tx)
|
||||||
|
if acc.IsNullSender() {
|
||||||
|
votes = append(votes, tx)
|
||||||
|
} else {
|
||||||
txs[acc] = append(txs[acc], tx)
|
txs[acc] = append(txs[acc], tx)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
txset := types.NewTransactionsByPriceAndNonce(self.current.signer, txs)
|
txset := types.NewTransactionsByPriceAndNonce(self.current.signer, txs)
|
||||||
self.current.commitTransactions(self.mux, txset, self.chain, self.coinbase)
|
self.current.commitTransactions(self.mux, txset, votes, self.chain, self.coinbase)
|
||||||
self.updateSnapshot()
|
self.updateSnapshot()
|
||||||
self.currentMu.Unlock()
|
self.currentMu.Unlock()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -370,7 +376,7 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error
|
||||||
}
|
}
|
||||||
work := &Work{
|
work := &Work{
|
||||||
config: self.config,
|
config: self.config,
|
||||||
signer: types.NewEIP155Signer(self.config.ChainId),
|
signer: types.NewEIP1011Signer(self.config),
|
||||||
state: state,
|
state: state,
|
||||||
ancestors: set.New(),
|
ancestors: set.New(),
|
||||||
family: set.New(),
|
family: set.New(),
|
||||||
|
|
@ -462,7 +468,8 @@ func (self *worker) commitNewWork() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
txs := types.NewTransactionsByPriceAndNonce(self.current.signer, pending)
|
txs := types.NewTransactionsByPriceAndNonce(self.current.signer, pending)
|
||||||
work.commitTransactions(self.mux, txs, self.chain, self.coinbase)
|
votes := self.eth.TxPool().Votes()
|
||||||
|
work.commitTransactions(self.mux, txs, votes, self.chain, self.coinbase)
|
||||||
|
|
||||||
// compute uncles for the new block.
|
// compute uncles for the new block.
|
||||||
var (
|
var (
|
||||||
|
|
@ -528,13 +535,11 @@ func (self *worker) updateSnapshot() {
|
||||||
self.snapshotState = self.current.state.Copy()
|
self.snapshotState = self.current.state.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, bc *core.BlockChain, coinbase common.Address) {
|
func (env *Work) commitTxs(txs *types.TransactionsByPriceAndNonce, bc *core.BlockChain, coinbase common.Address) []*types.Log {
|
||||||
if env.gasPool == nil {
|
if env.gasPool == nil {
|
||||||
env.gasPool = new(core.GasPool).AddGas(env.header.GasLimit)
|
env.gasPool = new(core.GasPool).AddGas(env.header.GasLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
var coalescedLogs []*types.Log
|
var coalescedLogs []*types.Log
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// If we don't have enough gas for any further transactions then we're done
|
// If we don't have enough gas for any further transactions then we're done
|
||||||
if env.gasPool.Gas() < params.TxGas {
|
if env.gasPool.Gas() < params.TxGas {
|
||||||
|
|
@ -592,6 +597,45 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
|
||||||
txs.Shift()
|
txs.Shift()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return coalescedLogs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (env *Work) commitVotes(votes types.Transactions, bc *core.BlockChain, coinbase common.Address) []*types.Log {
|
||||||
|
if env.vGasPool == nil {
|
||||||
|
env.vGasPool = new(core.GasPool).AddGas(env.header.GasLimit)
|
||||||
|
}
|
||||||
|
var coalescedLogs []*types.Log
|
||||||
|
for _, vote := range votes {
|
||||||
|
hash := vote.Hash()
|
||||||
|
// If we don't have enough gas for any further transactions then we're done
|
||||||
|
if env.vGasPool.Gas() < params.TxGas {
|
||||||
|
log.Trace("Not enough gas for further votes", "gp", env.vGasPool)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Start executing the transaction
|
||||||
|
env.state.Prepare(hash, common.Hash{}, env.tcount)
|
||||||
|
|
||||||
|
err, logs := env.commitTransaction(vote, bc, coinbase, env.vGasPool)
|
||||||
|
switch err {
|
||||||
|
case core.ErrGasLimitReached:
|
||||||
|
log.Trace("Vote gas limit exceeded for current block", "vote", hash)
|
||||||
|
case nil:
|
||||||
|
// Everything ok, collect the logs
|
||||||
|
coalescedLogs = append(coalescedLogs, logs...)
|
||||||
|
env.tcount++
|
||||||
|
default:
|
||||||
|
// Strange error, discard the transaction.
|
||||||
|
log.Debug("Transaction failed, vote skipped", "hash", hash, "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return coalescedLogs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, votes types.Transactions, bc *core.BlockChain, coinbase common.Address) {
|
||||||
|
coalescedLogs := env.commitTxs(txs, bc, coinbase)
|
||||||
|
if len(votes) > 0 {
|
||||||
|
coalescedLogs = append(coalescedLogs, env.commitVotes(votes, bc, coinbase)...)
|
||||||
|
}
|
||||||
|
|
||||||
if len(coalescedLogs) > 0 || env.tcount > 0 {
|
if len(coalescedLogs) > 0 || env.tcount > 0 {
|
||||||
// make a copy, the state caches the logs and these logs get "upgraded" from pending to mined
|
// make a copy, the state caches the logs and these logs get "upgraded" from pending to mined
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,19 @@ var (
|
||||||
EIP158Block: big.NewInt(2675000),
|
EIP158Block: big.NewInt(2675000),
|
||||||
ByzantiumBlock: big.NewInt(4370000),
|
ByzantiumBlock: big.NewInt(4370000),
|
||||||
ConstantinopleBlock: nil,
|
ConstantinopleBlock: nil,
|
||||||
|
Casper: &CasperConfig{
|
||||||
|
ForkBlock: big.NewInt(6666666),
|
||||||
|
ContractAddr: common.HexToAddress("0x"),
|
||||||
|
ContractCode: common.Hex2Bytes("0x"),
|
||||||
|
Balance: new(big.Int).Mul(big.NewInt(1250000), big.NewInt(Ether)),
|
||||||
|
MsgHasherAddr: common.HexToAddress("0x"),
|
||||||
|
MsgHasherCode: common.Hex2Bytes("0x"),
|
||||||
|
PurityCheckerAddr: common.HexToAddress("0x"),
|
||||||
|
PurityCheckerCode: common.Hex2Bytes("0x"),
|
||||||
|
BlockReward: new(big.Int).Mul(big.NewInt(600), big.NewInt(Finney)),
|
||||||
|
RewardStpDwnBlkCnt: big.NewInt(550000),
|
||||||
|
NonRevertMinDeposit: new(big.Int).Mul(big.NewInt(100), big.NewInt(Ether)),
|
||||||
|
},
|
||||||
Ethash: new(EthashConfig),
|
Ethash: new(EthashConfig),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,6 +69,7 @@ var (
|
||||||
EIP158Block: big.NewInt(10),
|
EIP158Block: big.NewInt(10),
|
||||||
ByzantiumBlock: big.NewInt(1700000),
|
ByzantiumBlock: big.NewInt(1700000),
|
||||||
ConstantinopleBlock: nil,
|
ConstantinopleBlock: nil,
|
||||||
|
Casper: nil,
|
||||||
Ethash: new(EthashConfig),
|
Ethash: new(EthashConfig),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,6 +85,7 @@ var (
|
||||||
EIP158Block: big.NewInt(3),
|
EIP158Block: big.NewInt(3),
|
||||||
ByzantiumBlock: big.NewInt(1035301),
|
ByzantiumBlock: big.NewInt(1035301),
|
||||||
ConstantinopleBlock: nil,
|
ConstantinopleBlock: nil,
|
||||||
|
Casper: nil,
|
||||||
Clique: &CliqueConfig{
|
Clique: &CliqueConfig{
|
||||||
Period: 15,
|
Period: 15,
|
||||||
Epoch: 30000,
|
Epoch: 30000,
|
||||||
|
|
@ -82,16 +97,16 @@ var (
|
||||||
//
|
//
|
||||||
// This configuration is intentionally not using keyed fields to force anyone
|
// This configuration is intentionally not using keyed fields to force anyone
|
||||||
// adding flags to the config to also have to set these fields.
|
// adding flags to the config to also have to set these fields.
|
||||||
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil}
|
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil}
|
||||||
|
|
||||||
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
|
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
|
||||||
// and accepted by the Ethereum core developers into the Clique consensus.
|
// and accepted by the Ethereum core developers into the Clique consensus.
|
||||||
//
|
//
|
||||||
// This configuration is intentionally not using keyed fields to force anyone
|
// This configuration is intentionally not using keyed fields to force anyone
|
||||||
// adding flags to the config to also have to set these fields.
|
// adding flags to the config to also have to set these fields.
|
||||||
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}}
|
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}}
|
||||||
|
|
||||||
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil}
|
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil}
|
||||||
TestRules = TestChainConfig.Rules(new(big.Int))
|
TestRules = TestChainConfig.Rules(new(big.Int))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -117,12 +132,29 @@ type ChainConfig struct {
|
||||||
|
|
||||||
ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium)
|
ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium)
|
||||||
ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated)
|
ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated)
|
||||||
|
Casper *CasperConfig `json:"casper,omitempty"`
|
||||||
|
|
||||||
// Various consensus engines
|
// Various consensus engines
|
||||||
Ethash *EthashConfig `json:"ethash,omitempty"`
|
Ethash *EthashConfig `json:"ethash,omitempty"`
|
||||||
Clique *CliqueConfig `json:"clique,omitempty"`
|
Clique *CliqueConfig `json:"clique,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CasperConfig is the configuration for applying Casper FFG rules on top of
|
||||||
|
// the consensus engine that produces blocks.
|
||||||
|
type CasperConfig struct {
|
||||||
|
ForkBlock *big.Int `json:"forkBlock,omitempty"`
|
||||||
|
ContractAddr common.Address `json:"contractAddr,omitempty"`
|
||||||
|
ContractCode []byte `json:"contractCode,omitempty"`
|
||||||
|
Balance *big.Int `json:"balance,omitempty"`
|
||||||
|
MsgHasherAddr common.Address `json:"msgHasherAddr,omitempty"`
|
||||||
|
MsgHasherCode []byte `json:"msgHasherCode,omitempty"`
|
||||||
|
PurityCheckerAddr common.Address `json:"purityCheckerAddr,omitempty"`
|
||||||
|
PurityCheckerCode []byte `json:"purityCheckerCode,omitempty"`
|
||||||
|
BlockReward *big.Int `json:"blockReward,omitempty"`
|
||||||
|
RewardStpDwnBlkCnt *big.Int `json:"rewardStpDwnBlkCnt,omitempty"`
|
||||||
|
NonRevertMinDeposit *big.Int `json:"nonRevertMinDeposit,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// EthashConfig is the consensus engine configs for proof-of-work based sealing.
|
// EthashConfig is the consensus engine configs for proof-of-work based sealing.
|
||||||
type EthashConfig struct{}
|
type EthashConfig struct{}
|
||||||
|
|
||||||
|
|
@ -153,7 +185,7 @@ func (c *ChainConfig) String() string {
|
||||||
default:
|
default:
|
||||||
engine = "unknown"
|
engine = "unknown"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Engine: %v}",
|
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Casper: %v Engine: %v}",
|
||||||
c.ChainId,
|
c.ChainId,
|
||||||
c.HomesteadBlock,
|
c.HomesteadBlock,
|
||||||
c.DAOForkBlock,
|
c.DAOForkBlock,
|
||||||
|
|
@ -163,6 +195,7 @@ func (c *ChainConfig) String() string {
|
||||||
c.EIP158Block,
|
c.EIP158Block,
|
||||||
c.ByzantiumBlock,
|
c.ByzantiumBlock,
|
||||||
c.ConstantinopleBlock,
|
c.ConstantinopleBlock,
|
||||||
|
c.Casper,
|
||||||
engine,
|
engine,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -197,6 +230,10 @@ func (c *ChainConfig) IsConstantinople(num *big.Int) bool {
|
||||||
return isForked(c.ConstantinopleBlock, num)
|
return isForked(c.ConstantinopleBlock, num)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ChainConfig) IsCasper(num *big.Int) bool {
|
||||||
|
return c.Casper != nil && isForked(c.Casper.ForkBlock, num)
|
||||||
|
}
|
||||||
|
|
||||||
// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice).
|
// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice).
|
||||||
//
|
//
|
||||||
// The returned GasTable's fields shouldn't, under any circumstances, be changed.
|
// The returned GasTable's fields shouldn't, under any circumstances, be changed.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue