forked from cerc-io/plugeth
core/types, params: add blob transaction type, RLP encoded for now (#27049)
* core/types, params: add blob transaction type, RLP encoded for now * all: integrate Cancun (and timestamp based forks) into MakeSigner * core/types: fix 2 back-and-forth type refactors * core: fix review comment * core/types: swap blob tx type id to 0x03
This commit is contained in:
+3
-3
@@ -84,7 +84,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
||||
toaddr := common.Address{}
|
||||
data := make([]byte, nbytes)
|
||||
gas, _ := IntrinsicGas(data, nil, false, false, false, false)
|
||||
signer := types.MakeSigner(gen.config, big.NewInt(int64(i)))
|
||||
signer := types.MakeSigner(gen.config, big.NewInt(int64(i)), gen.header.Time)
|
||||
gasPrice := big.NewInt(0)
|
||||
if gen.header.BaseFee != nil {
|
||||
gasPrice = gen.header.BaseFee
|
||||
@@ -128,7 +128,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
|
||||
if gen.header.BaseFee != nil {
|
||||
gasPrice = gen.header.BaseFee
|
||||
}
|
||||
signer := types.MakeSigner(gen.config, big.NewInt(int64(i)))
|
||||
signer := types.MakeSigner(gen.config, big.NewInt(int64(i)), gen.header.Time)
|
||||
for {
|
||||
gas -= params.TxGas
|
||||
if gas < params.TxGas {
|
||||
@@ -317,7 +317,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||
if full {
|
||||
hash := header.Hash()
|
||||
rawdb.ReadBody(db, hash, n)
|
||||
rawdb.ReadReceipts(db, hash, n, chain.Config())
|
||||
rawdb.ReadReceipts(db, hash, n, header.Time, chain.Config())
|
||||
}
|
||||
}
|
||||
chain.Stop()
|
||||
|
||||
+2
-2
@@ -1540,7 +1540,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool)
|
||||
}
|
||||
|
||||
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
|
||||
SenderCacher.RecoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
|
||||
SenderCacher.RecoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number(), chain[0].Time()), chain)
|
||||
|
||||
var (
|
||||
stats = insertStats{startTime: mclock.Now()}
|
||||
@@ -2049,7 +2049,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block) (common.Hash, error)
|
||||
// the processing of a block. These logs are later announced as deleted or reborn.
|
||||
func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log {
|
||||
receipts := rawdb.ReadRawReceipts(bc.db, b.Hash(), b.NumberU64())
|
||||
receipts.DeriveFields(bc.chainConfig, b.Hash(), b.NumberU64(), b.BaseFee(), b.Transactions())
|
||||
receipts.DeriveFields(bc.chainConfig, b.Hash(), b.NumberU64(), b.Time(), b.BaseFee(), b.Transactions())
|
||||
|
||||
var logs []*types.Log
|
||||
for _, receipt := range receipts {
|
||||
|
||||
@@ -217,7 +217,11 @@ func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||
if number == nil {
|
||||
return nil
|
||||
}
|
||||
receipts := rawdb.ReadReceipts(bc.db, hash, *number, bc.chainConfig)
|
||||
header := bc.GetHeader(hash, *number)
|
||||
if header == nil {
|
||||
return nil
|
||||
}
|
||||
receipts := rawdb.ReadReceipts(bc.db, hash, *number, header.Time, bc.chainConfig)
|
||||
if receipts == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -809,7 +809,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
|
||||
// Iterate over all chain data components, and cross reference
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
num, hash := blocks[i].NumberU64(), blocks[i].Hash()
|
||||
num, hash, time := blocks[i].NumberU64(), blocks[i].Hash(), blocks[i].Time()
|
||||
|
||||
if ftd, atd := fast.GetTd(hash, num), archive.GetTd(hash, num); ftd.Cmp(atd) != 0 {
|
||||
t.Errorf("block #%d [%x]: td mismatch: fastdb %v, archivedb %v", num, hash, ftd, atd)
|
||||
@@ -832,9 +832,9 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
}
|
||||
|
||||
// Check receipts.
|
||||
freceipts := rawdb.ReadReceipts(fastDb, hash, num, fast.Config())
|
||||
anreceipts := rawdb.ReadReceipts(ancientDb, hash, num, fast.Config())
|
||||
areceipts := rawdb.ReadReceipts(archiveDb, hash, num, fast.Config())
|
||||
freceipts := rawdb.ReadReceipts(fastDb, hash, num, time, fast.Config())
|
||||
anreceipts := rawdb.ReadReceipts(ancientDb, hash, num, time, fast.Config())
|
||||
areceipts := rawdb.ReadReceipts(archiveDb, hash, num, time, fast.Config())
|
||||
if types.DeriveSha(freceipts, trie.NewStackTrie(nil)) != types.DeriveSha(areceipts, trie.NewStackTrie(nil)) {
|
||||
t.Errorf("block #%d [%x]: receipts mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, freceipts, anreceipts, areceipts)
|
||||
}
|
||||
|
||||
@@ -625,7 +625,7 @@ func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Rec
|
||||
// The current implementation populates these metadata fields by reading the receipts'
|
||||
// corresponding block body, so if the block body is not found it will return nil even
|
||||
// if the receipt itself is stored.
|
||||
func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64, config *params.ChainConfig) types.Receipts {
|
||||
func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64, time uint64, config *params.ChainConfig) types.Receipts {
|
||||
// We're deriving many fields from the block body, retrieve beside the receipt
|
||||
receipts := ReadRawReceipts(db, hash, number)
|
||||
if receipts == nil {
|
||||
@@ -643,7 +643,7 @@ func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64, config *para
|
||||
} else {
|
||||
baseFee = header.BaseFee
|
||||
}
|
||||
if err := receipts.DeriveFields(config, hash, number, baseFee, body.Transactions); err != nil {
|
||||
if err := receipts.DeriveFields(config, hash, number, time, baseFee, body.Transactions); err != nil {
|
||||
log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -379,7 +379,7 @@ func TestBlockReceiptStorage(t *testing.T) {
|
||||
|
||||
// Check that no receipt entries are in a pristine database
|
||||
hash := common.BytesToHash([]byte{0x03, 0x14})
|
||||
if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) != 0 {
|
||||
if rs := ReadReceipts(db, hash, 0, 0, params.TestChainConfig); len(rs) != 0 {
|
||||
t.Fatalf("non existent receipts returned: %v", rs)
|
||||
}
|
||||
// Insert the body that corresponds to the receipts
|
||||
@@ -387,7 +387,7 @@ func TestBlockReceiptStorage(t *testing.T) {
|
||||
|
||||
// Insert the receipt slice into the database and check presence
|
||||
WriteReceipts(db, hash, 0, receipts)
|
||||
if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) == 0 {
|
||||
if rs := ReadReceipts(db, hash, 0, 0, params.TestChainConfig); len(rs) == 0 {
|
||||
t.Fatalf("no receipts returned")
|
||||
} else {
|
||||
if err := checkReceiptsRLP(rs, receipts); err != nil {
|
||||
@@ -396,7 +396,7 @@ func TestBlockReceiptStorage(t *testing.T) {
|
||||
}
|
||||
// Delete the body and ensure that the receipts are no longer returned (metadata can't be recomputed)
|
||||
DeleteBody(db, hash, 0)
|
||||
if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); rs != nil {
|
||||
if rs := ReadReceipts(db, hash, 0, 0, params.TestChainConfig); rs != nil {
|
||||
t.Fatalf("receipts returned when body was deleted: %v", rs)
|
||||
}
|
||||
// Ensure that receipts without metadata can be returned without the block body too
|
||||
@@ -407,7 +407,7 @@ func TestBlockReceiptStorage(t *testing.T) {
|
||||
WriteBody(db, hash, 0, body)
|
||||
|
||||
DeleteReceipts(db, hash, 0)
|
||||
if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) != 0 {
|
||||
if rs := ReadReceipts(db, hash, 0, 0, params.TestChainConfig); len(rs) != 0 {
|
||||
t.Fatalf("deleted receipts returned: %v", rs)
|
||||
}
|
||||
}
|
||||
@@ -727,7 +727,7 @@ func TestReadLogs(t *testing.T) {
|
||||
|
||||
hash := common.BytesToHash([]byte{0x03, 0x14})
|
||||
// Check that no receipt entries are in a pristine database
|
||||
if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) != 0 {
|
||||
if rs := ReadReceipts(db, hash, 0, 0, params.TestChainConfig); len(rs) != 0 {
|
||||
t.Fatalf("non existent receipts returned: %v", rs)
|
||||
}
|
||||
// Insert the body that corresponds to the receipts
|
||||
|
||||
@@ -130,8 +130,12 @@ func ReadReceipt(db ethdb.Reader, hash common.Hash, config *params.ChainConfig)
|
||||
if blockHash == (common.Hash{}) {
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
blockHeader := ReadHeader(db, blockHash, *blockNumber)
|
||||
if blockHeader == nil {
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
// Read all the receipts from the block and return the one with the matching hash
|
||||
receipts := ReadReceipts(db, blockHash, *blockNumber, config)
|
||||
receipts := ReadReceipts(db, blockHash, *blockNumber, blockHeader.Time, config)
|
||||
for receiptIndex, receipt := range receipts {
|
||||
if receipt.TxHash == hash {
|
||||
return receipt, blockHash, *blockNumber, uint64(receiptIndex)
|
||||
|
||||
@@ -53,7 +53,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
|
||||
gaspool = new(GasPool).AddGas(block.GasLimit())
|
||||
blockContext = NewEVMBlockContext(header, p.bc, nil)
|
||||
evm = vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
|
||||
signer = types.MakeSigner(p.config, header.Number)
|
||||
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
||||
)
|
||||
// Iterate over and process the individual transactions
|
||||
byzantium := p.config.IsByzantium(block.Number())
|
||||
|
||||
@@ -70,11 +70,14 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
|
||||
misc.ApplyDAOHardFork(statedb)
|
||||
}
|
||||
blockContext := NewEVMBlockContext(header, p.bc, nil)
|
||||
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
|
||||
var (
|
||||
context = NewEVMBlockContext(header, p.bc, nil)
|
||||
vmenv = vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg)
|
||||
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
||||
)
|
||||
// Iterate over and process the individual transactions
|
||||
for i, tx := range block.Transactions() {
|
||||
msg, err := TransactionToMessage(tx, types.MakeSigner(p.config, header.Number), header.BaseFee)
|
||||
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
||||
if err != nil {
|
||||
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||
}
|
||||
@@ -147,7 +150,7 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta
|
||||
// for the transaction, gas used and an error if the transaction failed,
|
||||
// indicating the block was invalid.
|
||||
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) {
|
||||
msg, err := TransactionToMessage(tx, types.MakeSigner(config, header.Number), header.BaseFee)
|
||||
msg, err := TransactionToMessage(tx, types.MakeSigner(config, header.Number, header.Time), header.BaseFee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -606,6 +606,10 @@ func (pool *TxPool) validateTxBasics(tx *types.Transaction, local bool) error {
|
||||
if !pool.eip1559.Load() && tx.Type() == types.DynamicFeeTxType {
|
||||
return core.ErrTxTypeNotSupported
|
||||
}
|
||||
// Reject blob transactions forever, those will have their own pool.
|
||||
if tx.Type() == types.BlobTxType {
|
||||
return core.ErrTxTypeNotSupported
|
||||
}
|
||||
// Reject transactions over defined size to prevent DOS attacks
|
||||
if tx.Size() > txMaxSize {
|
||||
return ErrOversizedData
|
||||
|
||||
@@ -313,8 +313,8 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
|
||||
|
||||
// DeriveFields fills the receipts with their computed fields based on consensus
|
||||
// data and contextual infos like containing block and transactions.
|
||||
func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, baseFee *big.Int, txs []*Transaction) error {
|
||||
signer := MakeSigner(config, new(big.Int).SetUint64(number))
|
||||
func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, time uint64, baseFee *big.Int, txs []*Transaction) error {
|
||||
signer := MakeSigner(config, new(big.Int).SetUint64(number), time)
|
||||
|
||||
logIndex := uint(0)
|
||||
if len(txs) != len(rs) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/kylelemons/godebug/diff"
|
||||
)
|
||||
|
||||
@@ -99,6 +100,8 @@ func TestDeriveFields(t *testing.T) {
|
||||
to3 := common.HexToAddress("0x3")
|
||||
to4 := common.HexToAddress("0x4")
|
||||
to5 := common.HexToAddress("0x5")
|
||||
to6 := common.HexToAddress("0x6")
|
||||
to7 := common.HexToAddress("0x7")
|
||||
txs := Transactions{
|
||||
NewTx(&LegacyTx{
|
||||
Nonce: 1,
|
||||
@@ -127,19 +130,39 @@ func TestDeriveFields(t *testing.T) {
|
||||
Value: big.NewInt(4),
|
||||
Gas: 4,
|
||||
GasTipCap: big.NewInt(44),
|
||||
GasFeeCap: big.NewInt(1045),
|
||||
GasFeeCap: big.NewInt(1044),
|
||||
}),
|
||||
NewTx(&DynamicFeeTx{
|
||||
To: &to5,
|
||||
Nonce: 5,
|
||||
Value: big.NewInt(5),
|
||||
Gas: 5,
|
||||
GasTipCap: big.NewInt(56),
|
||||
GasTipCap: big.NewInt(55),
|
||||
GasFeeCap: big.NewInt(1055),
|
||||
}),
|
||||
// EIP-4844 transactions.
|
||||
NewTx(&BlobTx{
|
||||
To: &to6,
|
||||
Nonce: 6,
|
||||
Value: uint256.NewInt(6),
|
||||
Gas: 6,
|
||||
GasTipCap: uint256.NewInt(66),
|
||||
GasFeeCap: uint256.NewInt(1066),
|
||||
BlobFeeCap: uint256.NewInt(100066),
|
||||
}),
|
||||
NewTx(&BlobTx{
|
||||
To: &to7,
|
||||
Nonce: 7,
|
||||
Value: uint256.NewInt(7),
|
||||
Gas: 7,
|
||||
GasTipCap: uint256.NewInt(77),
|
||||
GasFeeCap: uint256.NewInt(1077),
|
||||
BlobFeeCap: uint256.NewInt(100077),
|
||||
}),
|
||||
}
|
||||
|
||||
blockNumber := big.NewInt(1)
|
||||
blockTime := uint64(2)
|
||||
blockHash := common.BytesToHash([]byte{0x03, 0x14})
|
||||
|
||||
// Create the corresponding receipts
|
||||
@@ -246,12 +269,38 @@ func TestDeriveFields(t *testing.T) {
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 4,
|
||||
},
|
||||
&Receipt{
|
||||
Type: BlobTxType,
|
||||
PostState: common.Hash{6}.Bytes(),
|
||||
CumulativeGasUsed: 21,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[5].Hash(),
|
||||
GasUsed: 6,
|
||||
EffectiveGasPrice: big.NewInt(1066),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 5,
|
||||
},
|
||||
&Receipt{
|
||||
Type: BlobTxType,
|
||||
PostState: common.Hash{7}.Bytes(),
|
||||
CumulativeGasUsed: 28,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[6].Hash(),
|
||||
GasUsed: 7,
|
||||
EffectiveGasPrice: big.NewInt(1077),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 6,
|
||||
},
|
||||
}
|
||||
|
||||
// Re-derive receipts.
|
||||
basefee := big.NewInt(1000)
|
||||
derivedReceipts := clearComputedFieldsOnReceipts(receipts)
|
||||
err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), basefee, txs)
|
||||
err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), blockTime, basefee, txs)
|
||||
if err != nil {
|
||||
t.Fatalf("DeriveFields(...) = %v, want <nil>", err)
|
||||
}
|
||||
|
||||
@@ -42,9 +42,10 @@ var (
|
||||
|
||||
// Transaction types.
|
||||
const (
|
||||
LegacyTxType = iota
|
||||
AccessListTxType
|
||||
DynamicFeeTxType
|
||||
LegacyTxType = 0x00
|
||||
AccessListTxType = 0x01
|
||||
DynamicFeeTxType = 0x02
|
||||
BlobTxType = 0x03
|
||||
)
|
||||
|
||||
// Transaction is an Ethereum transaction.
|
||||
@@ -82,6 +83,9 @@ type TxData interface {
|
||||
value() *big.Int
|
||||
nonce() uint64
|
||||
to() *common.Address
|
||||
blobGas() uint64
|
||||
blobGasFeeCap() *big.Int
|
||||
blobHashes() []common.Hash
|
||||
|
||||
rawSignatureValues() (v, r, s *big.Int)
|
||||
setSignatureValues(chainID, v, r, s *big.Int)
|
||||
@@ -192,6 +196,10 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
|
||||
var inner DynamicFeeTx
|
||||
err := rlp.DecodeBytes(b[1:], &inner)
|
||||
return &inner, err
|
||||
case BlobTxType:
|
||||
var inner BlobTx
|
||||
err := rlp.DecodeBytes(b[1:], &inner) // TODO(karalabe): This needs to be ssz
|
||||
return &inner, err
|
||||
default:
|
||||
return nil, ErrTxTypeNotSupported
|
||||
}
|
||||
@@ -281,6 +289,15 @@ func (tx *Transaction) GasTipCap() *big.Int { return new(big.Int).Set(tx.inner.g
|
||||
// GasFeeCap returns the fee cap per gas of the transaction.
|
||||
func (tx *Transaction) GasFeeCap() *big.Int { return new(big.Int).Set(tx.inner.gasFeeCap()) }
|
||||
|
||||
// BlobGas returns the data gas limit of the transaction for blob transactions, 0 otherwise.
|
||||
func (tx *Transaction) BlobGas() uint64 { return tx.inner.blobGas() }
|
||||
|
||||
// BlobGasFeeCap returns the data gas fee cap per data gas of the transaction for blob transactions, nil otherwise.
|
||||
func (tx *Transaction) BlobGasFeeCap() *big.Int { return tx.inner.blobGasFeeCap() }
|
||||
|
||||
// BlobHashes returns the hases of the blob commitments for blob transactions, nil otherwise.
|
||||
func (tx *Transaction) BlobHashes() []common.Hash { return tx.inner.blobHashes() }
|
||||
|
||||
// Value returns the ether amount of the transaction.
|
||||
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) }
|
||||
|
||||
@@ -293,9 +310,12 @@ func (tx *Transaction) To() *common.Address {
|
||||
return copyAddressPtr(tx.inner.to())
|
||||
}
|
||||
|
||||
// Cost returns gas * gasPrice + value.
|
||||
// Cost returns (gas * gasPrice) + (blobGas * blobGasPrice) + value.
|
||||
func (tx *Transaction) Cost() *big.Int {
|
||||
total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas()))
|
||||
if tx.Type() == BlobTxType {
|
||||
total.Add(total, new(big.Int).Mul(tx.BlobGasFeeCap(), new(big.Int).SetUint64(tx.BlobGas())))
|
||||
}
|
||||
total.Add(total, tx.Value())
|
||||
return total
|
||||
}
|
||||
@@ -364,6 +384,16 @@ func (tx *Transaction) EffectiveGasTipIntCmp(other *big.Int, baseFee *big.Int) i
|
||||
return tx.EffectiveGasTipValue(baseFee).Cmp(other)
|
||||
}
|
||||
|
||||
// BlobGasFeeCapCmp compares the blob fee cap of two transactions.
|
||||
func (tx *Transaction) BlobGasFeeCapCmp(other *Transaction) int {
|
||||
return tx.inner.blobGasFeeCap().Cmp(other.inner.blobGasFeeCap())
|
||||
}
|
||||
|
||||
// BlobGasFeeCapIntCmp compares the blob fee cap of the transaction against the given blob fee cap.
|
||||
func (tx *Transaction) BlobGasFeeCapIntCmp(other *big.Int) int {
|
||||
return tx.inner.blobGasFeeCap().Cmp(other)
|
||||
}
|
||||
|
||||
// Hash returns the transaction hash.
|
||||
func (tx *Transaction) Hash() common.Hash {
|
||||
if hash := tx.hash.Load(); hash != nil {
|
||||
@@ -387,7 +417,11 @@ func (tx *Transaction) Size() uint64 {
|
||||
return size.(uint64)
|
||||
}
|
||||
c := writeCounter(0)
|
||||
rlp.Encode(&c, &tx.inner)
|
||||
if tx.Type() == BlobTxType {
|
||||
rlp.Encode(&c, &tx.inner) // TODO(karalabe): Replace with SSZ encoding
|
||||
} else {
|
||||
rlp.Encode(&c, &tx.inner)
|
||||
}
|
||||
|
||||
size := uint64(c)
|
||||
if tx.Type() != LegacyTxType {
|
||||
|
||||
@@ -37,9 +37,11 @@ type sigCache struct {
|
||||
}
|
||||
|
||||
// MakeSigner returns a Signer based on the given chain config and block number.
|
||||
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
|
||||
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int, blockTime uint64) Signer {
|
||||
var signer Signer
|
||||
switch {
|
||||
case config.IsCancun(blockTime):
|
||||
signer = NewCancunSigner(config.ChainID)
|
||||
case config.IsLondon(blockNumber):
|
||||
signer = NewLondonSigner(config.ChainID)
|
||||
case config.IsBerlin(blockNumber):
|
||||
@@ -63,6 +65,9 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
|
||||
// have the current block number available, use MakeSigner instead.
|
||||
func LatestSigner(config *params.ChainConfig) Signer {
|
||||
if config.ChainID != nil {
|
||||
if config.CancunTime != nil {
|
||||
return NewCancunSigner(config.ChainID)
|
||||
}
|
||||
if config.LondonBlock != nil {
|
||||
return NewLondonSigner(config.ChainID)
|
||||
}
|
||||
@@ -87,7 +92,7 @@ func LatestSignerForChainID(chainID *big.Int) Signer {
|
||||
if chainID == nil {
|
||||
return HomesteadSigner{}
|
||||
}
|
||||
return NewLondonSigner(chainID)
|
||||
return NewCancunSigner(chainID)
|
||||
}
|
||||
|
||||
// SignTx signs the transaction using the given signer and private key.
|
||||
@@ -170,6 +175,75 @@ type Signer interface {
|
||||
Equal(Signer) bool
|
||||
}
|
||||
|
||||
type cancunSigner struct{ londonSigner }
|
||||
|
||||
// NewCancunSigner returns a signer that accepts
|
||||
// - EIP-4844 blob transactions
|
||||
// - EIP-1559 dynamic fee transactions
|
||||
// - EIP-2930 access list transactions,
|
||||
// - EIP-155 replay protected transactions, and
|
||||
// - legacy Homestead transactions.
|
||||
func NewCancunSigner(chainId *big.Int) Signer {
|
||||
return cancunSigner{londonSigner{eip2930Signer{NewEIP155Signer(chainId)}}}
|
||||
}
|
||||
|
||||
func (s cancunSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||
if tx.Type() != BlobTxType {
|
||||
return s.londonSigner.Sender(tx)
|
||||
}
|
||||
V, R, S := tx.RawSignatureValues()
|
||||
// Blob txs are defined to use 0 and 1 as their recovery
|
||||
// id, add 27 to become equivalent to unprotected Homestead signatures.
|
||||
V = new(big.Int).Add(V, big.NewInt(27))
|
||||
if tx.ChainId().Cmp(s.chainId) != 0 {
|
||||
return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId)
|
||||
}
|
||||
return recoverPlain(s.Hash(tx), R, S, V, true)
|
||||
}
|
||||
|
||||
func (s cancunSigner) Equal(s2 Signer) bool {
|
||||
x, ok := s2.(cancunSigner)
|
||||
return ok && x.chainId.Cmp(s.chainId) == 0
|
||||
}
|
||||
|
||||
func (s cancunSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
|
||||
txdata, ok := tx.inner.(*BlobTx)
|
||||
if !ok {
|
||||
return s.londonSigner.SignatureValues(tx, sig)
|
||||
}
|
||||
// Check that chain ID of tx matches the signer. We also accept ID zero here,
|
||||
// because it indicates that the chain ID was not specified in the tx.
|
||||
if txdata.ChainID.Sign() != 0 && txdata.ChainID.ToBig().Cmp(s.chainId) != 0 {
|
||||
return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId)
|
||||
}
|
||||
R, S, _ = decodeSignature(sig)
|
||||
V = big.NewInt(int64(sig[64]))
|
||||
return R, S, V, nil
|
||||
}
|
||||
|
||||
// Hash returns the hash to be signed by the sender.
|
||||
// It does not uniquely identify the transaction.
|
||||
func (s cancunSigner) Hash(tx *Transaction) common.Hash {
|
||||
if tx.Type() != BlobTxType {
|
||||
return s.londonSigner.Hash(tx)
|
||||
}
|
||||
return prefixedRlpHash(
|
||||
tx.Type(),
|
||||
[]interface{}{
|
||||
s.chainId,
|
||||
tx.Nonce(),
|
||||
tx.GasTipCap(),
|
||||
tx.GasFeeCap(),
|
||||
tx.Gas(),
|
||||
tx.To(),
|
||||
tx.Value(),
|
||||
tx.Data(),
|
||||
tx.AccessList(),
|
||||
tx.BlobGasFeeCap(),
|
||||
tx.BlobHashes(),
|
||||
})
|
||||
}
|
||||
|
||||
type londonSigner struct{ eip2930Signer }
|
||||
|
||||
// NewLondonSigner returns a signer that accepts
|
||||
|
||||
@@ -94,17 +94,20 @@ func (tx *AccessListTx) copy() TxData {
|
||||
}
|
||||
|
||||
// accessors for innerTx.
|
||||
func (tx *AccessListTx) txType() byte { return AccessListTxType }
|
||||
func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID }
|
||||
func (tx *AccessListTx) accessList() AccessList { return tx.AccessList }
|
||||
func (tx *AccessListTx) data() []byte { return tx.Data }
|
||||
func (tx *AccessListTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice }
|
||||
func (tx *AccessListTx) gasTipCap() *big.Int { return tx.GasPrice }
|
||||
func (tx *AccessListTx) gasFeeCap() *big.Int { return tx.GasPrice }
|
||||
func (tx *AccessListTx) value() *big.Int { return tx.Value }
|
||||
func (tx *AccessListTx) nonce() uint64 { return tx.Nonce }
|
||||
func (tx *AccessListTx) to() *common.Address { return tx.To }
|
||||
func (tx *AccessListTx) txType() byte { return AccessListTxType }
|
||||
func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID }
|
||||
func (tx *AccessListTx) accessList() AccessList { return tx.AccessList }
|
||||
func (tx *AccessListTx) data() []byte { return tx.Data }
|
||||
func (tx *AccessListTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice }
|
||||
func (tx *AccessListTx) gasTipCap() *big.Int { return tx.GasPrice }
|
||||
func (tx *AccessListTx) gasFeeCap() *big.Int { return tx.GasPrice }
|
||||
func (tx *AccessListTx) value() *big.Int { return tx.Value }
|
||||
func (tx *AccessListTx) nonce() uint64 { return tx.Nonce }
|
||||
func (tx *AccessListTx) to() *common.Address { return tx.To }
|
||||
func (tx *AccessListTx) blobGas() uint64 { return 0 }
|
||||
func (tx *AccessListTx) blobGasFeeCap() *big.Int { return nil }
|
||||
func (tx *AccessListTx) blobHashes() []common.Hash { return nil }
|
||||
|
||||
func (tx *AccessListTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
|
||||
return dst.Set(tx.GasPrice)
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
// Copyright 2023 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// BlobTx represents an EIP-4844 transaction.
|
||||
type BlobTx struct {
|
||||
ChainID *uint256.Int
|
||||
Nonce uint64
|
||||
GasTipCap *uint256.Int // a.k.a. maxPriorityFeePerGas
|
||||
GasFeeCap *uint256.Int // a.k.a. maxFeePerGas
|
||||
Gas uint64
|
||||
To *common.Address // `rlp:"nil"` // nil means contract creation
|
||||
Value *uint256.Int
|
||||
Data []byte
|
||||
AccessList AccessList
|
||||
BlobFeeCap *uint256.Int // a.k.a. maxFeePerDataGas
|
||||
BlobHashes []common.Hash
|
||||
|
||||
// Signature values
|
||||
V *uint256.Int `json:"v" gencodec:"required"`
|
||||
R *uint256.Int `json:"r" gencodec:"required"`
|
||||
S *uint256.Int `json:"s" gencodec:"required"`
|
||||
}
|
||||
|
||||
// copy creates a deep copy of the transaction data and initializes all fields.
|
||||
func (tx *BlobTx) copy() TxData {
|
||||
cpy := &BlobTx{
|
||||
Nonce: tx.Nonce,
|
||||
To: copyAddressPtr(tx.To),
|
||||
Data: common.CopyBytes(tx.Data),
|
||||
Gas: tx.Gas,
|
||||
// These are copied below.
|
||||
AccessList: make(AccessList, len(tx.AccessList)),
|
||||
BlobHashes: make([]common.Hash, len(tx.BlobHashes)),
|
||||
Value: new(uint256.Int),
|
||||
ChainID: new(uint256.Int),
|
||||
GasTipCap: new(uint256.Int),
|
||||
GasFeeCap: new(uint256.Int),
|
||||
BlobFeeCap: new(uint256.Int),
|
||||
V: new(uint256.Int),
|
||||
R: new(uint256.Int),
|
||||
S: new(uint256.Int),
|
||||
}
|
||||
copy(cpy.AccessList, tx.AccessList)
|
||||
copy(cpy.BlobHashes, tx.BlobHashes)
|
||||
|
||||
if tx.Value != nil {
|
||||
cpy.Value.Set(tx.Value)
|
||||
}
|
||||
if tx.ChainID != nil {
|
||||
cpy.ChainID.Set(tx.ChainID)
|
||||
}
|
||||
if tx.GasTipCap != nil {
|
||||
cpy.GasTipCap.Set(tx.GasTipCap)
|
||||
}
|
||||
if tx.GasFeeCap != nil {
|
||||
cpy.GasFeeCap.Set(tx.GasFeeCap)
|
||||
}
|
||||
if tx.BlobFeeCap != nil {
|
||||
cpy.BlobFeeCap.Set(tx.BlobFeeCap)
|
||||
}
|
||||
if tx.V != nil {
|
||||
cpy.V.Set(tx.V)
|
||||
}
|
||||
if tx.R != nil {
|
||||
cpy.R.Set(tx.R)
|
||||
}
|
||||
if tx.S != nil {
|
||||
cpy.S.Set(tx.S)
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
// accessors for innerTx.
|
||||
func (tx *BlobTx) txType() byte { return BlobTxType }
|
||||
func (tx *BlobTx) chainID() *big.Int { return tx.ChainID.ToBig() }
|
||||
func (tx *BlobTx) accessList() AccessList { return tx.AccessList }
|
||||
func (tx *BlobTx) data() []byte { return tx.Data }
|
||||
func (tx *BlobTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *BlobTx) gasFeeCap() *big.Int { return tx.GasFeeCap.ToBig() }
|
||||
func (tx *BlobTx) gasTipCap() *big.Int { return tx.GasTipCap.ToBig() }
|
||||
func (tx *BlobTx) gasPrice() *big.Int { return tx.GasFeeCap.ToBig() }
|
||||
func (tx *BlobTx) value() *big.Int { return tx.Value.ToBig() }
|
||||
func (tx *BlobTx) nonce() uint64 { return tx.Nonce }
|
||||
func (tx *BlobTx) to() *common.Address { return tx.To }
|
||||
func (tx *BlobTx) blobGas() uint64 { return params.BlobTxDataGasPerBlob * uint64(len(tx.BlobHashes)) }
|
||||
func (tx *BlobTx) blobGasFeeCap() *big.Int { return tx.BlobFeeCap.ToBig() }
|
||||
func (tx *BlobTx) blobHashes() []common.Hash { return tx.BlobHashes }
|
||||
|
||||
func (tx *BlobTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
|
||||
if baseFee == nil {
|
||||
return dst.Set(tx.GasFeeCap.ToBig())
|
||||
}
|
||||
tip := dst.Sub(tx.GasFeeCap.ToBig(), baseFee)
|
||||
if tip.Cmp(tx.GasTipCap.ToBig()) > 0 {
|
||||
tip.Set(tx.GasTipCap.ToBig())
|
||||
}
|
||||
return tip.Add(tip, baseFee)
|
||||
}
|
||||
|
||||
func (tx *BlobTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||
return tx.V.ToBig(), tx.R.ToBig(), tx.S.ToBig()
|
||||
}
|
||||
|
||||
func (tx *BlobTx) setSignatureValues(chainID, v, r, s *big.Int) {
|
||||
tx.ChainID.SetFromBig(chainID)
|
||||
tx.V.SetFromBig(v)
|
||||
tx.R.SetFromBig(r)
|
||||
tx.S.SetFromBig(s)
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// DynamicFeeTx represents an EIP-1559 transaction.
|
||||
type DynamicFeeTx struct {
|
||||
ChainID *big.Int
|
||||
Nonce uint64
|
||||
@@ -82,17 +83,20 @@ func (tx *DynamicFeeTx) copy() TxData {
|
||||
}
|
||||
|
||||
// accessors for innerTx.
|
||||
func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType }
|
||||
func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID }
|
||||
func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList }
|
||||
func (tx *DynamicFeeTx) data() []byte { return tx.Data }
|
||||
func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *DynamicFeeTx) gasFeeCap() *big.Int { return tx.GasFeeCap }
|
||||
func (tx *DynamicFeeTx) gasTipCap() *big.Int { return tx.GasTipCap }
|
||||
func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap }
|
||||
func (tx *DynamicFeeTx) value() *big.Int { return tx.Value }
|
||||
func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce }
|
||||
func (tx *DynamicFeeTx) to() *common.Address { return tx.To }
|
||||
func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType }
|
||||
func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID }
|
||||
func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList }
|
||||
func (tx *DynamicFeeTx) data() []byte { return tx.Data }
|
||||
func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *DynamicFeeTx) gasFeeCap() *big.Int { return tx.GasFeeCap }
|
||||
func (tx *DynamicFeeTx) gasTipCap() *big.Int { return tx.GasTipCap }
|
||||
func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap }
|
||||
func (tx *DynamicFeeTx) value() *big.Int { return tx.Value }
|
||||
func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce }
|
||||
func (tx *DynamicFeeTx) to() *common.Address { return tx.To }
|
||||
func (tx *DynamicFeeTx) blobGas() uint64 { return 0 }
|
||||
func (tx *DynamicFeeTx) blobGasFeeCap() *big.Int { return nil }
|
||||
func (tx *DynamicFeeTx) blobHashes() []common.Hash { return nil }
|
||||
|
||||
func (tx *DynamicFeeTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
|
||||
if baseFee == nil {
|
||||
|
||||
+15
-12
@@ -22,7 +22,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// LegacyTx is the transaction data of regular Ethereum transactions.
|
||||
// LegacyTx is the transaction data of the original Ethereum transactions.
|
||||
type LegacyTx struct {
|
||||
Nonce uint64 // nonce of sender account
|
||||
GasPrice *big.Int // wei per gas
|
||||
@@ -91,17 +91,20 @@ func (tx *LegacyTx) copy() TxData {
|
||||
}
|
||||
|
||||
// accessors for innerTx.
|
||||
func (tx *LegacyTx) txType() byte { return LegacyTxType }
|
||||
func (tx *LegacyTx) chainID() *big.Int { return deriveChainId(tx.V) }
|
||||
func (tx *LegacyTx) accessList() AccessList { return nil }
|
||||
func (tx *LegacyTx) data() []byte { return tx.Data }
|
||||
func (tx *LegacyTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice }
|
||||
func (tx *LegacyTx) gasTipCap() *big.Int { return tx.GasPrice }
|
||||
func (tx *LegacyTx) gasFeeCap() *big.Int { return tx.GasPrice }
|
||||
func (tx *LegacyTx) value() *big.Int { return tx.Value }
|
||||
func (tx *LegacyTx) nonce() uint64 { return tx.Nonce }
|
||||
func (tx *LegacyTx) to() *common.Address { return tx.To }
|
||||
func (tx *LegacyTx) txType() byte { return LegacyTxType }
|
||||
func (tx *LegacyTx) chainID() *big.Int { return deriveChainId(tx.V) }
|
||||
func (tx *LegacyTx) accessList() AccessList { return nil }
|
||||
func (tx *LegacyTx) data() []byte { return tx.Data }
|
||||
func (tx *LegacyTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice }
|
||||
func (tx *LegacyTx) gasTipCap() *big.Int { return tx.GasPrice }
|
||||
func (tx *LegacyTx) gasFeeCap() *big.Int { return tx.GasPrice }
|
||||
func (tx *LegacyTx) value() *big.Int { return tx.Value }
|
||||
func (tx *LegacyTx) nonce() uint64 { return tx.Nonce }
|
||||
func (tx *LegacyTx) to() *common.Address { return tx.To }
|
||||
func (tx *LegacyTx) blobGas() uint64 { return 0 }
|
||||
func (tx *LegacyTx) blobGasFeeCap() *big.Int { return nil }
|
||||
func (tx *LegacyTx) blobHashes() []common.Hash { return nil }
|
||||
|
||||
func (tx *LegacyTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
|
||||
return dst.Set(tx.GasPrice)
|
||||
|
||||
Reference in New Issue
Block a user