diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index 70bf2c8ac2..950006ab0a 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -388,7 +388,10 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
b.mu.Lock()
defer b.mu.Unlock()
- sender, err := types.Sender(types.HomesteadSigner{}, tx)
+ // Check transaction validity.
+ block := b.blockchain.CurrentBlock()
+ signer := types.MakeSigner(b.blockchain.Config(), block.Number())
+ sender, err := types.Sender(signer, tx)
if err != nil {
panic(fmt.Errorf("invalid transaction: %v", err))
}
@@ -397,7 +400,8 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce))
}
- blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), b.blockchain.Engine(), b.database, 1, func(number int, block *core.BlockGen) {
+ // Include tx in chain.
+ blocks, _ := core.GenerateChain(b.config, block, b.blockchain.Engine(), b.database, 1, func(number int, block *core.BlockGen) {
for _, tx := range b.pendingBlock.Transactions() {
block.AddTxWithChain(b.blockchain, tx)
}
@@ -506,15 +510,16 @@ type callmsg struct {
XDPoSChain.CallMsg
}
-func (m callmsg) From() common.Address { return m.CallMsg.From }
-func (m callmsg) Nonce() uint64 { return 0 }
-func (m callmsg) CheckNonce() bool { return false }
-func (m callmsg) To() *common.Address { return m.CallMsg.To }
-func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
-func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
-func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
-func (m callmsg) Data() []byte { return m.CallMsg.Data }
-func (m callmsg) BalanceTokenFee() *big.Int { return m.CallMsg.BalanceTokenFee }
+func (m callmsg) From() common.Address { return m.CallMsg.From }
+func (m callmsg) Nonce() uint64 { return 0 }
+func (m callmsg) CheckNonce() bool { return false }
+func (m callmsg) To() *common.Address { return m.CallMsg.To }
+func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
+func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
+func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
+func (m callmsg) Data() []byte { return m.CallMsg.Data }
+func (m callmsg) BalanceTokenFee() *big.Int { return m.CallMsg.BalanceTokenFee }
+func (m callmsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
// filterBackend implements filters.Backend to support filtering for logs without
// taking bloom-bits acceleration structures into account.
diff --git a/accounts/keystore/keystore.go b/accounts/keystore/keystore.go
index 93854794c4..45a3be3036 100644
--- a/accounts/keystore/keystore.go
+++ b/accounts/keystore/keystore.go
@@ -288,11 +288,9 @@ func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *b
if !found {
return nil, ErrLocked
}
- // Depending on the presence of the chain ID, sign with EIP155 or homestead
- if chainID != nil {
- return types.SignTx(tx, types.NewEIP155Signer(chainID), unlockedKey.PrivateKey)
- }
- return types.SignTx(tx, types.HomesteadSigner{}, unlockedKey.PrivateKey)
+ // Depending on the presence of the chain ID, sign with 2718 or homestead
+ signer := types.LatestSignerForChainID(chainID)
+ return types.SignTx(tx, signer, unlockedKey.PrivateKey)
}
// SignHashWithPassphrase signs hash if the private key matching the given address
@@ -316,11 +314,9 @@ func (ks *KeyStore) SignTxWithPassphrase(a accounts.Account, passphrase string,
}
defer zeroKey(key.PrivateKey)
- // Depending on the presence of the chain ID, sign with EIP155 or homestead
- if chainID != nil {
- return types.SignTx(tx, types.NewEIP155Signer(chainID), key.PrivateKey)
- }
- return types.SignTx(tx, types.HomesteadSigner{}, key.PrivateKey)
+ // Depending on the presence of the chain ID, sign with or without replay protection.
+ signer := types.LatestSignerForChainID(chainID)
+ return types.SignTx(tx, signer, key.PrivateKey)
}
// Unlock unlocks the given account indefinitely.
diff --git a/accounts/usbwallet/trezor.go b/accounts/usbwallet/trezor.go
index 551b69e224..04e57292ba 100644
--- a/accounts/usbwallet/trezor.go
+++ b/accounts/usbwallet/trezor.go
@@ -27,13 +27,13 @@ import (
"io"
"math/big"
- "github.com/golang/protobuf/proto"
"github.com/XinFinOrg/XDPoSChain/accounts"
"github.com/XinFinOrg/XDPoSChain/accounts/usbwallet/internal/trezor"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/golang/protobuf/proto"
)
// ErrTrezorPINNeeded is returned if opening the trezor requires a PIN code. In
@@ -80,13 +80,13 @@ func (w *trezorDriver) Status() (string, error) {
// Open implements usbwallet.driver, attempting to initialize the connection to
// the Trezor hardware wallet. Initializing the Trezor is a two phase operation:
-// * The first phase is to initialize the connection and read the wallet's
-// features. This phase is invoked is the provided passphrase is empty. The
-// device will display the pinpad as a result and will return an appropriate
-// error to notify the user that a second open phase is needed.
-// * The second phase is to unlock access to the Trezor, which is done by the
-// user actually providing a passphrase mapping a keyboard keypad to the pin
-// number of the user (shuffled according to the pinpad displayed).
+// - The first phase is to initialize the connection and read the wallet's
+// features. This phase is invoked is the provided passphrase is empty. The
+// device will display the pinpad as a result and will return an appropriate
+// error to notify the user that a second open phase is needed.
+// - The second phase is to unlock access to the Trezor, which is done by the
+// user actually providing a passphrase mapping a keyboard keypad to the pin
+// number of the user (shuffled according to the pinpad displayed).
func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
w.device, w.failure = device, nil
@@ -220,9 +220,11 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
if chainID == nil {
signer = new(types.HomesteadSigner)
} else {
+ // Trezor backend does not support typed transactions yet.
signer = types.NewEIP155Signer(chainID)
signature[64] = signature[64] - byte(chainID.Uint64()*2+35)
}
+
// Inject the final signature into the transaction and sanity check the sender
signed, err := tx.WithSignature(signer, signature)
if err != nil {
diff --git a/consensus/tests/engine_v1_tests/helper.go b/consensus/tests/engine_v1_tests/helper.go
index 3b777e487b..be64d0c050 100644
--- a/consensus/tests/engine_v1_tests/helper.go
+++ b/consensus/tests/engine_v1_tests/helper.go
@@ -163,7 +163,7 @@ func transferTx(t *testing.T, to common.Address, transferAmount int64) *types.Tr
amount := big.NewInt(transferAmount)
nonce := uint64(1)
tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
- signedTX, err := types.SignTx(tx, types.NewEIP155Signer(big.NewInt(chainID)), voterKey)
+ signedTX, err := types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(chainID)), voterKey)
if err != nil {
t.Fatal(err)
}
@@ -183,7 +183,7 @@ func voteTX(gasLimit uint64, nonce uint64, addr string) (*types.Transaction, err
to := common.HexToAddress(common.MasternodeVotingSMC)
tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
- signedTX, err := types.SignTx(tx, types.NewEIP155Signer(big.NewInt(chainID)), voterKey)
+ signedTX, err := types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(chainID)), voterKey)
if err != nil {
return nil, err
}
@@ -411,7 +411,7 @@ func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*ty
// nonce := uint64(0)
// to := common.HexToAddress("xdc35658f7b2a9e7701e65e7a654659eb1c481d1dc5")
// tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
-// signedTX, err := types.SignTx(tx, types.NewEIP155Signer(big.NewInt(chainID)), acc4Key)
+// signedTX, err := types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(chainID)), acc4Key)
// if err != nil {
// t.Fatal(err)
// }
diff --git a/consensus/tests/engine_v2_tests/helper.go b/consensus/tests/engine_v2_tests/helper.go
index f11baf1b2d..2856b55f9f 100644
--- a/consensus/tests/engine_v2_tests/helper.go
+++ b/consensus/tests/engine_v2_tests/helper.go
@@ -108,7 +108,7 @@ func voteTX(gasLimit uint64, nonce uint64, addr string) (*types.Transaction, err
to := common.HexToAddress(common.MasternodeVotingSMC)
tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
- signedTX, err := types.SignTx(tx, types.NewEIP155Signer(big.NewInt(chainID)), voterKey)
+ signedTX, err := types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(chainID)), voterKey)
if err != nil {
return nil, err
}
@@ -285,7 +285,7 @@ func getMultiCandidatesBackend(t *testing.T, chainConfig *params.ChainConfig, n
func signingTxWithKey(header *types.Header, nonce uint64, privateKey *ecdsa.PrivateKey) (*types.Transaction, error) {
tx := contracts.CreateTxSign(header.Number, header.Hash(), nonce, common.HexToAddress(common.BlockSigners))
- s := types.NewEIP155Signer(big.NewInt(chainID))
+ s := types.LatestSignerForChainID(big.NewInt(chainID))
h := s.Hash(tx)
sig, err := crypto.Sign(h[:], privateKey)
if err != nil {
@@ -300,7 +300,7 @@ func signingTxWithKey(header *types.Header, nonce uint64, privateKey *ecdsa.Priv
func signingTxWithSignerFn(header *types.Header, nonce uint64, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) (*types.Transaction, error) {
tx := contracts.CreateTxSign(header.Number, header.Hash(), nonce, common.HexToAddress(common.BlockSigners))
- s := types.NewEIP155Signer(big.NewInt(chainID))
+ s := types.LatestSignerForChainID(big.NewInt(chainID))
h := s.Hash(tx)
sig, err := signFn(accounts.Account{Address: signer}, h[:])
if err != nil {
diff --git a/contracts/trc21issuer/trc21issuer_test.go b/contracts/trc21issuer/trc21issuer_test.go
index e0744b26d1..698aa3f97d 100644
--- a/contracts/trc21issuer/trc21issuer_test.go
+++ b/contracts/trc21issuer/trc21issuer_test.go
@@ -91,10 +91,13 @@ func TestFeeTxWithTRC21Token(t *testing.T) {
t.Fatal("check balance after fail transfer in tr20: ", err, "get", balance, "transfer", airDropAmount)
}
- //check balance fee
+ // check balance fee
balanceIssuerFee, err = trc21Issuer.GetTokenCapacity(trc21TokenAddr)
- if err != nil || balanceIssuerFee.Cmp(remainFee) != 0 {
- t.Fatal("can't get balance token fee in smart contract: ", err, "got", balanceIssuerFee, "wanted", remainFee)
+ if err != nil {
+ t.Fatal("can't get balance token fee in smart contract: ", err)
+ }
+ if balanceIssuerFee.Cmp(remainFee) != 0 {
+ t.Fatal("check balance token fee in smart contract: got", balanceIssuerFee, "wanted", remainFee)
}
//check trc21 SMC balance
balance, err = contractBackend.BalanceAt(nil, trc21IssuerAddr, nil)
diff --git a/core/bench_test.go b/core/bench_test.go
index 5884292402..7cfed07f45 100644
--- a/core/bench_test.go
+++ b/core/bench_test.go
@@ -85,7 +85,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
return func(i int, gen *BlockGen) {
toaddr := common.Address{}
data := make([]byte, nbytes)
- gas, _ := IntrinsicGas(data, false, false)
+ gas, _ := IntrinsicGas(data, nil, false, false)
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey)
gen.AddTx(tx)
}
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index 1542510a8e..c19a68dcc1 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -561,7 +561,7 @@ func TestFastVsFullChains(t *testing.T) {
Alloc: GenesisAlloc{address: {Balance: funds}},
}
genesis = gspec.MustCommit(gendb)
- signer = types.NewEIP155Signer(gspec.Config.ChainId)
+ signer = types.LatestSigner(gspec.Config)
)
blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, 1024, func(i int, block *BlockGen) {
block.SetCoinbase(common.Address{0x00})
@@ -736,7 +736,7 @@ func TestChainTxReorgs(t *testing.T) {
},
}
genesis = gspec.MustCommit(db)
- signer = types.NewEIP155Signer(gspec.Config.ChainId)
+ signer = types.LatestSigner(gspec.Config)
)
// Create two transactions shared between the chains:
@@ -842,7 +842,7 @@ func TestLogReorgs(t *testing.T) {
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
genesis = gspec.MustCommit(db)
- signer = types.NewEIP155Signer(gspec.Config.ChainId)
+ signer = types.LatestSigner(gspec.Config)
)
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
@@ -889,7 +889,7 @@ func TestLogReorgs(t *testing.T) {
// Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}},
// }
// genesis = gspec.MustCommit(db)
-// signer = types.NewEIP155Signer(gspec.Config.ChainId)
+// signer = types.LatestSigner(gspec.Config)
// )
//
// blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
@@ -1015,7 +1015,7 @@ func TestEIP155Transition(t *testing.T) {
funds = big.NewInt(1000000000)
deleteAddr = common.Address{1}
gspec = &Genesis{
- Config: ¶ms.ChainConfig{ChainId: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)},
+ Config: ¶ms.ChainConfig{ChainId: big.NewInt(1), EIP150Block: big.NewInt(0), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)},
Alloc: GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: new(big.Int)}},
}
genesis = gspec.MustCommit(db)
@@ -1046,7 +1046,7 @@ func TestEIP155Transition(t *testing.T) {
}
block.AddTx(tx)
- tx, err = basicTx(types.NewEIP155Signer(gspec.Config.ChainId))
+ tx, err = basicTx(types.LatestSigner(gspec.Config))
if err != nil {
t.Fatal(err)
}
@@ -1058,7 +1058,7 @@ func TestEIP155Transition(t *testing.T) {
}
block.AddTx(tx)
- tx, err = basicTx(types.NewEIP155Signer(gspec.Config.ChainId))
+ tx, err = basicTx(types.LatestSigner(gspec.Config))
if err != nil {
t.Fatal(err)
}
@@ -1086,7 +1086,7 @@ func TestEIP155Transition(t *testing.T) {
}
// generate an invalid chain id transaction
- config := ¶ms.ChainConfig{ChainId: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
+ config := ¶ms.ChainConfig{ChainId: big.NewInt(2), EIP150Block: big.NewInt(0), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
blocks, _ = GenerateChain(config, blocks[len(blocks)-1], ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
var (
tx *types.Transaction
@@ -1095,9 +1095,8 @@ func TestEIP155Transition(t *testing.T) {
return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key)
}
)
- switch i {
- case 0:
- tx, err = basicTx(types.NewEIP155Signer(big.NewInt(2)))
+ if i == 0 {
+ tx, err = basicTx(types.LatestSigner(config))
if err != nil {
t.Fatal(err)
}
@@ -1136,7 +1135,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
var (
tx *types.Transaction
err error
- signer = types.NewEIP155Signer(gspec.Config.ChainId)
+ signer = types.LatestSigner(gspec.Config)
)
switch i {
case 0:
@@ -1412,3 +1411,93 @@ func TestAreTwoBlocksSamePath(t *testing.T) {
})
}
+
+// TestEIP2718Transition tests that an EIP-2718 transaction will be accepted
+// after the fork block has passed. This is verified by sending an EIP-2930
+// access list transaction, which specifies a single slot access, and then
+// checking that the gas usage of a hot SLOAD and a cold SLOAD are calculated
+// correctly.
+func TestEIP2718Transition(t *testing.T) {
+ var (
+ aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
+
+ // Generate a canonical chain to act as the main dataset
+ engine = ethash.NewFaker()
+ db = rawdb.NewMemoryDatabase()
+
+ // A sender who makes transactions, has some funds
+ key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ address = crypto.PubkeyToAddress(key.PublicKey)
+ funds = big.NewInt(1000000000)
+ gspec = &Genesis{
+ Config: ¶ms.ChainConfig{
+ ChainId: new(big.Int).SetBytes([]byte("eip1559")),
+ HomesteadBlock: big.NewInt(0),
+ DAOForkBlock: nil,
+ DAOForkSupport: true,
+ EIP150Block: big.NewInt(0),
+ EIP155Block: big.NewInt(0),
+ EIP158Block: big.NewInt(0),
+ ByzantiumBlock: big.NewInt(0),
+ ConstantinopleBlock: big.NewInt(0),
+ PetersburgBlock: big.NewInt(0),
+ IstanbulBlock: big.NewInt(0),
+ Eip1559Block: big.NewInt(0),
+ },
+ Alloc: GenesisAlloc{
+ address: {Balance: funds},
+ // The address 0xAAAA sloads 0x00 and 0x01
+ aa: {
+ Code: []byte{
+ byte(vm.PC),
+ byte(vm.PC),
+ byte(vm.SLOAD),
+ byte(vm.SLOAD),
+ },
+ Nonce: 0,
+ Balance: big.NewInt(0),
+ },
+ },
+ }
+ genesis = gspec.MustCommit(db)
+ )
+
+ blocks, _ := GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *BlockGen) {
+ b.SetCoinbase(common.Address{1})
+
+ // One transaction to 0xAAAA
+ signer := types.LatestSigner(gspec.Config)
+ tx, _ := types.SignNewTx(key, signer, &types.AccessListTx{
+ ChainID: gspec.Config.ChainId,
+ Nonce: 0,
+ To: &aa,
+ Gas: 30000,
+ GasPrice: big.NewInt(1),
+ AccessList: types.AccessList{{
+ Address: aa,
+ StorageKeys: []common.Hash{{0}},
+ }},
+ })
+ b.AddTx(tx)
+ })
+
+ // Import the canonical chain
+ diskdb := rawdb.NewMemoryDatabase()
+ gspec.MustCommit(diskdb)
+
+ chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{})
+ if err != nil {
+ t.Fatalf("failed to create tester chain: %v", err)
+ }
+ if n, err := chain.InsertChain(blocks); err != nil {
+ t.Fatalf("block %d: failed to insert into chain: %v", n, err)
+ }
+
+ block := chain.GetBlockByNumber(1)
+
+ // Expected gas is intrinsic + 2 * pc + hot load + cold load, since only one load is in the access list
+ expected := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas + vm.GasQuickStep*2 + vm.WarmStorageReadCostEIP2929 + vm.ColdSloadCostEIP2929
+ if block.GasUsed() != expected {
+ t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed())
+ }
+}
diff --git a/core/database_util_test.go b/core/database_util_test.go
index a38a68fd41..ecd843a3e8 100644
--- a/core/database_util_test.go
+++ b/core/database_util_test.go
@@ -18,11 +18,11 @@ package core
import (
"bytes"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"math/big"
"testing"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/rlp"
@@ -335,6 +335,10 @@ func TestLookupStorage(t *testing.T) {
func TestBlockReceiptStorage(t *testing.T) {
db := rawdb.NewMemoryDatabase()
+ // Create a live block since we need metadata to reconstruct the receipt
+ tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
+ tx2 := types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil)
+
receipt1 := &types.Receipt{
Status: types.ReceiptStatusFailed,
CumulativeGasUsed: 1,
@@ -342,10 +346,12 @@ func TestBlockReceiptStorage(t *testing.T) {
{Address: common.BytesToAddress([]byte{0x11})},
{Address: common.BytesToAddress([]byte{0x01, 0x11})},
},
- TxHash: common.BytesToHash([]byte{0x11, 0x11}),
+ TxHash: tx1.Hash(),
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
GasUsed: 111111,
}
+ receipt1.Bloom = types.CreateBloom(types.Receipts{receipt1})
+
receipt2 := &types.Receipt{
PostState: common.Hash{2}.Bytes(),
CumulativeGasUsed: 2,
@@ -353,10 +359,12 @@ func TestBlockReceiptStorage(t *testing.T) {
{Address: common.BytesToAddress([]byte{0x22})},
{Address: common.BytesToAddress([]byte{0x02, 0x22})},
},
- TxHash: common.BytesToHash([]byte{0x22, 0x22}),
+ TxHash: tx2.Hash(),
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
GasUsed: 222222,
}
+ receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2})
+
receipts := []*types.Receipt{receipt1, receipt2}
// Check that no receipt entries are in a pristine database
diff --git a/core/error.go b/core/error.go
index f6559bf06f..6268c0dc9a 100644
--- a/core/error.go
+++ b/core/error.go
@@ -16,7 +16,11 @@
package core
-import "errors"
+import (
+ "errors"
+
+ "github.com/XinFinOrg/XDPoSChain/core/types"
+)
var (
// ErrKnownBlock is returned when a block to import is already known locally.
@@ -38,4 +42,8 @@ var (
ErrNotFoundM1 = errors.New("list M1 not found ")
ErrStopPreparingBlock = errors.New("stop calculating a block not verified by M2")
+
+ // ErrTxTypeNotSupported is returned if a transaction is not supported in the
+ // current network configuration.
+ ErrTxTypeNotSupported = types.ErrTxTypeNotSupported
)
diff --git a/core/state/statedb.go b/core/state/statedb.go
index e73273a710..15ca9ca67e 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -730,6 +730,32 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
return root, err
}
+// PrepareAccessList handles the preparatory steps for executing a state transition with
+// regards to both EIP-2929 and EIP-2930:
+//
+// - Add sender to access list (2929)
+// - Add destination to access list (2929)
+// - Add precompiles to access list (2929)
+// - Add the contents of the optional tx access list (2930)
+//
+// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number.
+func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
+ s.AddAddressToAccessList(sender)
+ if dst != nil {
+ s.AddAddressToAccessList(*dst)
+ // If it's a create-tx, the destination will be added inside evm.create
+ }
+ for _, addr := range precompiles {
+ s.AddAddressToAccessList(addr)
+ }
+ for _, el := range list {
+ s.AddAddressToAccessList(el.Address)
+ for _, key := range el.StorageKeys {
+ s.AddSlotToAccessList(el.Address, key)
+ }
+ }
+}
+
// AddAddressToAccessList adds the given address to the access list
func (s *StateDB) AddAddressToAccessList(addr common.Address) {
if s.accessList.AddAddress(addr) {
diff --git a/core/state_processor.go b/core/state_processor.go
index e21df09779..cc5697e069 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -242,23 +242,12 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
if err != nil {
return nil, 0, err, false
}
- // Create a new context to be used in the EVM environment
+ // Create a new context to be used in the EVM environment.
context := NewEVMContext(msg, header, bc, author)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmenv := vm.NewEVM(context, statedb, XDCxState, config, cfg)
- if config.IsEIP1559(header.Number) {
- statedb.AddAddressToAccessList(msg.From())
- if dst := msg.To(); dst != nil {
- statedb.AddAddressToAccessList(*dst)
- // If it's a create-tx, the destination will be added inside evm.create
- }
- for _, addr := range vmenv.ActivePrecompiles() {
- statedb.AddAddressToAccessList(addr)
- }
- }
-
// If we don't have an explicit author (i.e. not mining), extract from the header
var beneficiary common.Address
if author == nil {
@@ -419,7 +408,8 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
if err != nil {
return nil, 0, err, false
}
- // Update the state with pending changes
+
+ // Update the state with pending changes.
var root []byte
if config.IsByzantium(header.Number) {
statedb.Finalise(true)
@@ -428,16 +418,23 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
}
*usedGas += gas
- // 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.
- receipt := types.NewReceipt(root, failed, *usedGas)
+ // Create a new receipt for the transaction, storing the intermediate root and gas used
+ // by the tx.
+ receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas}
+ if failed {
+ receipt.Status = types.ReceiptStatusFailed
+ } else {
+ receipt.Status = types.ReceiptStatusSuccessful
+ }
receipt.TxHash = tx.Hash()
receipt.GasUsed = gas
- // if the transaction created a contract, store the creation address in the receipt.
+
+ // If the transaction created a contract, store the creation address in the receipt.
if msg.To() == nil {
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
}
- // Set the receipt logs and create a bloom for filtering
+
+ // Set the receipt logs and create the bloom filter.
receipt.Logs = statedb.GetLogs(tx.Hash())
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
receipt.BlockHash = statedb.BlockHash()
@@ -532,7 +529,6 @@ func InitSignerInTransactions(config *params.ChainConfig, header *types.Header,
go func(from int, to int) {
for j := from; j < to; j++ {
types.CacheSigner(signer, txs[j])
- txs[j].CacheHash()
}
wg.Done()
}(from, to)
diff --git a/core/state_transition.go b/core/state_transition.go
index c9c4fdfefd..7a45e73d7d 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -22,6 +22,7 @@ import (
"math/big"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
@@ -42,8 +43,10 @@ The state transitioning model does all all the necessary work to work out a vali
3) Create a new state object if the recipient is \0*32
4) Value transfer
== If contract creation ==
- 4a) Attempt to run transaction data
- 4b) If valid, use result as code for the new state object
+
+ 4a) Attempt to run transaction data
+ 4b) If valid, use result as code for the new state object
+
== end ==
5) Run Script section
6) Derive new state root
@@ -74,13 +77,14 @@ type Message interface {
CheckNonce() bool
Data() []byte
BalanceTokenFee() *big.Int
+ AccessList() types.AccessList
}
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
-func IntrinsicGas(data []byte, contractCreation, homestead bool) (uint64, error) {
+func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead bool) (uint64, error) {
// Set the starting gas for the raw transaction
var gas uint64
- if contractCreation && homestead {
+ if isContractCreation && isHomestead {
gas = params.TxGasContractCreation
} else {
gas = params.TxGas
@@ -106,6 +110,10 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) (uint64, error)
}
gas += z * params.TxDataZeroGas
}
+ if accessList != nil {
+ gas += uint64(len(accessList)) * params.TxAccessListAddressGas
+ gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas
+ }
return gas, nil
}
@@ -226,7 +234,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
contractCreation := msg.To() == nil
// Pay intrinsic gas
- gas, err := IntrinsicGas(st.data, contractCreation, homestead)
+ gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead)
if err != nil {
return nil, 0, false, err, nil
}
@@ -234,6 +242,11 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
return nil, 0, false, err, nil
}
+ // Set up the initial access list.
+ if st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) {
+ st.state.PrepareAccessList(msg.From(), msg.To(), st.evm.ActivePrecompiles(), msg.AccessList())
+ }
+
var (
evm = st.evm
// vm errors do not effect consensus and are therefor
diff --git a/core/token_validator.go b/core/token_validator.go
index f3ee929163..19f782fce5 100644
--- a/core/token_validator.go
+++ b/core/token_validator.go
@@ -27,6 +27,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/contracts/XDCx/contract"
"github.com/XinFinOrg/XDPoSChain/core/state"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/log"
)
@@ -42,15 +43,16 @@ type callmsg struct {
ethereum.CallMsg
}
-func (m callmsg) From() common.Address { return m.CallMsg.From }
-func (m callmsg) Nonce() uint64 { return 0 }
-func (m callmsg) CheckNonce() bool { return false }
-func (m callmsg) To() *common.Address { return m.CallMsg.To }
-func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
-func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
-func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
-func (m callmsg) Data() []byte { return m.CallMsg.Data }
-func (m callmsg) BalanceTokenFee() *big.Int { return m.CallMsg.BalanceTokenFee }
+func (m callmsg) From() common.Address { return m.CallMsg.From }
+func (m callmsg) Nonce() uint64 { return 0 }
+func (m callmsg) CheckNonce() bool { return false }
+func (m callmsg) To() *common.Address { return m.CallMsg.To }
+func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
+func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
+func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
+func (m callmsg) Data() []byte { return m.CallMsg.Data }
+func (m callmsg) BalanceTokenFee() *big.Int { return m.CallMsg.BalanceTokenFee }
+func (m callmsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
type SimulatedBackend interface {
CallContractWithState(call ethereum.CallMsg, chain consensus.ChainContext, statedb *state.StateDB) ([]byte, error)
diff --git a/core/tx_pool.go b/core/tx_pool.go
index f2d9fc99a2..54a0431c4b 100644
--- a/core/tx_pool.go
+++ b/core/tx_pool.go
@@ -283,6 +283,7 @@ type TxPool struct {
reorgShutdownCh chan struct{} // requests shutdown of scheduleReorgLoop
wg sync.WaitGroup // tracks loop, scheduleReorgLoop
+ eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions.
IsSigner func(address common.Address) bool
trc21FeeCapacity map[common.Address]*big.Int
}
@@ -302,7 +303,7 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
config: config,
chainconfig: chainconfig,
chain: chain,
- signer: types.NewEIP155Signer(chainconfig.ChainId),
+ signer: types.LatestSigner(chainconfig),
pending: make(map[common.Address]*txList),
queue: make(map[common.Address]*txList),
beats: make(map[common.Address]time.Time),
@@ -565,6 +566,10 @@ func (pool *TxPool) GetSender(tx *types.Transaction) (common.Address, error) {
// validateTx checks whether a transaction is valid according to the consensus
// rules and adheres to some heuristic limits of the local node (price and size).
func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
+ // Accept only legacy transactions until EIP-2718/2930 activates.
+ if !pool.eip2718 && tx.Type() != types.LegacyTxType {
+ return ErrTxTypeNotSupported
+ }
// Reject transactions over defined size to prevent DOS attacks
if uint64(tx.Size()) > txMaxSize {
return ErrOversizedData
@@ -586,7 +591,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
if pool.currentMaxGas < tx.Gas() {
return ErrGasLimit
}
- // Make sure the transaction is signed properly
+ // Make sure the transaction is signed properly.
from, err := types.Sender(pool.signer, tx)
if err != nil {
return ErrInvalidSender
@@ -630,7 +635,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
if tx.To() == nil || (tx.To() != nil && !tx.IsSpecialTransaction()) {
// Ensure the transaction has more gas than the basic tx fee.
- intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, true)
+ intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true)
if err != nil {
return err
}
@@ -1346,6 +1351,10 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
log.Debug("Reinjecting stale transactions", "count", len(reinject))
senderCacher.recover(pool.signer, reinject)
pool.addTxsLocked(reinject, false)
+
+ // Update all fork indicator by next pending block number.
+ next := new(big.Int).Add(newHead.Number, big.NewInt(1))
+ pool.eip2718 = pool.chainconfig.IsEIP1559(next)
}
// promoteExecutables moves transactions that have become processable from the
diff --git a/core/types/access_list_tx.go b/core/types/access_list_tx.go
new file mode 100644
index 0000000000..f80044e108
--- /dev/null
+++ b/core/types/access_list_tx.go
@@ -0,0 +1,115 @@
+// Copyright 2020 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 .
+
+package types
+
+import (
+ "math/big"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+)
+
+//go:generate gencodec -type AccessTuple -out gen_access_tuple.go
+
+// AccessList is an EIP-2930 access list.
+type AccessList []AccessTuple
+
+// AccessTuple is the element type of an access list.
+type AccessTuple struct {
+ Address common.Address `json:"address" gencodec:"required"`
+ StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"`
+}
+
+// StorageKeys returns the total number of storage keys in the access list.
+func (al AccessList) StorageKeys() int {
+ sum := 0
+ for _, tuple := range al {
+ sum += len(tuple.StorageKeys)
+ }
+ return sum
+}
+
+// AccessListTx is the data of EIP-2930 access list transactions.
+type AccessListTx struct {
+ ChainID *big.Int // destination chain ID
+ Nonce uint64 // nonce of sender account
+ GasPrice *big.Int // wei per gas
+ Gas uint64 // gas limit
+ To *common.Address `rlp:"nil"` // nil means contract creation
+ Value *big.Int // wei amount
+ Data []byte // contract invocation input data
+ AccessList AccessList // EIP-2930 access list
+ V, R, S *big.Int // signature values
+}
+
+// copy creates a deep copy of the transaction data and initializes all fields.
+func (tx *AccessListTx) copy() TxData {
+ cpy := &AccessListTx{
+ Nonce: tx.Nonce,
+ To: tx.To, // TODO: copy pointed-to address
+ Data: common.CopyBytes(tx.Data),
+ Gas: tx.Gas,
+ // These are copied below.
+ AccessList: make(AccessList, len(tx.AccessList)),
+ Value: new(big.Int),
+ ChainID: new(big.Int),
+ GasPrice: new(big.Int),
+ V: new(big.Int),
+ R: new(big.Int),
+ S: new(big.Int),
+ }
+ copy(cpy.AccessList, tx.AccessList)
+ if tx.Value != nil {
+ cpy.Value.Set(tx.Value)
+ }
+ if tx.ChainID != nil {
+ cpy.ChainID.Set(tx.ChainID)
+ }
+ if tx.GasPrice != nil {
+ cpy.GasPrice.Set(tx.GasPrice)
+ }
+ 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 *AccessListTx) txType() byte { return AccessListTxType }
+func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID }
+func (tx *AccessListTx) protected() bool { return true }
+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) 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) rawSignatureValues() (v, r, s *big.Int) {
+ return tx.V, tx.R, tx.S
+}
+
+func (tx *AccessListTx) setSignatureValues(chainID, v, r, s *big.Int) {
+ tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s
+}
diff --git a/core/types/block.go b/core/types/block.go
index 042db62323..071666a801 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -23,16 +23,13 @@ import (
"io"
"math/big"
"sort"
- "sync"
"sync/atomic"
"time"
"unsafe"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
- "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/rlp"
- "golang.org/x/crypto/sha3"
)
var (
@@ -157,22 +154,6 @@ func (h *Header) Size() common.StorageSize {
return common.StorageSize(unsafe.Sizeof(*h)) + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen()+h.Time.BitLen())/8)
}
-// hasherPool holds LegacyKeccak hashers.
-var hasherPool = sync.Pool{
- New: func() interface{} {
- return sha3.NewLegacyKeccak256()
- },
-}
-
-func rlpHash(x interface{}) (h common.Hash) {
- sha := hasherPool.Get().(crypto.KeccakState)
- defer hasherPool.Put(sha)
- sha.Reset()
- rlp.Encode(sha, x)
- sha.Read(h[:])
- return h
-}
-
// Body is a simple (mutable, non-safe) data container for storing and moving
// a block's data contents (transactions and uncles) together.
type Body struct {
diff --git a/core/types/block_test.go b/core/types/block_test.go
index c95aaae717..3cb50180b9 100644
--- a/core/types/block_test.go
+++ b/core/types/block_test.go
@@ -17,13 +17,18 @@
package types
import (
+ "bytes"
+ "hash"
"math/big"
+ "reflect"
"testing"
- "bytes"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/math"
+ "github.com/XinFinOrg/XDPoSChain/crypto"
+ "github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rlp"
- "reflect"
+ "golang.org/x/crypto/sha3"
)
// from bcValidBlockTest.json, "SimpleTx"
@@ -59,3 +64,152 @@ func TestBlockEncoding(t *testing.T) {
t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc)
}
}
+
+func TestEIP2718BlockEncoding(t *testing.T) {
+ blockEnc := common.FromHex("f9031cf90214a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a0e6e49996c7ec59f7a23d22b83239a60151512c65613bf84a0d7da336399ebc4aa0cafe75574d59780665a97fbfd11365c7545aa8f1abf4e5e12e8243334ef7286bb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000820200832fefd882a410845506eb0796636f6f6c65737420626c6f636b206f6e20636861696ea0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4808080f90101f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1b89e01f89b01800a8301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000001a03dbacc8d0259f2508625e97fdfc57cd85fdd16e5821bc2c10bdd1a52649e8335a0476e10695b183a87b0aa292a7f4b78ef0c3fbe62aa2c42c84e1d9c3da159ef14c0")
+ var block Block
+ if err := rlp.DecodeBytes(blockEnc, &block); err != nil {
+ t.Fatal("decode error: ", err)
+ }
+
+ check := func(f string, got, want interface{}) {
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("%s mismatch: got %v, want %v", f, got, want)
+ }
+ }
+ check("Difficulty", block.Difficulty(), big.NewInt(131072))
+ check("GasLimit", block.GasLimit(), uint64(3141592))
+ check("GasUsed", block.GasUsed(), uint64(42000))
+ check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1"))
+ check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498"))
+ check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
+ check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
+ check("Time", block.Time().Uint64(), uint64(1426516743))
+ check("Size", block.Size(), common.StorageSize(len(blockEnc)))
+
+ // Create legacy tx.
+ to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87")
+ tx1 := NewTx(&LegacyTx{
+ Nonce: 0,
+ To: &to,
+ Value: big.NewInt(10),
+ Gas: 50000,
+ GasPrice: big.NewInt(10),
+ })
+ sig := common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100")
+ tx1, _ = tx1.WithSignature(HomesteadSigner{}, sig)
+
+ // Create ACL tx.
+ addr := common.HexToAddress("0x0000000000000000000000000000000000000001")
+ tx2 := NewTx(&AccessListTx{
+ ChainID: big.NewInt(1),
+ Nonce: 0,
+ To: &to,
+ Gas: 123457,
+ GasPrice: big.NewInt(10),
+ AccessList: AccessList{{Address: addr, StorageKeys: []common.Hash{{0}}}},
+ })
+ sig2 := common.Hex2Bytes("3dbacc8d0259f2508625e97fdfc57cd85fdd16e5821bc2c10bdd1a52649e8335476e10695b183a87b0aa292a7f4b78ef0c3fbe62aa2c42c84e1d9c3da159ef1401")
+ tx2, _ = tx2.WithSignature(NewEIP2930Signer(big.NewInt(1)), sig2)
+
+ check("len(Transactions)", len(block.Transactions()), 2)
+ check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash())
+ check("Transactions[1].Hash", block.Transactions()[1].Hash(), tx2.Hash())
+ check("Transactions[1].Type()", block.Transactions()[1].Type(), uint8(AccessListTxType))
+
+ ourBlockEnc, err := rlp.EncodeToBytes(&block)
+ if err != nil {
+ t.Fatal("encode error: ", err)
+ }
+ if !bytes.Equal(ourBlockEnc, blockEnc) {
+ t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc)
+ }
+}
+
+func TestUncleHash(t *testing.T) {
+ uncles := make([]*Header, 0)
+ h := CalcUncleHash(uncles)
+ exp := common.HexToHash("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")
+ if h != exp {
+ t.Fatalf("empty uncle hash is wrong, got %x != %x", h, exp)
+ }
+}
+
+var benchBuffer = bytes.NewBuffer(make([]byte, 0, 32000))
+
+func BenchmarkEncodeBlock(b *testing.B) {
+ block := makeBenchBlock()
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ benchBuffer.Reset()
+ if err := rlp.Encode(benchBuffer, block); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+// testHasher is the helper tool for transaction/receipt list hashing.
+// The original hasher is trie, in order to get rid of import cycle,
+// use the testing hasher instead.
+type testHasher struct {
+ hasher hash.Hash
+}
+
+func newHasher() *testHasher {
+ return &testHasher{hasher: sha3.NewLegacyKeccak256()}
+}
+
+func (h *testHasher) Reset() {
+ h.hasher.Reset()
+}
+
+func (h *testHasher) Update(key, val []byte) {
+ h.hasher.Write(key)
+ h.hasher.Write(val)
+}
+
+func (h *testHasher) Hash() common.Hash {
+ return common.BytesToHash(h.hasher.Sum(nil))
+}
+
+func makeBenchBlock() *Block {
+ var (
+ key, _ = crypto.GenerateKey()
+ txs = make([]*Transaction, 70)
+ receipts = make([]*Receipt, len(txs))
+ signer = LatestSigner(params.TestChainConfig)
+ uncles = make([]*Header, 3)
+ )
+ header := &Header{
+ Difficulty: math.BigPow(11, 11),
+ Number: math.BigPow(2, 9),
+ GasLimit: 12345678,
+ GasUsed: 1476322,
+ Time: big.NewInt(9876543),
+ Extra: []byte("coolest block on chain"),
+ }
+ for i := range txs {
+ amount := math.BigPow(2, int64(i))
+ price := big.NewInt(300000)
+ data := make([]byte, 100)
+ tx := NewTransaction(uint64(i), common.Address{}, amount, 123457, price, data)
+ signedTx, err := SignTx(tx, signer, key)
+ if err != nil {
+ panic(err)
+ }
+ txs[i] = signedTx
+ receipts[i] = NewReceipt(make([]byte, 32), false, tx.Gas())
+ }
+ for i := range uncles {
+ uncles[i] = &Header{
+ Difficulty: math.BigPow(11, 11),
+ Number: math.BigPow(2, 9),
+ GasLimit: 12345678,
+ GasUsed: 1476322,
+ Time: big.NewInt(9876543),
+ Extra: []byte("benchmark uncle"),
+ }
+ }
+ return NewBlock(header, txs, uncles, receipts)
+}
diff --git a/core/types/gen_access_tuple.go b/core/types/gen_access_tuple.go
new file mode 100644
index 0000000000..d23b32c009
--- /dev/null
+++ b/core/types/gen_access_tuple.go
@@ -0,0 +1,43 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package types
+
+import (
+ "encoding/json"
+ "errors"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+)
+
+// MarshalJSON marshals as JSON.
+func (a AccessTuple) MarshalJSON() ([]byte, error) {
+ type AccessTuple struct {
+ Address common.Address `json:"address" gencodec:"required"`
+ StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"`
+ }
+ var enc AccessTuple
+ enc.Address = a.Address
+ enc.StorageKeys = a.StorageKeys
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+func (a *AccessTuple) UnmarshalJSON(input []byte) error {
+ type AccessTuple struct {
+ Address *common.Address `json:"address" gencodec:"required"`
+ StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"`
+ }
+ var dec AccessTuple
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.Address == nil {
+ return errors.New("missing required field 'address' for AccessTuple")
+ }
+ a.Address = *dec.Address
+ if dec.StorageKeys == nil {
+ return errors.New("missing required field 'storageKeys' for AccessTuple")
+ }
+ a.StorageKeys = dec.StorageKeys
+ return nil
+}
diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go
index ee3e8c7c2a..b72ef0270d 100644
--- a/core/types/gen_receipt_json.go
+++ b/core/types/gen_receipt_json.go
@@ -13,8 +13,10 @@ import (
var _ = (*receiptMarshaling)(nil)
+// MarshalJSON marshals as JSON.
func (r Receipt) MarshalJSON() ([]byte, error) {
type Receipt struct {
+ Type hexutil.Uint64 `json:"type,omitempty"`
PostState hexutil.Bytes `json:"root"`
Status hexutil.Uint `json:"status"`
CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"`
@@ -28,6 +30,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
TransactionIndex hexutil.Uint `json:"transactionIndex"`
}
var enc Receipt
+ enc.Type = hexutil.Uint64(r.Type)
enc.PostState = r.PostState
enc.Status = hexutil.Uint(r.Status)
enc.CumulativeGasUsed = hexutil.Uint64(r.CumulativeGasUsed)
@@ -42,8 +45,10 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
return json.Marshal(&enc)
}
+// UnmarshalJSON unmarshals from JSON.
func (r *Receipt) UnmarshalJSON(input []byte) error {
type Receipt struct {
+ Type *hexutil.Uint64 `json:"type,omitempty"`
PostState *hexutil.Bytes `json:"root"`
Status *hexutil.Uint `json:"status"`
CumulativeGasUsed *hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"`
@@ -60,6 +65,9 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
+ if dec.Type != nil {
+ r.Type = uint8(*dec.Type)
+ }
if dec.PostState != nil {
r.PostState = *dec.PostState
}
diff --git a/core/types/gen_tx_json.go b/core/types/gen_tx_json.go
deleted file mode 100644
index 71f4c7d373..0000000000
--- a/core/types/gen_tx_json.go
+++ /dev/null
@@ -1,101 +0,0 @@
-// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
-
-package types
-
-import (
- "encoding/json"
- "errors"
- "math/big"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/common/hexutil"
-)
-
-var _ = (*txdataMarshaling)(nil)
-
-// MarshalJSON marshals as JSON.
-func (t txdata) MarshalJSON() ([]byte, error) {
- type txdata struct {
- AccountNonce hexutil.Uint64 `json:"nonce" gencodec:"required"`
- Price *hexutil.Big `json:"gasPrice" gencodec:"required"`
- GasLimit hexutil.Uint64 `json:"gas" gencodec:"required"`
- Recipient *common.Address `json:"to" rlp:"nil"`
- Amount *hexutil.Big `json:"value" gencodec:"required"`
- Payload hexutil.Bytes `json:"input" gencodec:"required"`
- V *hexutil.Big `json:"v" gencodec:"required"`
- R *hexutil.Big `json:"r" gencodec:"required"`
- S *hexutil.Big `json:"s" gencodec:"required"`
- Hash *common.Hash `json:"hash" rlp:"-"`
- }
- var enc txdata
- enc.AccountNonce = hexutil.Uint64(t.AccountNonce)
- enc.Price = (*hexutil.Big)(t.Price)
- enc.GasLimit = hexutil.Uint64(t.GasLimit)
- enc.Recipient = t.Recipient
- enc.Amount = (*hexutil.Big)(t.Amount)
- enc.Payload = t.Payload
- enc.V = (*hexutil.Big)(t.V)
- enc.R = (*hexutil.Big)(t.R)
- enc.S = (*hexutil.Big)(t.S)
- enc.Hash = t.Hash
- return json.Marshal(&enc)
-}
-
-// UnmarshalJSON unmarshals from JSON.
-func (t *txdata) UnmarshalJSON(input []byte) error {
- type txdata struct {
- AccountNonce *hexutil.Uint64 `json:"nonce" gencodec:"required"`
- Price *hexutil.Big `json:"gasPrice" gencodec:"required"`
- GasLimit *hexutil.Uint64 `json:"gas" gencodec:"required"`
- Recipient *common.Address `json:"to" rlp:"nil"`
- Amount *hexutil.Big `json:"value" gencodec:"required"`
- Payload *hexutil.Bytes `json:"input" gencodec:"required"`
- V *hexutil.Big `json:"v" gencodec:"required"`
- R *hexutil.Big `json:"r" gencodec:"required"`
- S *hexutil.Big `json:"s" gencodec:"required"`
- Hash *common.Hash `json:"hash" rlp:"-"`
- }
- var dec txdata
- if err := json.Unmarshal(input, &dec); err != nil {
- return err
- }
- if dec.AccountNonce == nil {
- return errors.New("missing required field 'nonce' for txdata")
- }
- t.AccountNonce = uint64(*dec.AccountNonce)
- if dec.Price == nil {
- return errors.New("missing required field 'gasPrice' for txdata")
- }
- t.Price = (*big.Int)(dec.Price)
- if dec.GasLimit == nil {
- return errors.New("missing required field 'gas' for txdata")
- }
- t.GasLimit = uint64(*dec.GasLimit)
- if dec.Recipient != nil {
- t.Recipient = dec.Recipient
- }
- if dec.Amount == nil {
- return errors.New("missing required field 'value' for txdata")
- }
- t.Amount = (*big.Int)(dec.Amount)
- if dec.Payload == nil {
- return errors.New("missing required field 'input' for txdata")
- }
- t.Payload = *dec.Payload
- if dec.V == nil {
- return errors.New("missing required field 'v' for txdata")
- }
- t.V = (*big.Int)(dec.V)
- if dec.R == nil {
- return errors.New("missing required field 'r' for txdata")
- }
- t.R = (*big.Int)(dec.R)
- if dec.S == nil {
- return errors.New("missing required field 's' for txdata")
- }
- t.S = (*big.Int)(dec.S)
- if dec.Hash != nil {
- t.Hash = dec.Hash
- }
- return nil
-}
diff --git a/core/types/hashing.go b/core/types/hashing.go
new file mode 100644
index 0000000000..d712254c4e
--- /dev/null
+++ b/core/types/hashing.go
@@ -0,0 +1,58 @@
+// Copyright 2014 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 .
+
+package types
+
+import (
+ "bytes"
+ "sync"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/crypto"
+ "github.com/XinFinOrg/XDPoSChain/rlp"
+ "golang.org/x/crypto/sha3"
+)
+
+// hasherPool holds LegacyKeccak256 hashers for rlpHash.
+var hasherPool = sync.Pool{
+ New: func() interface{} { return sha3.NewLegacyKeccak256() },
+}
+
+// deriveBufferPool holds temporary encoder buffers for DeriveSha and TX encoding.
+var encodeBufferPool = sync.Pool{
+ New: func() interface{} { return new(bytes.Buffer) },
+}
+
+func rlpHash(x interface{}) (h common.Hash) {
+ sha := hasherPool.Get().(crypto.KeccakState)
+ defer hasherPool.Put(sha)
+ sha.Reset()
+ rlp.Encode(sha, x)
+ sha.Read(h[:])
+ return h
+}
+
+// prefixedRlpHash writes the prefix into the hasher before rlp-encoding the
+// given interface. It's used for typed transactions.
+func prefixedRlpHash(prefix byte, x interface{}) (h common.Hash) {
+ sha := hasherPool.Get().(crypto.KeccakState)
+ defer hasherPool.Put(sha)
+ sha.Reset()
+ sha.Write([]byte{prefix})
+ rlp.Encode(sha, x)
+ sha.Read(h[:])
+ return h
+}
diff --git a/core/types/legacy_tx.go b/core/types/legacy_tx.go
new file mode 100644
index 0000000000..146a1e2877
--- /dev/null
+++ b/core/types/legacy_tx.go
@@ -0,0 +1,111 @@
+// Copyright 2020 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 .
+
+package types
+
+import (
+ "math/big"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+)
+
+// LegacyTx is the transaction data of regular Ethereum transactions.
+type LegacyTx struct {
+ Nonce uint64 // nonce of sender account
+ GasPrice *big.Int // wei per gas
+ Gas uint64 // gas limit
+ To *common.Address `rlp:"nil"` // nil means contract creation
+ Value *big.Int // wei amount
+ Data []byte // contract invocation input data
+ V, R, S *big.Int // signature values
+}
+
+// NewTransaction creates an unsigned legacy transaction.
+// Deprecated: use NewTx instead.
+func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
+ return NewTx(&LegacyTx{
+ Nonce: nonce,
+ To: &to,
+ Value: amount,
+ Gas: gasLimit,
+ GasPrice: gasPrice,
+ Data: data,
+ })
+}
+
+// NewContractCreation creates an unsigned legacy transaction.
+// Deprecated: use NewTx instead.
+func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
+ return NewTx(&LegacyTx{
+ Nonce: nonce,
+ Value: amount,
+ Gas: gasLimit,
+ GasPrice: gasPrice,
+ Data: data,
+ })
+}
+
+// copy creates a deep copy of the transaction data and initializes all fields.
+func (tx *LegacyTx) copy() TxData {
+ cpy := &LegacyTx{
+ Nonce: tx.Nonce,
+ To: tx.To, // TODO: copy pointed-to address
+ Data: common.CopyBytes(tx.Data),
+ Gas: tx.Gas,
+ // These are initialized below.
+ Value: new(big.Int),
+ GasPrice: new(big.Int),
+ V: new(big.Int),
+ R: new(big.Int),
+ S: new(big.Int),
+ }
+ if tx.Value != nil {
+ cpy.Value.Set(tx.Value)
+ }
+ if tx.GasPrice != nil {
+ cpy.GasPrice.Set(tx.GasPrice)
+ }
+ 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 *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) 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) rawSignatureValues() (v, r, s *big.Int) {
+ return tx.V, tx.R, tx.S
+}
+
+func (tx *LegacyTx) setSignatureValues(chainID, v, r, s *big.Int) {
+ tx.V, tx.R, tx.S = v, r, s
+}
diff --git a/core/types/receipt.go b/core/types/receipt.go
index cec362f5a6..51b685a47d 100644
--- a/core/types/receipt.go
+++ b/core/types/receipt.go
@@ -18,6 +18,7 @@ package types
import (
"bytes"
+ "errors"
"fmt"
"io"
"math/big"
@@ -35,6 +36,8 @@ var (
receiptStatusSuccessfulRLP = []byte{0x01}
)
+var errEmptyTypedReceipt = errors.New("empty typed receipt bytes")
+
const (
// ReceiptStatusFailed is the status code of a transaction if execution failed.
ReceiptStatusFailed = uint(0)
@@ -46,6 +49,7 @@ const (
// Receipt represents the results of a transaction.
type Receipt struct {
// Consensus fields: These fields are defined by the Yellow Paper
+ Type uint8 `json:"type,omitempty"`
PostState []byte `json:"root"`
Status uint `json:"status"`
CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"`
@@ -66,6 +70,7 @@ type Receipt struct {
}
type receiptMarshaling struct {
+ Type hexutil.Uint64
PostState hexutil.Bytes
Status hexutil.Uint
CumulativeGasUsed hexutil.Uint64
@@ -82,7 +87,18 @@ type receiptRLP struct {
Logs []*Log
}
-type receiptStorageRLP struct {
+// v4StoredReceiptRLP is the storage encoding of a receipt used in database version 4.
+type v4StoredReceiptRLP struct {
+ PostStateOrStatus []byte
+ CumulativeGasUsed uint64
+ TxHash common.Hash
+ ContractAddress common.Address
+ Logs []*LogForStorage
+ GasUsed uint64
+}
+
+// v3StoredReceiptRLP is the original storage encoding of a receipt including some unnecessary fields.
+type v3StoredReceiptRLP struct {
PostStateOrStatus []byte
CumulativeGasUsed uint64
Bloom Bloom
@@ -93,8 +109,13 @@ type receiptStorageRLP struct {
}
// NewReceipt creates a barebone transaction receipt, copying the init fields.
+// Deprecated: create receipts using a struct literal instead.
func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt {
- r := &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: cumulativeGasUsed}
+ r := &Receipt{
+ Type: LegacyTxType,
+ PostState: common.CopyBytes(root),
+ CumulativeGasUsed: cumulativeGasUsed,
+ }
if failed {
r.Status = ReceiptStatusFailed
} else {
@@ -106,21 +127,65 @@ func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt {
// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt
// into an RLP stream. If no post state is present, byzantium fork is assumed.
func (r *Receipt) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs})
+ data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}
+ if r.Type == LegacyTxType {
+ return rlp.Encode(w, data)
+ }
+ // It's an EIP-2718 typed TX receipt.
+ if r.Type != AccessListTxType {
+ return ErrTxTypeNotSupported
+ }
+ buf := encodeBufferPool.Get().(*bytes.Buffer)
+ defer encodeBufferPool.Put(buf)
+ buf.Reset()
+ buf.WriteByte(r.Type)
+ if err := rlp.Encode(buf, data); err != nil {
+ return err
+ }
+ return rlp.Encode(w, buf.Bytes())
}
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
// from an RLP stream.
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
- var dec receiptRLP
- if err := s.Decode(&dec); err != nil {
+ kind, _, err := s.Kind()
+ switch {
+ case err != nil:
return err
+ case kind == rlp.List:
+ // It's a legacy receipt.
+ var dec receiptRLP
+ if err := s.Decode(&dec); err != nil {
+ return err
+ }
+ r.Type = LegacyTxType
+ return r.setFromRLP(dec)
+ case kind == rlp.String:
+ // It's an EIP-2718 typed tx receipt.
+ b, err := s.Bytes()
+ if err != nil {
+ return err
+ }
+ if len(b) == 0 {
+ return errEmptyTypedReceipt
+ }
+ r.Type = b[0]
+ if r.Type == AccessListTxType {
+ var dec receiptRLP
+ if err := rlp.DecodeBytes(b[1:], &dec); err != nil {
+ return err
+ }
+ return r.setFromRLP(dec)
+ }
+ return ErrTxTypeNotSupported
+ default:
+ return rlp.ErrExpectedList
}
- if err := r.setStatus(dec.PostStateOrStatus); err != nil {
- return err
- }
- r.CumulativeGasUsed, r.Bloom, r.Logs = dec.CumulativeGasUsed, dec.Bloom, dec.Logs
- return nil
+}
+
+func (r *Receipt) setFromRLP(data receiptRLP) error {
+ r.CumulativeGasUsed, r.Bloom, r.Logs = data.CumulativeGasUsed, data.Bloom, data.Logs
+ return r.setStatus(data.PostStateOrStatus)
}
func (r *Receipt) setStatus(postStateOrStatus []byte) error {
@@ -151,7 +216,6 @@ func (r *Receipt) statusEncoding() []byte {
// to approximate and limit the memory consumption of various caches.
func (r *Receipt) Size() common.StorageSize {
size := common.StorageSize(unsafe.Sizeof(*r)) + common.StorageSize(len(r.PostState))
-
size += common.StorageSize(len(r.Logs)) * common.StorageSize(unsafe.Sizeof(Log{}))
for _, log := range r.Logs {
size += common.StorageSize(len(log.Topics)*common.HashLength + len(log.Data))
@@ -174,7 +238,7 @@ type ReceiptForStorage Receipt
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
// into an RLP stream.
func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
- enc := &receiptStorageRLP{
+ enc := &v3StoredReceiptRLP{
PostStateOrStatus: (*Receipt)(r).statusEncoding(),
CumulativeGasUsed: r.CumulativeGasUsed,
Bloom: r.Bloom,
@@ -192,25 +256,64 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation
// fields of a receipt from an RLP stream.
func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
- var dec receiptStorageRLP
- if err := s.Decode(&dec); err != nil {
+ // Retrieve the entire receipt blob as we need to try multiple decoders
+ blob, err := s.Raw()
+ if err != nil {
return err
}
- if err := (*Receipt)(r).setStatus(dec.PostStateOrStatus); err != nil {
+ // Try decoding from the newest format for future proofness, then the older one
+ // for old nodes that just upgraded. V4 was an intermediate unreleased format so
+ // we do need to decode it, but it's not common (try last).
+ if err := decodeV3StoredReceiptRLP(r, blob); err == nil {
+ return nil
+ }
+ return decodeV4StoredReceiptRLP(r, blob)
+}
+
+func decodeV3StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
+ var stored v3StoredReceiptRLP
+ if err := rlp.DecodeBytes(blob, &stored); err != nil {
+ return err
+ }
+ if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil {
return err
}
// Assign the consensus fields
- r.CumulativeGasUsed, r.Bloom = dec.CumulativeGasUsed, dec.Bloom
- r.Logs = make([]*Log, len(dec.Logs))
- for i, log := range dec.Logs {
+ r.CumulativeGasUsed = stored.CumulativeGasUsed
+ r.Bloom = stored.Bloom
+ r.Logs = make([]*Log, len(stored.Logs))
+ for i, log := range stored.Logs {
r.Logs[i] = (*Log)(log)
}
// Assign the implementation fields
- r.TxHash, r.ContractAddress, r.GasUsed = dec.TxHash, dec.ContractAddress, dec.GasUsed
+ r.TxHash = stored.TxHash
+ r.ContractAddress = stored.ContractAddress
+ r.GasUsed = stored.GasUsed
return nil
}
-// Receipts is a wrapper around a Receipt array to implement DerivableList.
+func decodeV4StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
+ var stored v4StoredReceiptRLP
+ if err := rlp.DecodeBytes(blob, &stored); err != nil {
+ return err
+ }
+ if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil {
+ return err
+ }
+ r.CumulativeGasUsed = stored.CumulativeGasUsed
+ r.TxHash = stored.TxHash
+ r.ContractAddress = stored.ContractAddress
+ r.GasUsed = stored.GasUsed
+ r.Logs = make([]*Log, len(stored.Logs))
+ for i, log := range stored.Logs {
+ r.Logs[i] = (*Log)(log)
+ }
+ r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
+
+ return nil
+}
+
+// Receipts implements DerivableList for receipts.
type Receipts []*Receipt
// Len returns the number of receipts in this list.
diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go
new file mode 100644
index 0000000000..82fec06c96
--- /dev/null
+++ b/core/types/receipt_test.go
@@ -0,0 +1,170 @@
+// Copyright 2019 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 .
+
+package types
+
+import (
+ "bytes"
+ "math/big"
+ "reflect"
+ "testing"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/rlp"
+)
+
+func TestDecodeEmptyTypedReceipt(t *testing.T) {
+ input := []byte{0x80}
+ var r Receipt
+ err := rlp.DecodeBytes(input, &r)
+ if err != errEmptyTypedReceipt {
+ t.Fatal("wrong error:", err)
+ }
+}
+
+func TestLegacyReceiptDecoding(t *testing.T) {
+ tests := []struct {
+ name string
+ encode func(*Receipt) ([]byte, error)
+ }{
+ {
+ "V4StoredReceiptRLP",
+ encodeAsV4StoredReceiptRLP,
+ },
+ {
+ "V3StoredReceiptRLP",
+ encodeAsV3StoredReceiptRLP,
+ },
+ }
+
+ tx := NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
+ receipt := &Receipt{
+ Status: ReceiptStatusFailed,
+ CumulativeGasUsed: 1,
+ Logs: []*Log{
+ {
+ Address: common.BytesToAddress([]byte{0x11}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ Data: []byte{0x01, 0x00, 0xff},
+ },
+ {
+ Address: common.BytesToAddress([]byte{0x01, 0x11}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ Data: []byte{0x01, 0x00, 0xff},
+ },
+ },
+ TxHash: tx.Hash(),
+ ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
+ GasUsed: 111111,
+ }
+ receipt.Bloom = CreateBloom(Receipts{receipt})
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ enc, err := tc.encode(receipt)
+ if err != nil {
+ t.Fatalf("Error encoding receipt: %v", err)
+ }
+ var dec ReceiptForStorage
+ if err := rlp.DecodeBytes(enc, &dec); err != nil {
+ t.Fatalf("Error decoding RLP receipt: %v", err)
+ }
+ // Check whether all consensus fields are correct.
+ if dec.Status != receipt.Status {
+ t.Fatalf("Receipt status mismatch, want %v, have %v", receipt.Status, dec.Status)
+ }
+ if dec.CumulativeGasUsed != receipt.CumulativeGasUsed {
+ t.Fatalf("Receipt CumulativeGasUsed mismatch, want %v, have %v", receipt.CumulativeGasUsed, dec.CumulativeGasUsed)
+ }
+ if dec.Bloom != receipt.Bloom {
+ t.Fatalf("Bloom data mismatch, want %v, have %v", receipt.Bloom, dec.Bloom)
+ }
+ if len(dec.Logs) != len(receipt.Logs) {
+ t.Fatalf("Receipt log number mismatch, want %v, have %v", len(receipt.Logs), len(dec.Logs))
+ }
+ for i := 0; i < len(dec.Logs); i++ {
+ if dec.Logs[i].Address != receipt.Logs[i].Address {
+ t.Fatalf("Receipt log %d address mismatch, want %v, have %v", i, receipt.Logs[i].Address, dec.Logs[i].Address)
+ }
+ if !reflect.DeepEqual(dec.Logs[i].Topics, receipt.Logs[i].Topics) {
+ t.Fatalf("Receipt log %d topics mismatch, want %v, have %v", i, receipt.Logs[i].Topics, dec.Logs[i].Topics)
+ }
+ if !bytes.Equal(dec.Logs[i].Data, receipt.Logs[i].Data) {
+ t.Fatalf("Receipt log %d data mismatch, want %v, have %v", i, receipt.Logs[i].Data, dec.Logs[i].Data)
+ }
+ }
+ })
+ }
+}
+
+func encodeAsV4StoredReceiptRLP(want *Receipt) ([]byte, error) {
+ stored := &v4StoredReceiptRLP{
+ PostStateOrStatus: want.statusEncoding(),
+ CumulativeGasUsed: want.CumulativeGasUsed,
+ TxHash: want.TxHash,
+ ContractAddress: want.ContractAddress,
+ Logs: make([]*LogForStorage, len(want.Logs)),
+ GasUsed: want.GasUsed,
+ }
+ for i, log := range want.Logs {
+ stored.Logs[i] = (*LogForStorage)(log)
+ }
+ return rlp.EncodeToBytes(stored)
+}
+
+func encodeAsV3StoredReceiptRLP(want *Receipt) ([]byte, error) {
+ stored := &v3StoredReceiptRLP{
+ PostStateOrStatus: want.statusEncoding(),
+ CumulativeGasUsed: want.CumulativeGasUsed,
+ Bloom: want.Bloom,
+ TxHash: want.TxHash,
+ ContractAddress: want.ContractAddress,
+ Logs: make([]*LogForStorage, len(want.Logs)),
+ GasUsed: want.GasUsed,
+ }
+ for i, log := range want.Logs {
+ stored.Logs[i] = (*LogForStorage)(log)
+ }
+ return rlp.EncodeToBytes(stored)
+}
+
+// TestTypedReceiptEncodingDecoding reproduces a flaw that existed in the receipt
+// rlp decoder, which failed due to a shadowing error.
+func TestTypedReceiptEncodingDecoding(t *testing.T) {
+ var payload = common.FromHex("f9043eb9010c01f90108018262d4b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0b9010c01f901080182cd14b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0b9010d01f901090183013754b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0b9010d01f90109018301a194b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0")
+ check := func(bundle []*Receipt) {
+ t.Helper()
+ for i, receipt := range bundle {
+ if got, want := receipt.Type, uint8(1); got != want {
+ t.Fatalf("bundle %d: got %x, want %x", i, got, want)
+ }
+ }
+ }
+ {
+ var bundle []*Receipt
+ rlp.DecodeBytes(payload, &bundle)
+ check(bundle)
+ }
+ {
+ var bundle []*Receipt
+ r := bytes.NewReader(payload)
+ s := rlp.NewStream(r, uint64(len(payload)))
+ if err := s.Decode(&bundle); err != nil {
+ t.Fatal(err)
+ }
+ check(bundle)
+ }
+}
diff --git a/core/types/transaction.go b/core/types/transaction.go
index f5fbad376a..1bb0275a99 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -24,9 +24,9 @@ import (
"io"
"math/big"
"sync/atomic"
+ "time"
"github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/rlp"
)
@@ -35,6 +35,15 @@ import (
var (
ErrInvalidSig = errors.New("invalid transaction v, r, s values")
+ ErrUnexpectedProtection = errors.New("transaction type does not supported EIP-155 protected signatures")
+ ErrInvalidTxType = errors.New("transaction type not valid in this context")
+ ErrTxTypeNotSupported = errors.New("transaction type not supported")
+ ErrGasFeeCapTooLow = errors.New("fee cap less than base fee")
+ errShortTypedTx = errors.New("typed transaction too short")
+ errInvalidYParity = errors.New("'yParity' field must be 0 or 1")
+ errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match")
+ errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction")
+ errEmptyTypedTx = errors.New("empty typed transaction bytes")
errNoSigner = errors.New("missing signing methods")
skipNonceDestinationAddress = map[string]bool{
common.XDCXAddr: true,
@@ -44,227 +53,318 @@ var (
}
)
-// deriveSigner makes a *best* guess about which signer to use.
-func deriveSigner(V *big.Int) Signer {
- if V.Sign() != 0 && isProtectedV(V) {
- return NewEIP155Signer(deriveChainId(V))
- } else {
- return HomesteadSigner{}
- }
-}
+// Transaction types.
+const (
+ LegacyTxType = iota
+ AccessListTxType
+)
+// Transaction is an Ethereum transaction.
type Transaction struct {
- data txdata
+ inner TxData // Consensus contents of a transaction
+ time time.Time // Time first seen locally (spam avoidance)
+
// caches
hash atomic.Value
size atomic.Value
from atomic.Value
}
-type txdata struct {
- AccountNonce uint64 `json:"nonce" gencodec:"required"`
- Price *big.Int `json:"gasPrice" gencodec:"required"`
- GasLimit uint64 `json:"gas" gencodec:"required"`
- Recipient *common.Address `json:"to" rlp:"nil"` // nil means contract creation
- Amount *big.Int `json:"value" gencodec:"required"`
- Payload []byte `json:"input" gencodec:"required"`
-
- // Signature values
- V *big.Int `json:"v" gencodec:"required"`
- R *big.Int `json:"r" gencodec:"required"`
- S *big.Int `json:"s" gencodec:"required"`
-
- // This is only used when marshaling to JSON.
- Hash *common.Hash `json:"hash" rlp:"-"`
+// NewTx creates a new transaction.
+func NewTx(inner TxData) *Transaction {
+ tx := new(Transaction)
+ tx.setDecoded(inner.copy(), 0)
+ return tx
}
-type txdataMarshaling struct {
- AccountNonce hexutil.Uint64
- Price *hexutil.Big
- GasLimit hexutil.Uint64
- Amount *hexutil.Big
- Payload hexutil.Bytes
- V *hexutil.Big
- R *hexutil.Big
- S *hexutil.Big
+// TxData is the underlying data of a transaction.
+//
+// This is implemented by LegacyTx and AccessListTx.
+type TxData interface {
+ txType() byte // returns the type ID
+ copy() TxData // creates a deep copy and initializes all fields
+
+ chainID() *big.Int
+ accessList() AccessList
+ data() []byte
+ gas() uint64
+ gasPrice() *big.Int
+ value() *big.Int
+ nonce() uint64
+ to() *common.Address
+
+ rawSignatureValues() (v, r, s *big.Int)
+ setSignatureValues(chainID, v, r, s *big.Int)
}
-func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
- return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data)
-}
-
-func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
- return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data)
-}
-
-func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
- if len(data) > 0 {
- data = common.CopyBytes(data)
+// EncodeRLP implements rlp.Encoder
+func (tx *Transaction) EncodeRLP(w io.Writer) error {
+ if tx.Type() == LegacyTxType {
+ return rlp.Encode(w, tx.inner)
}
- d := txdata{
- AccountNonce: nonce,
- Recipient: to,
- Payload: data,
- Amount: new(big.Int),
- GasLimit: gasLimit,
- Price: new(big.Int),
- V: new(big.Int),
- R: new(big.Int),
- S: new(big.Int),
+ // It's an EIP-2718 typed TX envelope.
+ buf := encodeBufferPool.Get().(*bytes.Buffer)
+ defer encodeBufferPool.Put(buf)
+ buf.Reset()
+ if err := tx.encodeTyped(buf); err != nil {
+ return err
}
- if amount != nil {
- d.Amount.Set(amount)
+ return rlp.Encode(w, buf.Bytes())
+}
+
+// encodeTyped writes the canonical encoding of a typed transaction to w.
+func (tx *Transaction) encodeTyped(w *bytes.Buffer) error {
+ w.WriteByte(tx.Type())
+ return rlp.Encode(w, tx.inner)
+}
+
+// MarshalBinary returns the canonical encoding of the transaction.
+// For legacy transactions, it returns the RLP encoding. For EIP-2718 typed
+// transactions, it returns the type and payload.
+func (tx *Transaction) MarshalBinary() ([]byte, error) {
+ if tx.Type() == LegacyTxType {
+ return rlp.EncodeToBytes(tx.inner)
}
- if gasPrice != nil {
- d.Price.Set(gasPrice)
+ var buf bytes.Buffer
+ err := tx.encodeTyped(&buf)
+ return buf.Bytes(), err
+}
+
+// DecodeRLP implements rlp.Decoder
+func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
+ kind, size, err := s.Kind()
+ switch {
+ case err != nil:
+ return err
+ case kind == rlp.List:
+ // It's a legacy transaction.
+ var inner LegacyTx
+ err := s.Decode(&inner)
+ if err == nil {
+ tx.setDecoded(&inner, int(rlp.ListSize(size)))
+ }
+ return err
+ case kind == rlp.String:
+ // It's an EIP-2718 typed TX envelope.
+ var b []byte
+ if b, err = s.Bytes(); err != nil {
+ return err
+ }
+ inner, err := tx.decodeTyped(b)
+ if err == nil {
+ tx.setDecoded(inner, len(b))
+ }
+ return err
+ default:
+ return rlp.ErrExpectedList
+ }
+}
+
+// UnmarshalBinary decodes the canonical encoding of transactions.
+// It supports legacy RLP transactions and EIP2718 typed transactions.
+func (tx *Transaction) UnmarshalBinary(b []byte) error {
+ if len(b) > 0 && b[0] > 0x7f {
+ // It's a legacy transaction.
+ var data LegacyTx
+ err := rlp.DecodeBytes(b, &data)
+ if err != nil {
+ return err
+ }
+ tx.setDecoded(&data, len(b))
+ return nil
+ }
+ // It's an EIP2718 typed transaction envelope.
+ inner, err := tx.decodeTyped(b)
+ if err != nil {
+ return err
+ }
+ tx.setDecoded(inner, len(b))
+ return nil
+}
+
+// decodeTyped decodes a typed transaction from the canonical format.
+func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
+ if len(b) == 0 {
+ return nil, errEmptyTypedTx
+ }
+ switch b[0] {
+ case AccessListTxType:
+ var inner AccessListTx
+ err := rlp.DecodeBytes(b[1:], &inner)
+ return &inner, err
+ default:
+ return nil, ErrTxTypeNotSupported
+ }
+}
+
+// setDecoded sets the inner transaction and size after decoding.
+func (tx *Transaction) setDecoded(inner TxData, size int) {
+ tx.inner = inner
+ tx.time = time.Now()
+ if size > 0 {
+ tx.size.Store(common.StorageSize(size))
+ }
+}
+
+func sanityCheckSignature(v *big.Int, r *big.Int, s *big.Int, maybeProtected bool) error {
+ if isProtectedV(v) && !maybeProtected {
+ return ErrUnexpectedProtection
}
- return &Transaction{data: d}
-}
+ var plainV byte
+ if isProtectedV(v) {
+ chainID := deriveChainId(v).Uint64()
+ plainV = byte(v.Uint64() - 35 - 2*chainID)
+ } else if maybeProtected {
+ // Only EIP-155 signatures can be optionally protected. Since
+ // we determined this v value is not protected, it must be a
+ // raw 27 or 28.
+ plainV = byte(v.Uint64() - 27)
+ } else {
+ // If the signature is not optionally protected, we assume it
+ // must already be equal to the recovery id.
+ plainV = byte(v.Uint64())
+ }
+ if !crypto.ValidateSignatureValues(plainV, r, s, false) {
+ return ErrInvalidSig
+ }
-// ChainId returns which chain id this transaction was signed for (if at all)
-func (tx *Transaction) ChainId() *big.Int {
- return deriveChainId(tx.data.V)
-}
-
-// Protected returns whether the transaction is protected from replay protection.
-func (tx *Transaction) Protected() bool {
- return isProtectedV(tx.data.V)
+ return nil
}
func isProtectedV(V *big.Int) bool {
if V.BitLen() <= 8 {
v := V.Uint64()
- return v != 27 && v != 28
+ return v != 27 && v != 28 && v != 1 && v != 0
}
- // anything not 27 or 28 are considered unprotected
+ // anything not 27 or 28 is considered protected
return true
}
-// EncodeRLP implements rlp.Encoder
-func (tx *Transaction) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, &tx.data)
-}
-
-// DecodeRLP implements rlp.Decoder
-func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
- _, size, _ := s.Kind()
- err := s.Decode(&tx.data)
- if err == nil {
- tx.size.Store(common.StorageSize(rlp.ListSize(size)))
+// Protected says whether the transaction is replay-protected.
+func (tx *Transaction) Protected() bool {
+ switch tx := tx.inner.(type) {
+ case *LegacyTx:
+ return tx.V != nil && isProtectedV(tx.V)
+ default:
+ return true
}
-
- return err
}
-// MarshalJSON encodes the web3 RPC transaction format.
-func (tx *Transaction) MarshalJSON() ([]byte, error) {
- hash := tx.Hash()
- data := tx.data
- data.Hash = &hash
- return data.MarshalJSON()
+// Type returns the transaction type.
+func (tx *Transaction) Type() uint8 {
+ return tx.inner.txType()
}
-// UnmarshalJSON decodes the web3 RPC transaction format.
-func (tx *Transaction) UnmarshalJSON(input []byte) error {
- var dec txdata
- if err := dec.UnmarshalJSON(input); err != nil {
- return err
- }
- var V byte
- if isProtectedV(dec.V) {
- chainID := deriveChainId(dec.V).Uint64()
- V = byte(dec.V.Uint64() - 35 - 2*chainID)
- } else {
- V = byte(dec.V.Uint64() - 27)
- }
- if !crypto.ValidateSignatureValues(V, dec.R, dec.S, false) {
- return ErrInvalidSig
- }
- *tx = Transaction{data: dec}
- return nil
+// ChainId returns the EIP155 chain ID of the transaction. The return value will always be
+// non-nil. For legacy transactions which are not replay-protected, the return value is
+// zero.
+func (tx *Transaction) ChainId() *big.Int {
+ return tx.inner.chainID()
}
-func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
-func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit }
-func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) }
-func (tx *Transaction) GasPriceCmp(other *Transaction) int {
- return tx.data.Price.Cmp(other.data.Price)
-}
-func (tx *Transaction) GasPriceIntCmp(other *big.Int) int {
- return tx.data.Price.Cmp(other)
-}
-func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) }
-func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce }
-func (tx *Transaction) CheckNonce() bool { return true }
+// Data returns the input data of the transaction.
+func (tx *Transaction) Data() []byte { return tx.inner.data() }
+
+// AccessList returns the access list of the transaction.
+func (tx *Transaction) AccessList() AccessList { return tx.inner.accessList() }
+
+// Gas returns the gas limit of the transaction.
+func (tx *Transaction) Gas() uint64 { return tx.inner.gas() }
+
+// GasPrice returns the gas price of the transaction.
+func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.inner.gasPrice()) }
+
+// Value returns the ether amount of the transaction.
+func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) }
+
+// Nonce returns the sender account nonce of the transaction.
+func (tx *Transaction) Nonce() uint64 { return tx.inner.nonce() }
// To returns the recipient address of the transaction.
-// It returns nil if the transaction is a contract creation.
+// For contract-creation transactions, To returns nil.
func (tx *Transaction) To() *common.Address {
- if tx.data.Recipient == nil {
+ // Copy the pointed-to address.
+ ito := tx.inner.to()
+ if ito == nil {
return nil
}
- to := *tx.data.Recipient
- return &to
+ cpy := *ito
+ return &cpy
}
func (tx *Transaction) From() *common.Address {
- if tx.data.V != nil {
- signer := deriveSigner(tx.data.V)
- if f, err := Sender(signer, tx); err != nil {
- return nil
- } else {
- return &f
- }
+ var signer Signer
+ if tx.Protected() {
+ signer = LatestSignerForChainID(tx.ChainId())
} else {
+ signer = HomesteadSigner{}
+ }
+ from, err := Sender(signer, tx)
+ if err != nil {
return nil
}
+ return &from
}
-// Hash hashes the RLP encoding of tx.
-// It uniquely identifies the transaction.
+// RawSignatureValues returns the V, R, S signature values of the transaction.
+// The return values should not be modified by the caller.
+func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) {
+ return tx.inner.rawSignatureValues()
+}
+
+// GasPriceCmp compares the gas prices of two transactions.
+func (tx *Transaction) GasPriceCmp(other *Transaction) int {
+ return tx.inner.gasPrice().Cmp(other.GasPrice())
+}
+
+// GasPriceIntCmp compares the gas price of the transaction against the given price.
+func (tx *Transaction) GasPriceIntCmp(other *big.Int) int {
+ return tx.inner.gasPrice().Cmp(other)
+}
+
+// Hash returns the transaction hash.
func (tx *Transaction) Hash() common.Hash {
if hash := tx.hash.Load(); hash != nil {
return hash.(common.Hash)
}
- v := rlpHash(tx)
- tx.hash.Store(v)
- return v
-}
-func (tx *Transaction) CacheHash() {
- v := rlpHash(tx)
- tx.hash.Store(v)
+ var h common.Hash
+ if tx.Type() == LegacyTxType {
+ h = rlpHash(tx.inner)
+ } else {
+ h = prefixedRlpHash(tx.Type(), tx.inner)
+ }
+ tx.hash.Store(h)
+ return h
}
// Size returns the true RLP encoded storage size of the transaction, either by
-// encoding and returning it, or returning a previsouly cached value.
+// encoding and returning it, or returning a previously cached value.
func (tx *Transaction) Size() common.StorageSize {
if size := tx.size.Load(); size != nil {
return size.(common.StorageSize)
}
c := writeCounter(0)
- rlp.Encode(&c, &tx.data)
+ rlp.Encode(&c, &tx.inner)
tx.size.Store(common.StorageSize(c))
return common.StorageSize(c)
}
// AsMessage returns the transaction as a core.Message.
-//
-// AsMessage requires a signer to derive the sender.
-//
-// XXX Rename message to something less arbitrary?
func (tx *Transaction) AsMessage(s Signer, balanceFee *big.Int, number *big.Int) (Message, error) {
msg := Message{
- nonce: tx.data.AccountNonce,
- gasLimit: tx.data.GasLimit,
- gasPrice: new(big.Int).Set(tx.data.Price),
- to: tx.data.Recipient,
- amount: tx.data.Amount,
- data: tx.data.Payload,
+ nonce: tx.Nonce(),
+ gasLimit: tx.Gas(),
+ gasPrice: new(big.Int).Set(tx.GasPrice()),
+ to: tx.To(),
+ amount: tx.Value(),
+ data: tx.Data(),
+ accessList: tx.AccessList(),
checkNonce: true,
balanceTokenFee: balanceFee,
}
+
var err error
msg.from, err = Sender(s, tx)
if balanceFee != nil {
@@ -280,35 +380,31 @@ func (tx *Transaction) AsMessage(s Signer, balanceFee *big.Int, number *big.Int)
}
// 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 in the [R || S || V] format where V is 0 or 1.
func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
r, s, v, err := signer.SignatureValues(tx, sig)
if err != nil {
return nil, err
}
- cpy := &Transaction{data: tx.data}
- cpy.data.R, cpy.data.S, cpy.data.V = r, s, v
- return cpy, nil
+ cpy := tx.inner.copy()
+ cpy.setSignatureValues(signer.ChainID(), v, r, s)
+ return &Transaction{inner: cpy, time: tx.time}, nil
}
-// Cost returns amount + gasprice * gaslimit.
+// Cost returns gas * gasPrice + value.
func (tx *Transaction) Cost() *big.Int {
- total := new(big.Int).Mul(tx.data.Price, new(big.Int).SetUint64(tx.data.GasLimit))
- total.Add(total, tx.data.Amount)
+ total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas()))
+ total.Add(total, tx.Value())
return total
}
-// Cost returns amount + gasprice * gaslimit.
+// TxCost returns gas * gasPrice + value.
func (tx *Transaction) TxCost(number *big.Int) *big.Int {
- total := new(big.Int).Mul(common.GetGasPrice(number), new(big.Int).SetUint64(tx.data.GasLimit))
- total.Add(total, tx.data.Amount)
+ total := new(big.Int).Mul(common.GetGasPrice(number), new(big.Int).SetUint64(tx.Gas()))
+ total.Add(total, tx.Value())
return total
}
-func (tx *Transaction) RawSignatureValues() (*big.Int, *big.Int, *big.Int) {
- return tx.data.V, tx.data.R, tx.data.S
-}
-
func (tx *Transaction) IsSpecialTransaction() bool {
if tx.To() == nil {
return false
@@ -479,25 +575,24 @@ func (tx *Transaction) IsXDCZApplyTransaction() bool {
func (tx *Transaction) String() string {
var from, to string
- if tx.data.V != nil {
- // make a best guess about the signer and use that to derive
- // the sender.
- signer := deriveSigner(tx.data.V)
- if f, err := Sender(signer, tx); err != nil { // derive but don't cache
- from = "[invalid sender: invalid sig]"
- } else {
- from = fmt.Sprintf("%x", f[:])
- }
+
+ sender := tx.From()
+ if sender != nil {
+ from = fmt.Sprintf("%x", sender[:])
} else {
- from = "[invalid sender: nil V field]"
+ from = "[invalid sender]"
}
- if tx.data.Recipient == nil {
+ receiver := tx.To()
+ if receiver == nil {
to = "[contract creation]"
} else {
- to = fmt.Sprintf("%x", tx.data.Recipient[:])
+ to = fmt.Sprintf("%x", receiver[:])
}
- enc, _ := rlp.EncodeToBytes(&tx.data)
+
+ enc, _ := rlp.EncodeToBytes(tx.Data())
+ v, r, s := tx.RawSignatureValues()
+
return fmt.Sprintf(`
TX(%x)
Contract: %v
@@ -514,17 +609,17 @@ func (tx *Transaction) String() string {
Hex: %x
`,
tx.Hash(),
- tx.data.Recipient == nil,
+ receiver == nil,
from,
to,
- tx.data.AccountNonce,
- tx.data.Price,
- tx.data.GasLimit,
- tx.data.Amount,
- tx.data.Payload,
- tx.data.V,
- tx.data.R,
- tx.data.S,
+ tx.Nonce(),
+ tx.GasPrice(),
+ tx.Gas(),
+ tx.Value(),
+ tx.Data(),
+ v,
+ r,
+ s,
enc,
)
}
@@ -568,40 +663,47 @@ func TxDifference(a, b Transactions) (keep Transactions) {
type TxByNonce Transactions
func (s TxByNonce) Len() int { return len(s) }
-func (s TxByNonce) Less(i, j int) bool { return s[i].data.AccountNonce < s[j].data.AccountNonce }
+func (s TxByNonce) Less(i, j int) bool { return s[i].Nonce() < s[j].Nonce() }
func (s TxByNonce) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-// TxByPrice implements both the sort and the heap interface, making it useful
+// TxByPriceAndTime implements both the sort and the heap interface, making it useful
// for all at once sorting as well as individually adding and removing elements.
-type TxByPrice struct {
+type TxByPriceAndTime struct {
txs Transactions
payersSwap map[common.Address]*big.Int
}
-func (s TxByPrice) Len() int { return len(s.txs) }
-func (s TxByPrice) Less(i, j int) bool {
- i_price := s.txs[i].data.Price
+func (s TxByPriceAndTime) Len() int { return len(s.txs) }
+func (s TxByPriceAndTime) Less(i, j int) bool {
+ i_price := s.txs[i].GasPrice()
if s.txs[i].To() != nil {
if _, ok := s.payersSwap[*s.txs[i].To()]; ok {
i_price = common.TRC21GasPrice
}
}
- j_price := s.txs[j].data.Price
+ j_price := s.txs[j].GasPrice()
if s.txs[j].To() != nil {
if _, ok := s.payersSwap[*s.txs[j].To()]; ok {
j_price = common.TRC21GasPrice
}
}
- return i_price.Cmp(j_price) > 0
-}
-func (s TxByPrice) Swap(i, j int) { s.txs[i], s.txs[j] = s.txs[j], s.txs[i] }
-func (s *TxByPrice) Push(x interface{}) {
+ // If the prices are equal, use the time the transaction was first seen for
+ // deterministic sorting
+ cmp := i_price.Cmp(j_price)
+ if cmp == 0 {
+ return s.txs[i].time.Before(s.txs[j].time)
+ }
+ return cmp > 0
+}
+func (s TxByPriceAndTime) Swap(i, j int) { s.txs[i], s.txs[j] = s.txs[j], s.txs[i] }
+
+func (s *TxByPriceAndTime) Push(x interface{}) {
s.txs = append(s.txs, x.(*Transaction))
}
-func (s *TxByPrice) Pop() interface{} {
+func (s *TxByPriceAndTime) Pop() interface{} {
old := s.txs
n := len(old)
x := old[n-1]
@@ -614,7 +716,7 @@ func (s *TxByPrice) Pop() interface{} {
// entire batches of transactions for non-executable accounts.
type TransactionsByPriceAndNonce struct {
txs map[common.Address]Transactions // Per account nonce-sorted list of transactions
- heads TxByPrice // Next transaction for each unique account (price heap)
+ heads TxByPriceAndTime // Next transaction for each unique account (price heap)
signer Signer // Signer for the set of transactions
}
@@ -623,11 +725,11 @@ type TransactionsByPriceAndNonce struct {
//
// Note, the input map is reowned so the caller should not interact any more with
// if after providing it to the constructor.
-
+//
// It also classifies special txs and normal txs
func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions, signers map[common.Address]struct{}, payersSwap map[common.Address]*big.Int) (*TransactionsByPriceAndNonce, Transactions) {
- // Initialize a price based heap with the head transactions
- heads := TxByPrice{}
+ // Initialize a price and received time based heap with the head transactions
+ heads := TxByPriceAndTime{}
heads.payersSwap = payersSwap
specialTxs := Transactions{}
for _, accTxs := range txs {
@@ -704,11 +806,12 @@ type Message struct {
gasLimit uint64
gasPrice *big.Int
data []byte
+ accessList AccessList
checkNonce bool
balanceTokenFee *big.Int
}
-func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool, balanceTokenFee *big.Int, number *big.Int) Message {
+func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, accessList AccessList, checkNonce bool, balanceTokenFee *big.Int, number *big.Int) Message {
if balanceTokenFee != nil {
gasPrice = common.GetGasPrice(number)
}
@@ -720,6 +823,7 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b
gasLimit: gasLimit,
gasPrice: gasPrice,
data: data,
+ accessList: accessList,
checkNonce: checkNonce,
balanceTokenFee: balanceTokenFee,
}
@@ -734,5 +838,6 @@ func (m Message) Gas() uint64 { return m.gasLimit }
func (m Message) Nonce() uint64 { return m.nonce }
func (m Message) Data() []byte { return m.data }
func (m Message) CheckNonce() bool { return m.checkNonce }
+func (m Message) AccessList() AccessList { return m.accessList }
func (m *Message) SetNonce(nonce uint64) { m.nonce = nonce }
diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go
new file mode 100644
index 0000000000..91403994bf
--- /dev/null
+++ b/core/types/transaction_marshalling.go
@@ -0,0 +1,187 @@
+package types
+
+import (
+ "encoding/json"
+ "errors"
+ "math/big"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/hexutil"
+)
+
+// txJSON is the JSON representation of transactions.
+type txJSON struct {
+ Type hexutil.Uint64 `json:"type"`
+
+ // Common transaction fields:
+ Nonce *hexutil.Uint64 `json:"nonce"`
+ GasPrice *hexutil.Big `json:"gasPrice"`
+ Gas *hexutil.Uint64 `json:"gas"`
+ Value *hexutil.Big `json:"value"`
+ Data *hexutil.Bytes `json:"input"`
+ V *hexutil.Big `json:"v"`
+ R *hexutil.Big `json:"r"`
+ S *hexutil.Big `json:"s"`
+ To *common.Address `json:"to"`
+
+ // Access list transaction fields:
+ ChainID *hexutil.Big `json:"chainId,omitempty"`
+ AccessList *AccessList `json:"accessList,omitempty"`
+
+ // Only used for encoding:
+ Hash common.Hash `json:"hash"`
+}
+
+// MarshalJSON marshals as JSON with a hash.
+func (t *Transaction) MarshalJSON() ([]byte, error) {
+ var enc txJSON
+ // These are set for all tx types.
+ enc.Hash = t.Hash()
+ enc.Type = hexutil.Uint64(t.Type())
+
+ // Other fields are set conditionally depending on tx type.
+ switch tx := t.inner.(type) {
+ case *LegacyTx:
+ enc.Nonce = (*hexutil.Uint64)(&tx.Nonce)
+ enc.Gas = (*hexutil.Uint64)(&tx.Gas)
+ enc.GasPrice = (*hexutil.Big)(tx.GasPrice)
+ enc.Value = (*hexutil.Big)(tx.Value)
+ enc.Data = (*hexutil.Bytes)(&tx.Data)
+ enc.To = t.To()
+ enc.V = (*hexutil.Big)(tx.V)
+ enc.R = (*hexutil.Big)(tx.R)
+ enc.S = (*hexutil.Big)(tx.S)
+ case *AccessListTx:
+ enc.ChainID = (*hexutil.Big)(tx.ChainID)
+ enc.AccessList = &tx.AccessList
+ enc.Nonce = (*hexutil.Uint64)(&tx.Nonce)
+ enc.Gas = (*hexutil.Uint64)(&tx.Gas)
+ enc.GasPrice = (*hexutil.Big)(tx.GasPrice)
+ enc.Value = (*hexutil.Big)(tx.Value)
+ enc.Data = (*hexutil.Bytes)(&tx.Data)
+ enc.To = t.To()
+ enc.V = (*hexutil.Big)(tx.V)
+ enc.R = (*hexutil.Big)(tx.R)
+ enc.S = (*hexutil.Big)(tx.S)
+ }
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+func (t *Transaction) UnmarshalJSON(input []byte) error {
+ var dec txJSON
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+
+ // Decode / verify fields according to transaction type.
+ var inner TxData
+ switch dec.Type {
+ case LegacyTxType:
+ var itx LegacyTx
+ inner = &itx
+ if dec.To != nil {
+ itx.To = dec.To
+ }
+ if dec.Nonce == nil {
+ return errors.New("missing required field 'nonce' in transaction")
+ }
+ itx.Nonce = uint64(*dec.Nonce)
+ if dec.GasPrice == nil {
+ return errors.New("missing required field 'gasPrice' in transaction")
+ }
+ itx.GasPrice = (*big.Int)(dec.GasPrice)
+ if dec.Gas == nil {
+ return errors.New("missing required field 'gas' in transaction")
+ }
+ itx.Gas = uint64(*dec.Gas)
+ if dec.Value == nil {
+ return errors.New("missing required field 'value' in transaction")
+ }
+ itx.Value = (*big.Int)(dec.Value)
+ if dec.Data == nil {
+ return errors.New("missing required field 'input' in transaction")
+ }
+ itx.Data = *dec.Data
+ if dec.V == nil {
+ return errors.New("missing required field 'v' in transaction")
+ }
+ itx.V = (*big.Int)(dec.V)
+ if dec.R == nil {
+ return errors.New("missing required field 'r' in transaction")
+ }
+ itx.R = (*big.Int)(dec.R)
+ if dec.S == nil {
+ return errors.New("missing required field 's' in transaction")
+ }
+ itx.S = (*big.Int)(dec.S)
+ withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0
+ if withSignature {
+ if err := sanityCheckSignature(itx.V, itx.R, itx.S, true); err != nil {
+ return err
+ }
+ }
+
+ case AccessListTxType:
+ var itx AccessListTx
+ inner = &itx
+ // Access list is optional for now.
+ if dec.AccessList != nil {
+ itx.AccessList = *dec.AccessList
+ }
+ if dec.ChainID == nil {
+ return errors.New("missing required field 'chainId' in transaction")
+ }
+ itx.ChainID = (*big.Int)(dec.ChainID)
+ if dec.To != nil {
+ itx.To = dec.To
+ }
+ if dec.Nonce == nil {
+ return errors.New("missing required field 'nonce' in transaction")
+ }
+ itx.Nonce = uint64(*dec.Nonce)
+ if dec.GasPrice == nil {
+ return errors.New("missing required field 'gasPrice' in transaction")
+ }
+ itx.GasPrice = (*big.Int)(dec.GasPrice)
+ if dec.Gas == nil {
+ return errors.New("missing required field 'gas' in transaction")
+ }
+ itx.Gas = uint64(*dec.Gas)
+ if dec.Value == nil {
+ return errors.New("missing required field 'value' in transaction")
+ }
+ itx.Value = (*big.Int)(dec.Value)
+ if dec.Data == nil {
+ return errors.New("missing required field 'input' in transaction")
+ }
+ itx.Data = *dec.Data
+ if dec.V == nil {
+ return errors.New("missing required field 'v' in transaction")
+ }
+ itx.V = (*big.Int)(dec.V)
+ if dec.R == nil {
+ return errors.New("missing required field 'r' in transaction")
+ }
+ itx.R = (*big.Int)(dec.R)
+ if dec.S == nil {
+ return errors.New("missing required field 's' in transaction")
+ }
+ itx.S = (*big.Int)(dec.S)
+ withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0
+ if withSignature {
+ if err := sanityCheckSignature(itx.V, itx.R, itx.S, false); err != nil {
+ return err
+ }
+ }
+
+ default:
+ return ErrTxTypeNotSupported
+ }
+
+ // Now set the inner transaction.
+ t.setDecoded(inner, 0)
+
+ // TODO: check hash here?
+ return nil
+}
diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go
index 3658ed43b9..26c76c05b7 100644
--- a/core/types/transaction_signing.go
+++ b/core/types/transaction_signing.go
@@ -27,9 +27,8 @@ import (
"github.com/XinFinOrg/XDPoSChain/params"
)
-var (
- ErrInvalidChainId = errors.New("invalid chain id for signer")
-)
+var ErrInvalidChainId = errors.New("invalid chain id for signer")
+var ErrInvalidNilTx = errors.New("invalid nil tx")
// sigCache is used to cache the derived sender and contains
// the signer used to derive it.
@@ -42,6 +41,8 @@ type sigCache struct {
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
var signer Signer
switch {
+ case config.IsEIP1559(blockNumber):
+ signer = NewEIP2930Signer(config.ChainId)
case config.IsEIP155(blockNumber):
signer = NewEIP155Signer(config.ChainId)
case config.IsHomestead(blockNumber):
@@ -52,7 +53,40 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
return signer
}
-// SignTx signs the transaction using the given signer and private key
+// LatestSigner returns the 'most permissive' Signer available for the given chain
+// configuration. Specifically, this enables support of EIP-155 replay protection and
+// EIP-2930 access list transactions when their respective forks are scheduled to occur at
+// any block number in the chain config.
+//
+// Use this in transaction-handling code where the current block number is unknown. If you
+// have the current block number available, use MakeSigner instead.
+func LatestSigner(config *params.ChainConfig) Signer {
+ if config.ChainId != nil {
+ if common.Eip1559Block.Uint64() != 9999999999 || config.Eip1559Block != nil {
+ return NewEIP2930Signer(config.ChainId)
+ }
+ if config.EIP155Block != nil {
+ return NewEIP155Signer(config.ChainId)
+ }
+ }
+ return HomesteadSigner{}
+}
+
+// LatestSignerForChainID returns the 'most permissive' Signer available. Specifically,
+// this enables support for EIP-155 replay protection and all implemented EIP-2718
+// transaction types if chainID is non-nil.
+//
+// Use this in transaction-handling code where the current block number and fork
+// configuration are unknown. If you have a ChainConfig, use LatestSigner instead.
+// If you have a ChainConfig and know the current block number, use MakeSigner instead.
+func LatestSignerForChainID(chainID *big.Int) Signer {
+ if chainID == nil {
+ return HomesteadSigner{}
+ }
+ return NewEIP2930Signer(chainID)
+}
+
+// SignTx signs the transaction using the given signer and private key.
func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, error) {
h := s.Hash(tx)
sig, err := crypto.Sign(h[:], prv)
@@ -62,6 +96,27 @@ func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, err
return tx.WithSignature(s, sig)
}
+// SignNewTx creates a transaction and signs it.
+func SignNewTx(prv *ecdsa.PrivateKey, s Signer, txdata TxData) (*Transaction, error) {
+ tx := NewTx(txdata)
+ h := s.Hash(tx)
+ sig, err := crypto.Sign(h[:], prv)
+ if err != nil {
+ return nil, err
+ }
+ return tx.WithSignature(s, sig)
+}
+
+// MustSignNewTx creates a transaction and signs it.
+// This panics if the transaction cannot be signed.
+func MustSignNewTx(prv *ecdsa.PrivateKey, s Signer, txdata TxData) *Transaction {
+ tx, err := SignNewTx(prv, s, txdata)
+ if err != nil {
+ panic(err)
+ }
+ return tx
+}
+
// Sender returns the address derived from the signature (V, R, S) using secp256k1
// elliptic curve and an error if it failed deriving or upon an incorrect
// signature.
@@ -70,6 +125,10 @@ func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, err
// signing method. The cache is invalidated if the cached signer does
// not match the signer used in the current call.
func Sender(signer Signer, tx *Transaction) (common.Address, error) {
+ if tx == nil {
+ return common.Address{}, ErrInvalidNilTx
+ }
+
if sc := tx.from.Load(); sc != nil {
sigCache := sc.(sigCache)
// If the signer used to derive from in a previous
@@ -88,21 +147,128 @@ func Sender(signer Signer, tx *Transaction) (common.Address, error) {
return addr, nil
}
-// Signer encapsulates transaction signature handling. Note that this interface is not a
-// stable API and may change at any time to accommodate new protocol rules.
+// Signer encapsulates transaction signature handling. The name of this type is slightly
+// misleading because Signers don't actually sign, they're just for validating and
+// processing of signatures.
+//
+// Note that this interface is not a stable API and may change at any time to accommodate
+// new protocol rules.
type Signer interface {
// Sender returns the sender address of the transaction.
Sender(tx *Transaction) (common.Address, error)
+
// SignatureValues returns the raw R, S, V values corresponding to the
// given signature.
SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error)
- // Hash returns the hash to be signed.
+ ChainID() *big.Int
+
+ // Hash returns 'signature hash', i.e. the transaction hash that is signed by the
+ // private key. This hash does not uniquely identify the transaction.
Hash(tx *Transaction) common.Hash
+
// Equal returns true if the given signer is the same as the receiver.
Equal(Signer) bool
}
-// EIP155Transaction implements Signer using the EIP155 rules.
+type eip2930Signer struct{ EIP155Signer }
+
+// NewEIP2930Signer returns a signer that accepts EIP-2930 access list transactions,
+// EIP-155 replay protected transactions, and legacy Homestead transactions.
+func NewEIP2930Signer(chainId *big.Int) Signer {
+ return eip2930Signer{NewEIP155Signer(chainId)}
+}
+
+func (s eip2930Signer) ChainID() *big.Int {
+ return s.chainId
+}
+
+func (s eip2930Signer) Equal(s2 Signer) bool {
+ x, ok := s2.(eip2930Signer)
+ return ok && x.chainId.Cmp(s.chainId) == 0
+}
+
+func (s eip2930Signer) Sender(tx *Transaction) (common.Address, error) {
+ V, R, S := tx.RawSignatureValues()
+ switch tx.Type() {
+ case LegacyTxType:
+ if !tx.Protected() {
+ return HomesteadSigner{}.Sender(tx)
+ }
+ V = new(big.Int).Sub(V, s.chainIdMul)
+ V.Sub(V, big8)
+ case AccessListTxType:
+ // ACL 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))
+ default:
+ return common.Address{}, ErrTxTypeNotSupported
+ }
+ if tx.ChainId().Cmp(s.chainId) != 0 {
+ return common.Address{}, ErrInvalidChainId
+ }
+ return recoverPlain(s.Hash(tx), R, S, V, true)
+}
+
+func (s eip2930Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
+ switch txdata := tx.inner.(type) {
+ case *LegacyTx:
+ R, S, V = decodeSignature(sig)
+ if s.chainId.Sign() != 0 {
+ V = big.NewInt(int64(sig[64] + 35))
+ V.Add(V, s.chainIdMul)
+ }
+ case *AccessListTx:
+ // 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.Cmp(s.chainId) != 0 {
+ return nil, nil, nil, ErrInvalidChainId
+ }
+ R, S, _ = decodeSignature(sig)
+ V = big.NewInt(int64(sig[64]))
+ default:
+ return nil, nil, nil, ErrTxTypeNotSupported
+ }
+ return R, S, V, nil
+}
+
+// Hash returns the hash to be signed by the sender.
+// It does not uniquely identify the transaction.
+func (s eip2930Signer) Hash(tx *Transaction) common.Hash {
+ switch tx.Type() {
+ case LegacyTxType:
+ return rlpHash([]interface{}{
+ tx.Nonce(),
+ tx.GasPrice(),
+ tx.Gas(),
+ tx.To(),
+ tx.Value(),
+ tx.Data(),
+ s.chainId, uint(0), uint(0),
+ })
+ case AccessListTxType:
+ return prefixedRlpHash(
+ tx.Type(),
+ []interface{}{
+ s.chainId,
+ tx.Nonce(),
+ tx.GasPrice(),
+ tx.Gas(),
+ tx.To(),
+ tx.Value(),
+ tx.Data(),
+ tx.AccessList(),
+ })
+ default:
+ // This _should_ not happen, but in case someone sends in a bad
+ // json struct via RPC, it's probably more prudent to return an
+ // empty hash instead of killing the node with a panic
+ //panic("Unsupported transaction type: %d", tx.typ)
+ return common.Hash{}
+ }
+}
+
+// EIP155Signer implements Signer using the EIP-155 rules. This accepts transactions which
+// are replay-protected as well as unprotected homestead transactions.
type EIP155Signer struct {
chainId, chainIdMul *big.Int
}
@@ -117,6 +283,10 @@ func NewEIP155Signer(chainId *big.Int) EIP155Signer {
}
}
+func (s EIP155Signer) ChainID() *big.Int {
+ return s.chainId
+}
+
func (s EIP155Signer) Equal(s2 Signer) bool {
eip155, ok := s2.(EIP155Signer)
return ok && eip155.chainId.Cmp(s.chainId) == 0
@@ -125,24 +295,28 @@ func (s EIP155Signer) Equal(s2 Signer) bool {
var big8 = big.NewInt(8)
func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) {
+ if tx.Type() != LegacyTxType {
+ return common.Address{}, ErrTxTypeNotSupported
+ }
if !tx.Protected() {
return HomesteadSigner{}.Sender(tx)
}
if tx.ChainId().Cmp(s.chainId) != 0 {
return common.Address{}, ErrInvalidChainId
}
- V := new(big.Int).Sub(tx.data.V, s.chainIdMul)
+ V, R, S := tx.RawSignatureValues()
+ V = new(big.Int).Sub(V, s.chainIdMul)
V.Sub(V, big8)
- return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true)
+ return recoverPlain(s.Hash(tx), R, S, V, true)
}
-// WithSignature returns a new transaction with the given signature. This signature
+// SignatureValues returns signature values. This signature
// needs to be in the [R || S || V] format where V is 0 or 1.
func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
- R, S, V, err = HomesteadSigner{}.SignatureValues(tx, sig)
- if err != nil {
- return nil, nil, nil, err
+ if tx.Type() != LegacyTxType {
+ return nil, nil, nil, ErrTxTypeNotSupported
}
+ R, S, V = decodeSignature(sig)
if s.chainId.Sign() != 0 {
V = big.NewInt(int64(sig[64] + 35))
V.Add(V, s.chainIdMul)
@@ -154,12 +328,12 @@ func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big
// It does not uniquely identify the transaction.
func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
return rlpHash([]interface{}{
- tx.data.AccountNonce,
- tx.data.Price,
- tx.data.GasLimit,
- tx.data.Recipient,
- tx.data.Amount,
- tx.data.Payload,
+ tx.Nonce(),
+ tx.GasPrice(),
+ tx.Gas(),
+ tx.To(),
+ tx.Value(),
+ tx.Data(),
s.chainId, uint(0), uint(0),
})
}
@@ -168,6 +342,10 @@ func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
// homestead rules.
type HomesteadSigner struct{ FrontierSigner }
+func (s HomesteadSigner) ChainID() *big.Int {
+ return nil
+}
+
func (s HomesteadSigner) Equal(s2 Signer) bool {
_, ok := s2.(HomesteadSigner)
return ok
@@ -180,25 +358,39 @@ func (hs HomesteadSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v
}
func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) {
- return recoverPlain(hs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, true)
+ if tx.Type() != LegacyTxType {
+ return common.Address{}, ErrTxTypeNotSupported
+ }
+ v, r, s := tx.RawSignatureValues()
+ return recoverPlain(hs.Hash(tx), r, s, v, true)
}
type FrontierSigner struct{}
+func (s FrontierSigner) ChainID() *big.Int {
+ return nil
+}
+
func (s FrontierSigner) Equal(s2 Signer) bool {
_, ok := s2.(FrontierSigner)
return ok
}
+func (fs FrontierSigner) Sender(tx *Transaction) (common.Address, error) {
+ if tx.Type() != LegacyTxType {
+ return common.Address{}, ErrTxTypeNotSupported
+ }
+ v, r, s := tx.RawSignatureValues()
+ return recoverPlain(fs.Hash(tx), r, s, v, false)
+}
+
// SignatureValues returns signature values. This signature
// needs to be in the [R || S || V] format where V is 0 or 1.
func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
- if len(sig) != 65 {
- panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
+ if tx.Type() != LegacyTxType {
+ return nil, nil, nil, ErrTxTypeNotSupported
}
- r = new(big.Int).SetBytes(sig[:32])
- s = new(big.Int).SetBytes(sig[32:64])
- v = new(big.Int).SetBytes([]byte{sig[64] + 27})
+ r, s, v = decodeSignature(sig)
return r, s, v, nil
}
@@ -206,17 +398,23 @@ func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *
// It does not uniquely identify the transaction.
func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
return rlpHash([]interface{}{
- tx.data.AccountNonce,
- tx.data.Price,
- tx.data.GasLimit,
- tx.data.Recipient,
- tx.data.Amount,
- tx.data.Payload,
+ tx.Nonce(),
+ tx.GasPrice(),
+ tx.Gas(),
+ tx.To(),
+ tx.Value(),
+ tx.Data(),
})
}
-func (fs FrontierSigner) Sender(tx *Transaction) (common.Address, error) {
- return recoverPlain(fs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, false)
+func decodeSignature(sig []byte) (r, s, v *big.Int) {
+ if len(sig) != crypto.SignatureLength {
+ panic(fmt.Sprintf("wrong size for signature: got %d, want %d", len(sig), crypto.SignatureLength))
+ }
+ r = new(big.Int).SetBytes(sig[:32])
+ s = new(big.Int).SetBytes(sig[32:64])
+ v = new(big.Int).SetBytes([]byte{sig[64] + 27})
+ return r, s, v
}
func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (common.Address, error) {
diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go
index 234ef322c5..500dec7227 100644
--- a/core/types/transaction_test.go
+++ b/core/types/transaction_test.go
@@ -20,8 +20,12 @@ import (
"bytes"
"crypto/ecdsa"
"encoding/json"
+ "errors"
+ "fmt"
"math/big"
+ "reflect"
"testing"
+ "time"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/crypto"
@@ -31,6 +35,8 @@ import (
// The values in those tests are from the Transaction Tests
// at github.com/ethereum/tests.
var (
+ testAddr = common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b")
+
emptyTx = NewTransaction(
0,
common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
@@ -40,7 +46,7 @@ var (
rightvrsTx, _ = NewTransaction(
3,
- common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"),
+ testAddr,
big.NewInt(10),
2000,
big.NewInt(1),
@@ -49,8 +55,32 @@ var (
HomesteadSigner{},
common.Hex2Bytes("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a301"),
)
+
+ emptyEip2718Tx = NewTx(&AccessListTx{
+ ChainID: big.NewInt(1),
+ Nonce: 3,
+ To: &testAddr,
+ Value: big.NewInt(10),
+ Gas: 25000,
+ GasPrice: big.NewInt(1),
+ Data: common.FromHex("5544"),
+ })
+
+ signedEip2718Tx, _ = emptyEip2718Tx.WithSignature(
+ NewEIP2930Signer(big.NewInt(1)),
+ common.Hex2Bytes("c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b266032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d3752101"),
+ )
)
+func TestDecodeEmptyTypedTx(t *testing.T) {
+ input := []byte{0x80}
+ var tx Transaction
+ err := rlp.DecodeBytes(input, &tx)
+ if err != errEmptyTypedTx {
+ t.Fatal("wrong error:", err)
+ }
+}
+
func TestTransactionSigHash(t *testing.T) {
var homestead HomesteadSigner
if homestead.Hash(emptyTx) != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
@@ -72,6 +102,117 @@ func TestTransactionEncode(t *testing.T) {
}
}
+func TestEIP2718TransactionSigHash(t *testing.T) {
+ s := NewEIP2930Signer(big.NewInt(1))
+ if s.Hash(emptyEip2718Tx) != common.HexToHash("49b486f0ec0a60dfbbca2d30cb07c9e8ffb2a2ff41f29a1ab6737475f6ff69f3") {
+ t.Errorf("empty EIP-2718 transaction hash mismatch, got %x", s.Hash(emptyEip2718Tx))
+ }
+ if s.Hash(signedEip2718Tx) != common.HexToHash("49b486f0ec0a60dfbbca2d30cb07c9e8ffb2a2ff41f29a1ab6737475f6ff69f3") {
+ t.Errorf("signed EIP-2718 transaction hash mismatch, got %x", s.Hash(signedEip2718Tx))
+ }
+}
+
+// This test checks signature operations on access list transactions.
+func TestEIP2930Signer(t *testing.T) {
+ var (
+ key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ keyAddr = crypto.PubkeyToAddress(key.PublicKey)
+ signer1 = NewEIP2930Signer(big.NewInt(1))
+ signer2 = NewEIP2930Signer(big.NewInt(2))
+ tx0 = NewTx(&AccessListTx{Nonce: 1})
+ tx1 = NewTx(&AccessListTx{ChainID: big.NewInt(1), Nonce: 1})
+ tx2, _ = SignNewTx(key, signer2, &AccessListTx{ChainID: big.NewInt(2), Nonce: 1})
+ )
+
+ tests := []struct {
+ tx *Transaction
+ signer Signer
+ wantSignerHash common.Hash
+ wantSenderErr error
+ wantSignErr error
+ wantHash common.Hash // after signing
+ }{
+ {
+ tx: tx0,
+ signer: signer1,
+ wantSignerHash: common.HexToHash("846ad7672f2a3a40c1f959cd4a8ad21786d620077084d84c8d7c077714caa139"),
+ wantSenderErr: ErrInvalidChainId,
+ wantHash: common.HexToHash("1ccd12d8bbdb96ea391af49a35ab641e219b2dd638dea375f2bc94dd290f2549"),
+ },
+ {
+ tx: tx1,
+ signer: signer1,
+ wantSenderErr: ErrInvalidSig,
+ wantSignerHash: common.HexToHash("846ad7672f2a3a40c1f959cd4a8ad21786d620077084d84c8d7c077714caa139"),
+ wantHash: common.HexToHash("1ccd12d8bbdb96ea391af49a35ab641e219b2dd638dea375f2bc94dd290f2549"),
+ },
+ {
+ // This checks what happens when trying to sign an unsigned tx for the wrong chain.
+ tx: tx1,
+ signer: signer2,
+ wantSenderErr: ErrInvalidChainId,
+ wantSignerHash: common.HexToHash("367967247499343401261d718ed5aa4c9486583e4d89251afce47f4a33c33362"),
+ wantSignErr: ErrInvalidChainId,
+ },
+ {
+ // This checks what happens when trying to re-sign a signed tx for the wrong chain.
+ tx: tx2,
+ signer: signer1,
+ wantSenderErr: ErrInvalidChainId,
+ wantSignerHash: common.HexToHash("846ad7672f2a3a40c1f959cd4a8ad21786d620077084d84c8d7c077714caa139"),
+ wantSignErr: ErrInvalidChainId,
+ },
+ }
+
+ for i, test := range tests {
+ sigHash := test.signer.Hash(test.tx)
+ if sigHash != test.wantSignerHash {
+ t.Errorf("test %d: wrong sig hash: got %x, want %x", i, sigHash, test.wantSignerHash)
+ }
+ sender, err := Sender(test.signer, test.tx)
+ if !errors.Is(err, test.wantSenderErr) {
+ t.Errorf("test %d: wrong Sender error %q", i, err)
+ }
+ if err == nil && sender != keyAddr {
+ t.Errorf("test %d: wrong sender address %x", i, sender)
+ }
+ signedTx, err := SignTx(test.tx, test.signer, key)
+ if !errors.Is(err, test.wantSignErr) {
+ t.Fatalf("test %d: wrong SignTx error %q", i, err)
+ }
+ if signedTx != nil {
+ if signedTx.Hash() != test.wantHash {
+ t.Errorf("test %d: wrong tx hash after signing: got %x, want %x", i, signedTx.Hash(), test.wantHash)
+ }
+ }
+ }
+}
+
+func TestEIP2718TransactionEncode(t *testing.T) {
+ // RLP representation
+ {
+ have, err := rlp.EncodeToBytes(signedEip2718Tx)
+ if err != nil {
+ t.Fatalf("encode error: %v", err)
+ }
+ want := common.FromHex("b86601f8630103018261a894b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a825544c001a0c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b2660a032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d37521")
+ if !bytes.Equal(have, want) {
+ t.Errorf("encoded RLP mismatch, got %x", have)
+ }
+ }
+ // Binary representation
+ {
+ have, err := signedEip2718Tx.MarshalBinary()
+ if err != nil {
+ t.Fatalf("encode error: %v", err)
+ }
+ want := common.FromHex("01f8630103018261a894b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a825544c001a0c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b2660a032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d37521")
+ if !bytes.Equal(have, want) {
+ t.Errorf("encoded RLP mismatch, got %x", have)
+ }
+ }
+}
+
func decodeTx(data []byte) (*Transaction, error) {
var tx Transaction
t, err := &tx, rlp.Decode(bytes.NewReader(data), &tx)
@@ -233,3 +374,174 @@ func TestTransactionJSON(t *testing.T) {
}
}
}
+
+// Tests that if multiple transactions have the same price, the ones seen earlier
+// are prioritized to avoid network spam attacks aiming for a specific ordering.
+func TestTransactionTimeSort(t *testing.T) {
+ // Generate a batch of accounts to start with
+ keys := make([]*ecdsa.PrivateKey, 5)
+ for i := 0; i < len(keys); i++ {
+ keys[i], _ = crypto.GenerateKey()
+ }
+ signer := HomesteadSigner{}
+
+ // Generate a batch of transactions with overlapping prices, but different creation times
+ groups := map[common.Address]Transactions{}
+ for start, key := range keys {
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+
+ tx, _ := SignTx(NewTransaction(0, common.Address{}, big.NewInt(100), 100, big.NewInt(1), nil), signer, key)
+ tx.time = time.Unix(0, int64(len(keys)-start))
+
+ groups[addr] = append(groups[addr], tx)
+ }
+ // Sort the transactions and cross check the nonce ordering
+ txset, _ := NewTransactionsByPriceAndNonce(signer, groups, nil, map[common.Address]*big.Int{})
+
+ txs := Transactions{}
+ for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
+ txs = append(txs, tx)
+ txset.Shift()
+ }
+ if len(txs) != len(keys) {
+ t.Errorf("expected %d transactions, found %d", len(keys), len(txs))
+ }
+ for i, txi := range txs {
+ fromi, _ := Sender(signer, txi)
+ if i+1 < len(txs) {
+ next := txs[i+1]
+ fromNext, _ := Sender(signer, next)
+
+ if txi.GasPrice().Cmp(next.GasPrice()) < 0 {
+ t.Errorf("invalid gasprice ordering: tx #%d (A=%x P=%v) < tx #%d (A=%x P=%v)", i, fromi[:4], txi.GasPrice(), i+1, fromNext[:4], next.GasPrice())
+ }
+ // Make sure time order is ascending if the txs have the same gas price
+ if txi.GasPrice().Cmp(next.GasPrice()) == 0 && txi.time.After(next.time) {
+ t.Errorf("invalid received time ordering: tx #%d (A=%x T=%v) > tx #%d (A=%x T=%v)", i, fromi[:4], txi.time, i+1, fromNext[:4], next.time)
+ }
+ }
+ }
+}
+
+// TestTransactionCoding tests serializing/de-serializing to/from rlp and JSON.
+func TestTransactionCoding(t *testing.T) {
+ key, err := crypto.GenerateKey()
+ if err != nil {
+ t.Fatalf("could not generate key: %v", err)
+ }
+ var (
+ signer = NewEIP2930Signer(common.Big1)
+ addr = common.HexToAddress("0x0000000000000000000000000000000000000001")
+ recipient = common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87")
+ accesses = AccessList{{Address: addr, StorageKeys: []common.Hash{{0}}}}
+ )
+ for i := uint64(0); i < 500; i++ {
+ var txdata TxData
+ switch i % 5 {
+ case 0:
+ // Legacy tx.
+ txdata = &LegacyTx{
+ Nonce: i,
+ To: &recipient,
+ Gas: 1,
+ GasPrice: big.NewInt(2),
+ Data: []byte("abcdef"),
+ }
+ case 1:
+ // Legacy tx contract creation.
+ txdata = &LegacyTx{
+ Nonce: i,
+ Gas: 1,
+ GasPrice: big.NewInt(2),
+ Data: []byte("abcdef"),
+ }
+ case 2:
+ // Tx with non-zero access list.
+ txdata = &AccessListTx{
+ ChainID: big.NewInt(1),
+ Nonce: i,
+ To: &recipient,
+ Gas: 123457,
+ GasPrice: big.NewInt(10),
+ AccessList: accesses,
+ Data: []byte("abcdef"),
+ }
+ case 3:
+ // Tx with empty access list.
+ txdata = &AccessListTx{
+ ChainID: big.NewInt(1),
+ Nonce: i,
+ To: &recipient,
+ Gas: 123457,
+ GasPrice: big.NewInt(10),
+ Data: []byte("abcdef"),
+ }
+ case 4:
+ // Contract creation with access list.
+ txdata = &AccessListTx{
+ ChainID: big.NewInt(1),
+ Nonce: i,
+ Gas: 123457,
+ GasPrice: big.NewInt(10),
+ AccessList: accesses,
+ }
+ }
+ tx, err := SignNewTx(key, signer, txdata)
+ if err != nil {
+ t.Fatalf("could not sign transaction: %v", err)
+ }
+ // RLP
+ parsedTx, err := encodeDecodeBinary(tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ assertEqual(parsedTx, tx)
+
+ // JSON
+ parsedTx, err = encodeDecodeJSON(tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ assertEqual(parsedTx, tx)
+ }
+}
+
+func encodeDecodeJSON(tx *Transaction) (*Transaction, error) {
+ data, err := json.Marshal(tx)
+ if err != nil {
+ return nil, fmt.Errorf("json encoding failed: %v", err)
+ }
+ var parsedTx = &Transaction{}
+ if err := json.Unmarshal(data, &parsedTx); err != nil {
+ return nil, fmt.Errorf("json decoding failed: %v", err)
+ }
+ return parsedTx, nil
+}
+
+func encodeDecodeBinary(tx *Transaction) (*Transaction, error) {
+ data, err := tx.MarshalBinary()
+ if err != nil {
+ return nil, fmt.Errorf("rlp encoding failed: %v", err)
+ }
+ var parsedTx = &Transaction{}
+ if err := parsedTx.UnmarshalBinary(data); err != nil {
+ return nil, fmt.Errorf("rlp decoding failed: %v", err)
+ }
+ return parsedTx, nil
+}
+
+func assertEqual(orig *Transaction, cpy *Transaction) error {
+ // compare nonce, price, gaslimit, recipient, amount, payload, V, R, S
+ if want, got := orig.Hash(), cpy.Hash(); want != got {
+ return fmt.Errorf("parsed tx differs from original tx, want %v, got %v", want, got)
+ }
+ if want, got := orig.ChainId(), cpy.ChainId(); want.Cmp(got) != 0 {
+ return fmt.Errorf("invalid chain id, want %d, got %d", want, got)
+ }
+ if orig.AccessList() != nil {
+ if !reflect.DeepEqual(orig.AccessList(), cpy.AccessList()) {
+ return fmt.Errorf("access list wrong!")
+ }
+ }
+ return nil
+}
diff --git a/core/vm/interface.go b/core/vm/interface.go
index 2b209c0f09..903a957a20 100644
--- a/core/vm/interface.go
+++ b/core/vm/interface.go
@@ -57,6 +57,7 @@ type StateDB interface {
// is defined according to EIP161 (balance = nonce = code = 0).
Empty(common.Address) bool
+ PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
AddressInAccessList(addr common.Address) bool
SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool)
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index 5c643ee320..bc8c74b43c 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -108,12 +108,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
sender = vm.AccountRef(cfg.Origin)
)
if cfg.ChainConfig.IsEIP1559(vmenv.BlockNumber) {
- cfg.State.AddAddressToAccessList(cfg.Origin)
- cfg.State.AddAddressToAccessList(address)
- for _, addr := range vmenv.ActivePrecompiles() {
- cfg.State.AddAddressToAccessList(addr)
- cfg.State.AddAddressToAccessList(addr)
- }
+ cfg.State.PrepareAccessList(cfg.Origin, &address, vmenv.ActivePrecompiles(), nil)
}
cfg.State.CreateAccount(address)
// set the receiver's (the executing contract) code for execution.
@@ -146,10 +141,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
sender = vm.AccountRef(cfg.Origin)
)
if cfg.ChainConfig.IsEIP1559(vmenv.BlockNumber) {
- cfg.State.AddAddressToAccessList(cfg.Origin)
- for _, addr := range vmenv.ActivePrecompiles() {
- cfg.State.AddAddressToAccessList(addr)
- }
+ cfg.State.PrepareAccessList(cfg.Origin, nil, vmenv.ActivePrecompiles(), nil)
}
// Call the code with the given configuration.
@@ -173,12 +165,9 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
vmenv := NewEnv(cfg)
sender := cfg.State.GetOrNewStateObject(cfg.Origin)
+ statedb := cfg.State
if cfg.ChainConfig.IsEIP1559(vmenv.BlockNumber) {
- cfg.State.AddAddressToAccessList(cfg.Origin)
- cfg.State.AddAddressToAccessList(address)
- for _, addr := range vmenv.ActivePrecompiles() {
- cfg.State.AddAddressToAccessList(addr)
- }
+ statedb.PrepareAccessList(cfg.Origin, &address, vmenv.ActivePrecompiles(), nil)
}
// Call the code with the given configuration.
diff --git a/crypto/crypto.go b/crypto/crypto.go
index 745f9c909f..2872bb098b 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -36,7 +36,7 @@ import (
"golang.org/x/crypto/sha3"
)
-//SignatureLength indicates the byte length required to carry a signature with recovery id.
+// SignatureLength indicates the byte length required to carry a signature with recovery id.
const SignatureLength = 64 + 1 // 64 bytes ECDSA signature + 1 byte recovery id
// RecoveryIDOffset points to the byte offset within the signature that contains the recovery id.
@@ -60,10 +60,23 @@ type KeccakState interface {
Read([]byte) (int, error)
}
+// NewKeccakState creates a new KeccakState
+func NewKeccakState() KeccakState {
+ return sha3.NewLegacyKeccak256().(KeccakState)
+}
+
+// HashData hashes the provided data using the KeccakState and returns a 32 byte hash
+func HashData(kh KeccakState, data []byte) (h common.Hash) {
+ kh.Reset()
+ kh.Write(data)
+ kh.Read(h[:])
+ return h
+}
+
// Keccak256 calculates and returns the Keccak256 hash of the input data.
func Keccak256(data ...[]byte) []byte {
b := make([]byte, 32)
- d := sha3.NewLegacyKeccak256().(KeccakState)
+ d := NewKeccakState()
for _, b := range data {
d.Write(b)
}
@@ -74,7 +87,7 @@ func Keccak256(data ...[]byte) []byte {
// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
// converting it to an internal Hash data structure.
func Keccak256Hash(data ...[]byte) (h common.Hash) {
- d := sha3.NewLegacyKeccak256().(KeccakState)
+ d := NewKeccakState()
for _, b := range data {
d.Write(b)
}
diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go
index 5e32e6b6d8..1a500142c8 100644
--- a/eth/downloader/downloader.go
+++ b/eth/downloader/downloader.go
@@ -980,22 +980,22 @@ func (d *Downloader) fetchReceipts(from uint64) error {
// various callbacks to handle the slight differences between processing them.
//
// The instrumentation parameters:
-// - errCancel: error type to return if the fetch operation is cancelled (mostly makes logging nicer)
-// - deliveryCh: channel from which to retrieve downloaded data packets (merged from all concurrent peers)
-// - deliver: processing callback to deliver data packets into type specific download queues (usually within `queue`)
-// - wakeCh: notification channel for waking the fetcher when new tasks are available (or sync completed)
-// - expire: task callback method to abort requests that took too long and return the faulty peers (traffic shaping)
-// - pending: task callback for the number of requests still needing download (detect completion/non-completability)
-// - inFlight: task callback for the number of in-progress requests (wait for all active downloads to finish)
-// - throttle: task callback to check if the processing queue is full and activate throttling (bound memory use)
-// - reserve: task callback to reserve new download tasks to a particular peer (also signals partial completions)
-// - fetchHook: tester callback to notify of new tasks being initiated (allows testing the scheduling logic)
-// - fetch: network callback to actually send a particular download request to a physical remote peer
-// - cancel: task callback to abort an in-flight download request and allow rescheduling it (in case of lost peer)
-// - capacity: network callback to retrieve the estimated type-specific bandwidth capacity of a peer (traffic shaping)
-// - idle: network callback to retrieve the currently (type specific) idle peers that can be assigned tasks
-// - setIdle: network callback to set a peer back to idle and update its estimated capacity (traffic shaping)
-// - kind: textual label of the type being downloaded to display in log mesages
+// - errCancel: error type to return if the fetch operation is cancelled (mostly makes logging nicer)
+// - deliveryCh: channel from which to retrieve downloaded data packets (merged from all concurrent peers)
+// - deliver: processing callback to deliver data packets into type specific download queues (usually within `queue`)
+// - wakeCh: notification channel for waking the fetcher when new tasks are available (or sync completed)
+// - expire: task callback method to abort requests that took too long and return the faulty peers (traffic shaping)
+// - pending: task callback for the number of requests still needing download (detect completion/non-completability)
+// - inFlight: task callback for the number of in-progress requests (wait for all active downloads to finish)
+// - throttle: task callback to check if the processing queue is full and activate throttling (bound memory use)
+// - reserve: task callback to reserve new download tasks to a particular peer (also signals partial completions)
+// - fetchHook: tester callback to notify of new tasks being initiated (allows testing the scheduling logic)
+// - fetch: network callback to actually send a particular download request to a physical remote peer
+// - cancel: task callback to abort an in-flight download request and allow rescheduling it (in case of lost peer)
+// - capacity: network callback to retrieve the estimated type-specific bandwidth capacity of a peer (traffic shaping)
+// - idle: network callback to retrieve the currently (type specific) idle peers that can be assigned tasks
+// - setIdle: network callback to set a peer back to idle and update its estimated capacity (traffic shaping)
+// - kind: textual label of the type being downloaded to display in log mesages
func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliver func(dataPack) (int, error), wakeCh chan bool,
expire func() map[string]int, pending func() int, inFlight func() bool, throttle func() bool, reserve func(*peerConnection, int) (*fetchRequest, bool, error),
fetchHook func([]*types.Header), fetch func(*peerConnection, *fetchRequest) error, cancel func(*fetchRequest), capacity func(*peerConnection) int,
@@ -1036,7 +1036,7 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
case err == nil:
peer.log.Trace("Delivered new batch of data", "type", kind, "count", packet.Stats())
default:
- peer.log.Trace("Failed to deliver retrieved data", "type", kind, "err", err)
+ peer.log.Debug("Failed to deliver retrieved data", "type", kind, "err", err)
}
}
// Blocks assembled, try to update the progress
diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go
index 745f5656a6..af8ff441b3 100644
--- a/ethclient/ethclient.go
+++ b/ethclient/ethclient.go
@@ -479,7 +479,7 @@ func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64
// If the transaction was a contract creation use the TransactionReceipt method to get the
// contract address after the transaction has been mined.
func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) error {
- data, err := rlp.EncodeToBytes(tx)
+ data, err := tx.MarshalBinary()
if err != nil {
return err
}
diff --git a/ethclient/signer.go b/ethclient/signer.go
index d41bd379f4..1db03a9c95 100644
--- a/ethclient/signer.go
+++ b/ethclient/signer.go
@@ -51,9 +51,14 @@ func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error)
return s.addr, nil
}
+func (s *senderFromServer) ChainID() *big.Int {
+ panic("can't sign with senderFromServer")
+}
+
func (s *senderFromServer) Hash(tx *types.Transaction) common.Hash {
panic("can't sign with senderFromServer")
}
+
func (s *senderFromServer) SignatureValues(tx *types.Transaction, sig []byte) (R, S, V *big.Int, err error) {
panic("can't sign with senderFromServer")
}
diff --git a/interfaces.go b/interfaces.go
index 88cf2fcb0c..dfd96b217a 100644
--- a/interfaces.go
+++ b/interfaces.go
@@ -120,6 +120,7 @@ type CallMsg struct {
Value *big.Int // amount of wei sent along with the call
Data []byte // input data, usually an ABI-encoded contract method invocation
BalanceTokenFee *big.Int
+ AccessList types.AccessList // EIP-2930 access list.
}
// A ContractCaller provides contract calls, essentially transactions that are executed by
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index f2f7f01fbe..e8f62d1a42 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -393,7 +393,7 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
if err != nil {
return common.Hash{}, err
}
- return submitTransaction(ctx, s.b, signed)
+ return SubmitTransaction(ctx, s.b, signed)
}
// SignTransaction will create a transaction from the given arguments and
@@ -414,9 +414,10 @@ func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args SendTxArgs
}
signed, err := s.signTransaction(ctx, args, passwd)
if err != nil {
+ log.Warn("Failed transaction sign attempt", "from", args.From, "to", args.To, "value", args.Value.ToInt(), "err", err)
return nil, err
}
- data, err := rlp.EncodeToBytes(signed)
+ data, err := signed.MarshalBinary()
if err != nil {
return nil, err
}
@@ -1155,12 +1156,68 @@ func (s *PublicBlockChainAPI) getCandidatesFromSmartContract() ([]utils.Masterno
// CallArgs represents the arguments for a call.
type CallArgs struct {
- From common.Address `json:"from"`
- To *common.Address `json:"to"`
- Gas hexutil.Uint64 `json:"gas"`
- GasPrice hexutil.Big `json:"gasPrice"`
- Value hexutil.Big `json:"value"`
- Data hexutil.Bytes `json:"data"`
+ From *common.Address `json:"from"`
+ To *common.Address `json:"to"`
+ Gas *hexutil.Uint64 `json:"gas"`
+ GasPrice *hexutil.Big `json:"gasPrice"`
+ Value *hexutil.Big `json:"value"`
+ Data *hexutil.Bytes `json:"data"`
+ AccessList *types.AccessList `json:"accessList"`
+}
+
+// ToMessage converts CallArgs to the Message type used by the core evm
+// TODO: set balanceTokenFee
+func (args *CallArgs) ToMessage(b Backend, number *big.Int) types.Message {
+ // Set sender address or use a default if none specified
+ var addr common.Address
+ if args.From == nil || *args.From == (common.Address{}) {
+ if wallets := b.AccountManager().Wallets(); len(wallets) > 0 {
+ if accounts := wallets[0].Accounts(); len(accounts) > 0 {
+ addr = accounts[0].Address
+ }
+ }
+ } else {
+ addr = *args.From
+ }
+
+ // Set default gas & gas price if none were set
+ var gas uint64
+ if args.Gas != nil {
+ gas = *(*uint64)(args.Gas)
+ }
+ if gas == 0 {
+ gas = math.MaxUint64 / 2
+ }
+
+ gasPrice := new(big.Int)
+ if args.GasPrice != nil {
+ gasPrice = args.GasPrice.ToInt()
+ }
+ if gasPrice.Sign() <= 0 {
+ gasPrice = new(big.Int).SetUint64(defaultGasPrice)
+ }
+
+ value := new(big.Int)
+ if args.Value != nil {
+ value = args.Value.ToInt()
+ }
+
+ var data []byte
+ if args.Data != nil {
+ data = *args.Data
+ }
+
+ var accessList types.AccessList
+ if args.AccessList != nil {
+ accessList = *args.AccessList
+ }
+
+ balanceTokenFee := big.NewInt(0).SetUint64(gas)
+ balanceTokenFee = balanceTokenFee.Mul(balanceTokenFee, gasPrice)
+
+ // Create new call message
+ msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, accessList, false, balanceTokenFee, number)
+ return msg
}
func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, vmCfg vm.Config, timeout time.Duration) ([]byte, uint64, bool, error, error) {
@@ -1173,27 +1230,8 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo
if err := overrides.Apply(statedb); err != nil {
return nil, 0, false, err, nil
}
- // Set sender address or use a default if none specified
- addr := args.From
- if addr == (common.Address{}) {
- if wallets := b.AccountManager().Wallets(); len(wallets) > 0 {
- if accounts := wallets[0].Accounts(); len(accounts) > 0 {
- addr = accounts[0].Address
- }
- }
- }
- // Set default gas & gas price if none were set
- gas, gasPrice := uint64(args.Gas), args.GasPrice.ToInt()
- if gas == 0 {
- gas = math.MaxUint64 / 2
- }
- if gasPrice.Sign() == 0 {
- gasPrice = new(big.Int).SetUint64(defaultGasPrice)
- }
- balanceTokenFee := big.NewInt(0).SetUint64(gas)
- balanceTokenFee = balanceTokenFee.Mul(balanceTokenFee, gasPrice)
- // Create new call message
- msg := types.NewMessage(addr, args.To, 0, args.Value.ToInt(), gas, gasPrice, args.Data, false, balanceTokenFee, header.Number)
+
+ msg := args.ToMessage(b, header.Number)
// Setup context so it may be cancelled the call has completed
// or, in case of unmetered gas, setup a context with a timeout.
@@ -1219,6 +1257,7 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo
if err != nil {
return nil, 0, false, err, nil
}
+
// Get a new instance of the EVM.
evm, vmError, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, vmCfg)
if err != nil {
@@ -1231,8 +1270,7 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo
evm.Cancel()
}()
- // Setup the gas pool (also for unmetered requests)
- // and apply the message.
+ // Execute the message.
gp := new(core.GasPool).AddGas(math.MaxUint64)
owner := common.Address{}
res, gas, failed, err, vmErr := core.ApplyMessage(evm, msg, gp, owner)
@@ -1314,21 +1352,29 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNrOrHash
hi uint64
cap uint64
)
- if uint64(args.Gas) >= params.TxGas {
- hi = uint64(args.Gas)
+ // Use zero address if sender unspecified.
+ if args.From == nil {
+ args.From = new(common.Address)
+ }
+ // Determine the highest gas limit can be used during the estimation.
+ if args.Gas != nil && uint64(*args.Gas) >= params.TxGas {
+ hi = uint64(*args.Gas)
} else {
// Retrieve the current pending block to act as the gas ceiling
block, err := b.BlockByNumberOrHash(ctx, blockNrOrHash)
if err != nil {
return 0, err
}
+ if block == nil {
+ return 0, errors.New("block not found")
+ }
hi = block.GasLimit()
}
cap = hi
// Create a helper to check if a gas allowance results in an executable transaction
executable := func(gas uint64) (bool, []byte, error, error) {
- args.Gas = hexutil.Uint64(gas)
+ args.Gas = (*hexutil.Uint64)(&gas)
res, _, failed, err, vmErr := DoCall(ctx, b, args, blockNrOrHash, nil, vm.Config{}, 0)
if err != nil {
@@ -1348,8 +1394,8 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNrOrHash
// directly try 21000. Returning 21000 without any execution is dangerous as
// some tx field combos might bump the price up even for plain transfers (e.g.
// unused access list items). Ever so slightly wasteful, but safer overall.
- if len(args.Data) == 0 && args.To != nil {
- if state.GetCodeSize(*args.To) == 0 {
+ if args.Data == nil || len(*args.Data) == 0 {
+ if args.To != nil && state.GetCodeSize(*args.To) == 0 {
ok, _, err, _ := executable(params.TxGas)
if ok && err == nil {
return hexutil.Uint64(params.TxGas), nil
@@ -1679,33 +1725,43 @@ func (s *PublicBlockChainAPI) rpcOutputBlockSigners(b *types.Block, ctx context.
// RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction
type RPCTransaction struct {
- BlockHash common.Hash `json:"blockHash"`
- BlockNumber *hexutil.Big `json:"blockNumber"`
- From common.Address `json:"from"`
- Gas hexutil.Uint64 `json:"gas"`
- GasPrice *hexutil.Big `json:"gasPrice"`
- Hash common.Hash `json:"hash"`
- Input hexutil.Bytes `json:"input"`
- Nonce hexutil.Uint64 `json:"nonce"`
- To *common.Address `json:"to"`
- TransactionIndex hexutil.Uint `json:"transactionIndex"`
- Value *hexutil.Big `json:"value"`
- V *hexutil.Big `json:"v"`
- R *hexutil.Big `json:"r"`
- S *hexutil.Big `json:"s"`
+ BlockHash *common.Hash `json:"blockHash"`
+ BlockNumber *hexutil.Big `json:"blockNumber"`
+ From common.Address `json:"from"`
+ Gas hexutil.Uint64 `json:"gas"`
+ GasPrice *hexutil.Big `json:"gasPrice"`
+ Hash common.Hash `json:"hash"`
+ Input hexutil.Bytes `json:"input"`
+ Nonce hexutil.Uint64 `json:"nonce"`
+ To *common.Address `json:"to"`
+ TransactionIndex *hexutil.Uint64 `json:"transactionIndex"`
+ Value *hexutil.Big `json:"value"`
+ Type hexutil.Uint64 `json:"type"`
+ Accesses *types.AccessList `json:"accessList,omitempty"`
+ ChainID *hexutil.Big `json:"chainId,omitempty"`
+ V *hexutil.Big `json:"v"`
+ R *hexutil.Big `json:"r"`
+ S *hexutil.Big `json:"s"`
}
// newRPCTransaction returns a transaction that will serialize to the RPC
// representation, with the given location metadata set (if available).
func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) *RPCTransaction {
- var signer types.Signer = types.FrontierSigner{}
+ // Determine the signer. For replay-protected transactions, use the most permissive
+ // signer, because we assume that signers are backwards-compatible with old
+ // transactions. For non-protected transactions, the homestead signer signer is used
+ // because the return value of ChainId is zero for those transactions.
+ var signer types.Signer
if tx.Protected() {
- signer = types.NewEIP155Signer(tx.ChainId())
+ signer = types.LatestSignerForChainID(tx.ChainId())
+ } else {
+ signer = types.HomesteadSigner{}
}
+
from, _ := types.Sender(signer, tx)
v, r, s := tx.RawSignatureValues()
-
result := &RPCTransaction{
+ Type: hexutil.Uint64(tx.Type()),
From: from,
Gas: hexutil.Uint64(tx.Gas()),
GasPrice: (*hexutil.Big)(tx.GasPrice()),
@@ -1719,9 +1775,14 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
S: (*hexutil.Big)(s),
}
if blockHash != (common.Hash{}) {
- result.BlockHash = blockHash
+ result.BlockHash = &blockHash
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
- result.TransactionIndex = hexutil.Uint(index)
+ result.TransactionIndex = (*hexutil.Uint64)(&index)
+ }
+ if tx.Type() == types.AccessListTxType {
+ al := tx.AccessList()
+ result.Accesses = &al
+ result.ChainID = (*hexutil.Big)(tx.ChainId())
}
return result
}
@@ -1746,7 +1807,7 @@ func newRPCRawTransactionFromBlockIndex(b *types.Block, index uint64) hexutil.By
if index >= uint64(len(txs)) {
return nil
}
- blob, _ := rlp.EncodeToBytes(txs[index])
+ blob, _ := txs[index].MarshalBinary()
return blob
}
@@ -1764,6 +1825,7 @@ func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransa
type PublicTransactionPoolAPI struct {
b Backend
nonceLock *AddrLocker
+ signer types.Signer
}
// PublicTransactionPoolAPI exposes methods for the RPC interface
@@ -1774,7 +1836,10 @@ type PublicXDCXTransactionPoolAPI struct {
// NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool.
func NewPublicTransactionPoolAPI(b Backend, nonceLock *AddrLocker) *PublicTransactionPoolAPI {
- return &PublicTransactionPoolAPI{b, nonceLock}
+ // The signer used by the API should always be the 'latest' known one because we expect
+ // signers to be backwards-compatible with old transactions.
+ signer := types.LatestSigner(b.ChainConfig())
+ return &PublicTransactionPoolAPI{b, nonceLock, signer}
}
// NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool.
@@ -1867,17 +1932,16 @@ func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, has
// GetRawTransactionByHash returns the bytes of the transaction for the given hash.
func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
- var tx *types.Transaction
-
// Retrieve a finalized transaction, or a pooled otherwise
- if tx, _, _, _ = core.GetTransaction(s.b.ChainDb(), hash); tx == nil {
+ tx, _, _, _ := core.GetTransaction(s.b.ChainDb(), hash)
+ if tx == nil {
if tx = s.b.GetPoolTransaction(hash); tx == nil {
// Transaction not found anywhere, abort
return nil, nil
}
}
// Serialize to RLP and return
- return rlp.EncodeToBytes(tx)
+ return tx.MarshalBinary()
}
// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
@@ -1895,10 +1959,9 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
}
receipt := receipts[index]
- var signer types.Signer = types.FrontierSigner{}
- if tx.Protected() {
- signer = types.NewEIP155Signer(tx.ChainId())
- }
+ // Derive the sender.
+ bigblock := new(big.Int).SetUint64(blockNumber)
+ signer := types.MakeSigner(s.b.ChainConfig(), bigblock)
from, _ := types.Sender(signer, tx)
fields := map[string]interface{}{
@@ -1913,6 +1976,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
"contractAddress": nil,
"logs": receipt.Logs,
"logsBloom": receipt.Bloom,
+ "type": hexutil.Uint(tx.Type()),
}
// Assign receipt status or post state.
@@ -1960,14 +2024,14 @@ type SendTxArgs struct {
// newer name and should be preferred by clients.
Data *hexutil.Bytes `json:"data"`
Input *hexutil.Bytes `json:"input"`
+
+ // For non-legacy transactions
+ AccessList *types.AccessList `json:"accessList,omitempty"`
+ ChainID *hexutil.Big `json:"chainId,omitempty"`
}
-// setDefaults is a helper function that fills in default values for unspecified tx fields.
+// setDefaults fills in default values for unspecified tx fields.
func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
- if args.Gas == nil {
- args.Gas = new(hexutil.Uint64)
- *(*uint64)(args.Gas) = 90000
- }
if args.GasPrice == nil {
price, err := b.SuggestPrice(ctx)
if err != nil {
@@ -2000,45 +2064,100 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
return errors.New(`contract creation without any data provided`)
}
}
+
+ // Estimate the gas usage if necessary.
+ if args.Gas == nil {
+ // For backwards-compatibility reason, we try both input and data
+ // but input is preferred.
+ input := args.Input
+ if input == nil {
+ input = args.Data
+ }
+ callArgs := CallArgs{
+ From: &args.From, // From shouldn't be nil
+ To: args.To,
+ GasPrice: args.GasPrice,
+ Value: args.Value,
+ Data: input,
+ AccessList: args.AccessList,
+ }
+ pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
+ estimated, err := DoEstimateGas(ctx, b, callArgs, pendingBlockNr, nil)
+ if err != nil {
+ return err
+ }
+ args.Gas = &estimated
+ log.Trace("Estimate gas usage automatically", "gas", args.Gas)
+
+ }
+ if args.ChainID == nil {
+ id := (*hexutil.Big)(b.ChainConfig().ChainId)
+ args.ChainID = id
+ }
return nil
}
+// toTransaction converts the arguments to a transaction.
+// This assumes that setDefaults has been called.
func (args *SendTxArgs) toTransaction() *types.Transaction {
var input []byte
- if args.Data != nil {
- input = *args.Data
- } else if args.Input != nil {
+ if args.Input != nil {
input = *args.Input
+ } else if args.Data != nil {
+ input = *args.Data
}
- if args.To == nil {
- return types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
+
+ var data types.TxData
+ if args.AccessList == nil {
+ data = &types.LegacyTx{
+ To: args.To,
+ Nonce: uint64(*args.Nonce),
+ Gas: uint64(*args.Gas),
+ GasPrice: (*big.Int)(args.GasPrice),
+ Value: (*big.Int)(args.Value),
+ Data: input,
+ }
+ } else {
+ data = &types.AccessListTx{
+ To: args.To,
+ ChainID: (*big.Int)(args.ChainID),
+ Nonce: uint64(*args.Nonce),
+ Gas: uint64(*args.Gas),
+ GasPrice: (*big.Int)(args.GasPrice),
+ Value: (*big.Int)(args.Value),
+ Data: input,
+ AccessList: *args.AccessList,
+ }
}
- return types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
+ return types.NewTx(data)
}
-// submitTransaction is a helper function that submits tx to txPool and logs a message.
-func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) {
+// SubmitTransaction is a helper function that submits tx to txPool and logs a message.
+func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) {
if tx.To() != nil && tx.IsSpecialTransaction() {
return common.Hash{}, errors.New("Dont allow transaction sent to BlockSigners & RandomizeSMC smart contract via API")
}
if err := b.SendTx(ctx, tx); err != nil {
return common.Hash{}, err
}
+
+ // Print a log with full tx details for manual investigations and interventions
+ signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number())
+ from, err := types.Sender(signer, tx)
+ if err != nil {
+ return common.Hash{}, err
+ }
+
if tx.To() == nil {
- signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number())
- from, err := types.Sender(signer, tx)
- if err != nil {
- return common.Hash{}, err
- }
addr := crypto.CreateAddress(from, tx.Nonce())
- log.Trace("Submitted contract creation", "fullhash", tx.Hash().Hex(), "contract", addr.Hex())
+ log.Info("Submitted contract creation", "hash", tx.Hash().Hex(), "from", from, "nonce", tx.Nonce(), "contract", addr.Hex(), "value", tx.Value())
} else {
- log.Trace("Submitted transaction", "fullhash", tx.Hash().Hex(), "recipient", tx.To())
+ log.Info("Submitted transaction", "hash", tx.Hash().Hex(), "from", from, "nonce", tx.Nonce(), "recipient", tx.To(), "value", tx.Value())
}
return tx.Hash(), nil
}
-// submitTransaction is a helper function that submits tx to txPool and logs a message.
+// SubmitTransaction is a helper function that submits tx to txPool and logs a message.
func submitOrderTransaction(ctx context.Context, b Backend, tx *types.OrderTransaction) (common.Hash, error) {
if err := b.SendOrderTx(ctx, tx); err != nil {
@@ -2090,17 +2209,33 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
if err != nil {
return common.Hash{}, err
}
- return submitTransaction(ctx, s.b, signed)
+ return SubmitTransaction(ctx, s.b, signed)
+}
+
+// FillTransaction fills the defaults (nonce, gas, gasPrice) on a given unsigned transaction,
+// and returns it to the caller for further processing (signing + broadcast)
+func (s *PublicTransactionPoolAPI) FillTransaction(ctx context.Context, args SendTxArgs) (*SignTransactionResult, error) {
+ // Set some sanity defaults and terminate on failure
+ if err := args.setDefaults(ctx, s.b); err != nil {
+ return nil, err
+ }
+ // Assemble the transaction and obtain rlp
+ tx := args.toTransaction()
+ data, err := tx.MarshalBinary()
+ if err != nil {
+ return nil, err
+ }
+ return &SignTransactionResult{data, tx}, nil
}
// SendRawTransaction will add the signed transaction to the transaction pool.
// The sender is responsible for signing the transaction and using the correct nonce.
-func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encodedTx hexutil.Bytes) (common.Hash, error) {
+func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, input hexutil.Bytes) (common.Hash, error) {
tx := new(types.Transaction)
- if err := rlp.DecodeBytes(encodedTx, tx); err != nil {
+ if err := tx.UnmarshalBinary(input); err != nil {
return common.Hash{}, err
}
- return submitTransaction(ctx, s.b, tx)
+ return SubmitTransaction(ctx, s.b, tx)
}
// SendOrderRawTransaction will add the signed transaction to the transaction pool.
@@ -2993,29 +3128,30 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sen
if err != nil {
return nil, err
}
- data, err := rlp.EncodeToBytes(tx)
+ data, err := tx.MarshalBinary()
if err != nil {
return nil, err
}
return &SignTransactionResult{data, tx}, nil
}
-// PendingTransactions returns the transactions that are in the transaction pool and have a from address that is one of
-// the accounts this node manages.
+// PendingTransactions returns the transactions that are in the transaction pool
+// and have a from address that is one of the accounts this node manages.
func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, error) {
pending, err := s.b.GetPoolTransactions()
if err != nil {
return nil, err
}
-
+ accounts := make(map[common.Address]struct{})
+ for _, wallet := range s.b.AccountManager().Wallets() {
+ for _, account := range wallet.Accounts() {
+ accounts[account.Address] = struct{}{}
+ }
+ }
transactions := make([]*RPCTransaction, 0, len(pending))
for _, tx := range pending {
- var signer types.Signer = types.HomesteadSigner{}
- if tx.Protected() {
- signer = types.NewEIP155Signer(tx.ChainId())
- }
- from, _ := types.Sender(signer, tx)
- if _, err := s.b.AccountManager().Find(accounts.Account{Address: from}); err == nil {
+ from, _ := types.Sender(s.signer, tx)
+ if _, exists := accounts[from]; exists {
transactions = append(transactions, newRPCPendingTransaction(tx))
}
}
@@ -3032,19 +3168,16 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr
return common.Hash{}, err
}
matchTx := sendArgs.toTransaction()
+
+ // Iterate the pending list for replacement
pending, err := s.b.GetPoolTransactions()
if err != nil {
return common.Hash{}, err
}
-
for _, p := range pending {
- var signer types.Signer = types.HomesteadSigner{}
- if p.Protected() {
- signer = types.NewEIP155Signer(p.ChainId())
- }
- wantSigHash := signer.Hash(matchTx)
-
- if pFrom, err := types.Sender(signer, p); err == nil && pFrom == sendArgs.From && signer.Hash(p) == wantSigHash {
+ wantSigHash := s.signer.Hash(matchTx)
+ pFrom, err := types.Sender(s.signer, p)
+ if err == nil && pFrom == sendArgs.From && s.signer.Hash(p) == wantSigHash {
// Match. Re-sign and send the transaction.
if gasPrice != nil && (*big.Int)(gasPrice).Sign() != 0 {
sendArgs.GasPrice = gasPrice
@@ -3062,8 +3195,7 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr
return signedTx.Hash(), nil
}
}
-
- return common.Hash{}, fmt.Errorf("Transaction %#x not found", matchTx.Hash())
+ return common.Hash{}, fmt.Errorf("transaction %#x not found", matchTx.Hash())
}
// PublicDebugAPI is the collection of Ethereum APIs exposed over the public
@@ -3286,3 +3418,18 @@ func (s *PublicBlockChainAPI) GetStakerROIMasternode(masternode common.Address)
return 100.0 / float64(totalCap.Div(totalCap, voterRewardAYear).Uint64())
}
+
+// checkTxFee is an internal function used to check whether the fee of
+// the given transaction is _reasonable_(under the cap).
+func checkTxFee(gasPrice *big.Int, gas uint64, cap float64) error {
+ // Short circuit if there is no cap for transaction fee at all.
+ if cap == 0 {
+ return nil
+ }
+ feeEth := new(big.Float).Quo(new(big.Float).SetInt(new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(gas))), new(big.Float).SetInt(big.NewInt(params.Ether)))
+ feeFloat, _ := feeEth.Float64()
+ if feeFloat > cap {
+ return fmt.Errorf("tx fee (%.2f ether) exceeds the configured cap (%.2f ether)", feeFloat, cap)
+ }
+ return nil
+}
diff --git a/internal/guide/guide_test.go b/internal/guide/guide_test.go
index 9077ae52a4..64b9e162fa 100644
--- a/internal/guide/guide_test.go
+++ b/internal/guide/guide_test.go
@@ -30,6 +30,7 @@ import (
"time"
"github.com/XinFinOrg/XDPoSChain/accounts/keystore"
+ "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types"
)
@@ -74,7 +75,8 @@ func TestAccountManagement(t *testing.T) {
if err != nil {
t.Fatalf("Failed to create signer account: %v", err)
}
- tx, chain := new(types.Transaction), big.NewInt(1)
+ tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 0, big.NewInt(0), nil)
+ chain := big.NewInt(1)
// Sign a transaction with a single authorization
if _, err := ks.SignTxWithPassphrase(signer, "Signer password", tx, chain); err != nil {
diff --git a/les/odr_test.go b/les/odr_test.go
index bbaa7c0dfa..1234bd2853 100644
--- a/les/odr_test.go
+++ b/les/odr_test.go
@@ -19,7 +19,6 @@ package les
import (
"bytes"
"context"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"math/big"
"testing"
"time"
@@ -27,6 +26,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/math"
"github.com/XinFinOrg/XDPoSChain/core"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
@@ -133,7 +133,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
if value, ok := feeCapacity[testContractAddr]; ok {
balanceTokenFee = value
}
- msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false, balanceTokenFee, header.Number)}
+ msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, nil, false, balanceTokenFee, header.Number)}
context := core.NewEVMContext(msg, header, bc, nil)
vmenv := vm.NewEVM(context, statedb, nil, config, vm.Config{})
@@ -153,7 +153,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
if value, ok := feeCapacity[testContractAddr]; ok {
balanceTokenFee = value
}
- msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false, balanceTokenFee, header.Number)}
+ msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, nil, false, balanceTokenFee, header.Number)}
context := core.NewEVMContext(msg, header, lc, nil)
vmenv := vm.NewEVM(context, statedb, nil, config, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxUint64)
diff --git a/light/odr_test.go b/light/odr_test.go
index 4905260ade..83e1c807ca 100644
--- a/light/odr_test.go
+++ b/light/odr_test.go
@@ -20,12 +20,13 @@ import (
"bytes"
"context"
"errors"
- "github.com/XinFinOrg/XDPoSChain/consensus"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"math/big"
"testing"
"time"
+ "github.com/XinFinOrg/XDPoSChain/consensus"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
+
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/math"
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
@@ -183,7 +184,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
if value, ok := feeCapacity[testContractAddr]; ok {
balanceTokenFee = value
}
- msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, new(big.Int), data, false, balanceTokenFee, header.Number)}
+ msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, new(big.Int), data, nil, false, balanceTokenFee, header.Number)}
context := core.NewEVMContext(msg, header, chain, nil)
vmenv := vm.NewEVM(context, st, nil, config, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxUint64)
diff --git a/light/txpool.go b/light/txpool.go
index 281af18b2a..292e91b92d 100644
--- a/light/txpool.go
+++ b/light/txpool.go
@@ -19,6 +19,7 @@ package light
import (
"context"
"fmt"
+ "math/big"
"sync"
"time"
@@ -30,7 +31,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/event"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
- "github.com/XinFinOrg/XDPoSChain/rlp"
)
const (
@@ -67,6 +67,7 @@ type TxPool struct {
clearIdx uint64 // earliest block nr that can contain mined tx info
homestead bool
+ eip2718 bool // Fork indicator whether we are in the eip2718 stage.
}
// TxRelayBackend provides an interface to the mechanism that forwards transacions
@@ -91,7 +92,7 @@ type TxRelayBackend interface {
func NewTxPool(config *params.ChainConfig, chain *LightChain, relay TxRelayBackend) *TxPool {
pool := &TxPool{
config: config,
- signer: types.NewEIP155Signer(config.ChainId),
+ signer: types.LatestSigner(config),
nonce: make(map[common.Address]uint64),
pending: make(map[common.Hash]*types.Transaction),
mined: make(map[common.Hash][]*types.Transaction),
@@ -310,8 +311,11 @@ func (pool *TxPool) setNewHead(head *types.Header) {
txc, _ := pool.reorgOnNewHead(ctx, head)
m, r := txc.getLists()
pool.relay.NewHead(pool.head, m, r)
+
+ // Update fork indicator by next pending block number
+ next := new(big.Int).Add(head.Number, big.NewInt(1))
pool.homestead = pool.config.IsHomestead(head.Number)
- pool.signer = types.MakeSigner(pool.config, head.Number)
+ pool.eip2718 = pool.config.IsEIP1559(next)
}
// Stop stops the light transaction pool
@@ -403,7 +407,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
}
// Should supply enough intrinsic gas
- gas, err := core.IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)
+ gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, pool.homestead)
if err != nil {
return err
}
@@ -452,8 +456,7 @@ func (self *TxPool) add(ctx context.Context, tx *types.Transaction) error {
func (self *TxPool) Add(ctx context.Context, tx *types.Transaction) error {
self.mu.Lock()
defer self.mu.Unlock()
-
- data, err := rlp.EncodeToBytes(tx)
+ data, err := tx.MarshalBinary()
if err != nil {
return err
}
diff --git a/miner/worker.go b/miner/worker.go
index 823cd74258..3de0328bf8 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -19,17 +19,15 @@ package miner
import (
"bytes"
"encoding/binary"
-
- "github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate"
- "github.com/XinFinOrg/XDPoSChain/accounts"
-
+ "errors"
"math/big"
"sync"
"sync/atomic"
"time"
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
-
+ "github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate"
+ "github.com/XinFinOrg/XDPoSChain/accounts"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
@@ -466,10 +464,13 @@ func (self *worker) push(work *Work) {
// makeCurrent creates a new environment for the current cycle.
func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error {
+ // Retrieve the parent state to execute on top and start a prefetcher for
+ // the miner to speed block sealing up a bit
state, err := self.chain.StateAt(parent.Root())
if err != nil {
return err
}
+
author, _ := self.chain.Engine().Author(parent.Header())
var XDCxState *tradingstate.TradingStateDB
var lendingState *lendingstate.LendingStateDB
@@ -490,7 +491,7 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error
work := &Work{
config: self.config,
- signer: types.NewEIP155Signer(self.config.ChainId),
+ signer: types.MakeSigner(self.config, header.Number),
state: state,
parentState: state.Copy(),
tradingState: XDCxState,
@@ -981,28 +982,33 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad
continue
}
err, logs, tokenFeeUsed, gas := env.commitTransaction(balanceFee, tx, bc, coinbase, gp)
- switch err {
- case core.ErrGasLimitReached:
+ switch {
+ case errors.Is(err, core.ErrGasLimitReached):
// Pop the current out-of-gas transaction without shifting in the next from the account
log.Trace("Gas limit exceeded for current block", "sender", from)
txs.Pop()
- case core.ErrNonceTooLow:
+ case errors.Is(err, core.ErrNonceTooLow):
// New head notification data race between the transaction pool and miner, shift
log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce())
txs.Shift()
- case core.ErrNonceTooHigh:
+ case errors.Is(err, core.ErrNonceTooHigh):
// Reorg notification data race between the transaction pool and miner, skip account =
log.Trace("Skipping account with high nonce", "sender", from, "nonce", tx.Nonce())
txs.Pop()
- case nil:
+ case errors.Is(err, nil):
// Everything ok, collect the logs and shift in the next transaction from the same account
coalescedLogs = append(coalescedLogs, logs...)
env.tcount++
txs.Shift()
+ case errors.Is(err, core.ErrTxTypeNotSupported):
+ // Pop the unsupported transaction without shifting in the next from the account
+ log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type())
+ txs.Pop()
+
default:
// Strange error, discard the transaction and get the next in line (note, the
// nonce-too-high clause will prevent us from executing in vain).
diff --git a/params/config.go b/params/config.go
index ed488836d8..ff2de6a7eb 100644
--- a/params/config.go
+++ b/params/config.go
@@ -363,6 +363,14 @@ type ChainConfig struct {
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)
+ PetersburgBlock *big.Int `json:"petersburgBlock,omitempty"`
+ IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"`
+ BerlinBlock *big.Int `json:"berlinBlock,omitempty"`
+ LondonBlock *big.Int `json:"londonBlock,omitempty"`
+ MergeBlock *big.Int `json:"mergeBlock,omitempty"`
+ ShanghaiBlock *big.Int `json:"shanghaiBlock,omitempty"`
+ Eip1559Block *big.Int `json:"eip1559Block,omitempty"`
+
// Various consensus engines
Ethash *EthashConfig `json:"ethash,omitempty"`
Clique *CliqueConfig `json:"clique,omitempty"`
@@ -552,37 +560,37 @@ func (c *ChainConfig) IsConstantinople(num *big.Int) bool {
// - equal to or greater than the PetersburgBlock fork block,
// - OR is nil, and Constantinople is active
func (c *ChainConfig) IsPetersburg(num *big.Int) bool {
- return isForked(common.TIPXDCXCancellationFee, num)
+ return isForked(common.TIPXDCXCancellationFee, num) || isForked(c.PetersburgBlock, num)
}
// IsIstanbul returns whether num is either equal to the Istanbul fork block or greater.
func (c *ChainConfig) IsIstanbul(num *big.Int) bool {
- return isForked(common.TIPXDCXCancellationFee, num)
+ return isForked(common.TIPXDCXCancellationFee, num) || isForked(c.IstanbulBlock, num)
}
// IsBerlin returns whether num is either equal to the Berlin fork block or greater.
func (c *ChainConfig) IsBerlin(num *big.Int) bool {
- return isForked(common.BerlinBlock, num)
+ return isForked(common.BerlinBlock, num) || isForked(c.BerlinBlock, num)
}
// IsLondon returns whether num is either equal to the London fork block or greater.
func (c *ChainConfig) IsLondon(num *big.Int) bool {
- return isForked(common.LondonBlock, num)
+ return isForked(common.LondonBlock, num) || isForked(c.LondonBlock, num)
}
// IsMerge returns whether num is either equal to the Merge fork block or greater.
// Different from Geth which uses `block.difficulty != nil`
func (c *ChainConfig) IsMerge(num *big.Int) bool {
- return isForked(common.MergeBlock, num)
+ return isForked(common.MergeBlock, num) || isForked(c.MergeBlock, num)
}
// IsShanghai returns whether num is either equal to the Shanghai fork block or greater.
func (c *ChainConfig) IsShanghai(num *big.Int) bool {
- return isForked(common.ShanghaiBlock, num)
+ return isForked(common.ShanghaiBlock, num) || isForked(c.ShanghaiBlock, num)
}
func (c *ChainConfig) IsEIP1559(num *big.Int) bool {
- return isForked(common.Eip1559Block, num)
+ return isForked(common.Eip1559Block, num) || isForked(c.Eip1559Block, num)
}
func (c *ChainConfig) IsTIP2019(num *big.Int) bool {
diff --git a/params/protocol_params.go b/params/protocol_params.go
index 93419fc367..b8cd64f723 100644
--- a/params/protocol_params.go
+++ b/params/protocol_params.go
@@ -61,6 +61,10 @@ const (
CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction.
SuicideRefundGas uint64 = 24000 // Refunded following a suicide operation.
MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
+
+ TxAccessListAddressGas uint64 = 2400 // Per address specified in EIP 2930 access list
+ TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list
+
TxDataNonZeroGas uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
MaxCodeSize = 24576 // Maximum bytecode to permit for a contract
diff --git a/rlp/raw.go b/rlp/raw.go
index 2b3f328f66..74f2cb8632 100644
--- a/rlp/raw.go
+++ b/rlp/raw.go
@@ -154,3 +154,74 @@ func readSize(b []byte, slen byte) (uint64, error) {
}
return s, nil
}
+
+// AppendUint64 appends the RLP encoding of i to b, and returns the resulting slice.
+func AppendUint64(b []byte, i uint64) []byte {
+ if i == 0 {
+ return append(b, 0x80)
+ } else if i < 128 {
+ return append(b, byte(i))
+ }
+ switch {
+ case i < (1 << 8):
+ return append(b, 0x81, byte(i))
+ case i < (1 << 16):
+ return append(b, 0x82,
+ byte(i>>8),
+ byte(i),
+ )
+ case i < (1 << 24):
+ return append(b, 0x83,
+ byte(i>>16),
+ byte(i>>8),
+ byte(i),
+ )
+ case i < (1 << 32):
+ return append(b, 0x84,
+ byte(i>>24),
+ byte(i>>16),
+ byte(i>>8),
+ byte(i),
+ )
+ case i < (1 << 40):
+ return append(b, 0x85,
+ byte(i>>32),
+ byte(i>>24),
+ byte(i>>16),
+ byte(i>>8),
+ byte(i),
+ )
+
+ case i < (1 << 48):
+ return append(b, 0x86,
+ byte(i>>40),
+ byte(i>>32),
+ byte(i>>24),
+ byte(i>>16),
+ byte(i>>8),
+ byte(i),
+ )
+ case i < (1 << 56):
+ return append(b, 0x87,
+ byte(i>>48),
+ byte(i>>40),
+ byte(i>>32),
+ byte(i>>24),
+ byte(i>>16),
+ byte(i>>8),
+ byte(i),
+ )
+
+ default:
+ return append(b, 0x88,
+ byte(i>>56),
+ byte(i>>48),
+ byte(i>>40),
+ byte(i>>32),
+ byte(i>>24),
+ byte(i>>16),
+ byte(i>>8),
+ byte(i),
+ )
+ }
+}
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 18d09b0bc7..742d2c5955 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -135,23 +135,16 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD
if err != nil {
return nil, err
}
+
+ // Prepare the EVM.
context := core.NewEVMContext(msg, block.Header(), nil, &t.json.Env.Coinbase)
context.GetHash = vmTestBlockHash
evm := vm.NewEVM(context, statedb, nil, config, vmconfig)
- if config.IsEIP1559(context.BlockNumber) {
- statedb.AddAddressToAccessList(msg.From())
- if dst := msg.To(); dst != nil {
- statedb.AddAddressToAccessList(*dst)
- // If it's a create-tx, the destination will be added inside evm.create
- }
- for _, addr := range evm.ActivePrecompiles() {
- statedb.AddAddressToAccessList(addr)
- }
- }
+ // Execute the message.
+ snapshot := statedb.Snapshot()
gaspool := new(core.GasPool)
gaspool.AddGas(block.GasLimit())
- snapshot := statedb.Snapshot()
coinbase := &t.json.Env.Coinbase
if _, _, _, err, _ := core.ApplyMessage(evm, msg, gaspool, *coinbase); err != nil {
@@ -160,6 +153,8 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD
if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) {
return statedb, fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, post.Logs)
}
+
+ // Commit block
root, _ := statedb.Commit(config.IsEIP158(block.Number()))
if root != common.Hash(post.Root) {
return statedb, fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root)
@@ -245,7 +240,7 @@ func (tx *stTransaction) toMessage(ps stPostState, number *big.Int) (core.Messag
if err != nil {
return nil, fmt.Errorf("invalid tx data %q", dataHex)
}
- msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, data, true, nil, number)
+ msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, data, nil, true, nil, number)
return msg, nil
}