mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 07:06:42 +00:00
Added a new RPC endpoint (bor_sendRawTransactionConditional) to support EIP-4337 Bundled Transactions (#945)
* added new api to support conditional transactions (EIP-4337) (#700) * Refactored the code and updated the miner to check for the validity of options (#793) * refactored the code and updated the miner to check for the validity of options * added new errors -32003 and -32005 * added unit tests * addressed comments * Aa 4337 update generics (#799) * poc * minor bug fix * use common.Hash * updated UnmarshalJSON function (reference - tynes) * fix * done * linters * with test * undo some unintentional changes --------- Co-authored-by: Pratik Patil <pratikspatil024@gmail.com> * handelling the block range and timestamp range, also made timestamp a pointer --------- Co-authored-by: Evgeny Danilenko <6655321@bk.ru> * Added filtering of conditional transactions in txpool (#920) * added filtering of conditional transactions in txpool * minor fix in ValidateKnownAccounts * bug fix * Supporting nil knownAccounts * lints * bundled transactions are not announced/broadcasted to the peers * fixed after upstream merge * few fixes * sentry reject conditional transaction * Changed the namespace of conditional transaction API from `eth` to `bor` (#985) * added conditional transaction to bor namespace * test comit * test comit * added conditional transaction * namespapce changed to bor * cleanup * cleanup * addressed comments * reverted changes in ValidateKnownAccounts * addressed comments and removed unwanted code * addressed comments * bug fix * lint * removed licence from core/types/transaction_conditional_test.go --------- Co-authored-by: Evgeny Danilenko <6655321@bk.ru>
This commit is contained in:
parent
3442c0336d
commit
67fc4d87fb
25 changed files with 732 additions and 22 deletions
|
|
@ -28,8 +28,9 @@ import (
|
|||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"golang.org/x/crypto/sha3"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
// Lengths of hashes and addresses in bytes.
|
||||
|
|
@ -66,6 +67,12 @@ func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
|
|||
// If b is larger than len(h), b will be cropped from the left.
|
||||
func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
|
||||
|
||||
func HexToRefHash(s string) *Hash {
|
||||
v := BytesToHash(FromHex(s))
|
||||
|
||||
return &v
|
||||
}
|
||||
|
||||
// Bytes gets the byte representation of the underlying hash.
|
||||
func (h Hash) Bytes() []byte { return h[:] }
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,11 @@ import (
|
|||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
|
|
@ -273,3 +276,66 @@ func compareStateObjects(so0, so1 *stateObject, t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateKnownAccounts(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
knownAccounts := make(types.KnownAccounts)
|
||||
|
||||
types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add1"), common.HexToHash("0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1ce"))
|
||||
types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add2"), map[common.Hash]common.Hash{
|
||||
common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000aaa"): common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000bbb"),
|
||||
common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ccc"): common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ddd"),
|
||||
})
|
||||
|
||||
stateobjaddr1 := common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add1")
|
||||
stateobjaddr2 := common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add2")
|
||||
|
||||
storageaddr1 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000zzz")
|
||||
storageaddr21 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000aaa")
|
||||
storageaddr22 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ccc")
|
||||
|
||||
data1 := common.BytesToHash([]byte{24})
|
||||
data21 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000bbb")
|
||||
data22 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ddd")
|
||||
|
||||
s := newStateTest()
|
||||
|
||||
// set initial state object value
|
||||
s.state.SetState(stateobjaddr1, storageaddr1, data1)
|
||||
s.state.SetState(stateobjaddr2, storageaddr21, data21)
|
||||
s.state.SetState(stateobjaddr2, storageaddr22, data22)
|
||||
|
||||
require.NoError(t, s.state.ValidateKnownAccounts(knownAccounts))
|
||||
|
||||
types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add2"), common.HexToHash("0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1cf"))
|
||||
|
||||
stateobjaddr3 := common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add2")
|
||||
storageaddr3 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000yyy")
|
||||
data3 := common.BytesToHash([]byte{24})
|
||||
|
||||
s.state.SetState(stateobjaddr3, storageaddr3, data3)
|
||||
|
||||
// expected error
|
||||
err := s.state.ValidateKnownAccounts(knownAccounts)
|
||||
require.Error(t, err, "should have been an error")
|
||||
|
||||
// correct the previous mistake "0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1cf" -> "0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1ce"
|
||||
types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add2"), common.HexToHash("0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1ce"))
|
||||
types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add3"), map[common.Hash]common.Hash{
|
||||
common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000aaa"): common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000bbb"),
|
||||
common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ccc"): common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ddd"),
|
||||
})
|
||||
|
||||
stateobjaddr4 := common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add3")
|
||||
storageaddr41 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000aaa")
|
||||
storageaddr42 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ccc")
|
||||
data4 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000bbb")
|
||||
|
||||
s.state.SetState(stateobjaddr4, storageaddr41, data4)
|
||||
s.state.SetState(stateobjaddr4, storageaddr42, data4)
|
||||
|
||||
// expected error
|
||||
err = s.state.ValidateKnownAccounts(knownAccounts)
|
||||
require.Error(t, err, "should have been an error")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1640,6 +1640,39 @@ func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addre
|
|||
return s.accessList.Contains(addr, slot)
|
||||
}
|
||||
|
||||
func (s *StateDB) ValidateKnownAccounts(knownAccounts types.KnownAccounts) error {
|
||||
if knownAccounts == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for k, v := range knownAccounts {
|
||||
// check if the value is hex string or an object
|
||||
switch {
|
||||
case v.IsSingle():
|
||||
trie, _ := s.StorageTrie(k)
|
||||
if trie != nil {
|
||||
actualRootHash := trie.Hash()
|
||||
if *v.Single != actualRootHash {
|
||||
return fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, v.Single, actualRootHash)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Storage Trie is nil for: %v", k)
|
||||
}
|
||||
case v.IsStorage():
|
||||
for slot, value := range v.Storage {
|
||||
actualValue := s.GetState(k, slot)
|
||||
if value != actualValue {
|
||||
return fmt.Errorf("invalid slot value at address: %v slot: %v value: %v actual value: %v", k, slot, value, actualValue)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("impossible to validate known accounts: %v", k)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// convertAccountSet converts a provided account set from address keyed to hash keyed.
|
||||
func (s *StateDB) convertAccountSet(set map[common.Address]struct{}) map[common.Hash]struct{} {
|
||||
ret := make(map[common.Hash]struct{})
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
cmath "github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
|
@ -563,6 +564,32 @@ func (l *list) Filter(costLimit *uint256.Int, gasLimit uint64) (types.Transactio
|
|||
return removed, invalids
|
||||
}
|
||||
|
||||
// FilterTxConditional returns the conditional transactions with invalid KnownAccounts
|
||||
// TODO - We will also have to check block range and time stamp range!
|
||||
func (l *list) FilterTxConditional(state *state.StateDB) types.Transactions {
|
||||
removed := l.txs.filter(func(tx *types.Transaction) bool {
|
||||
if options := tx.GetOptions(); options != nil {
|
||||
err := state.ValidateKnownAccounts(options.KnownAccounts)
|
||||
if err != nil {
|
||||
log.Error("Error while Filtering Tx Conditional", "err", err)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if len(removed) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
l.txs.reheap(true)
|
||||
|
||||
return removed
|
||||
}
|
||||
|
||||
// Cap places a hard limit on the number of items, returning all transactions
|
||||
// exceeding that limit.
|
||||
func (l *list) Cap(threshold int) types.Transactions {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,11 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
|
@ -78,3 +82,63 @@ func BenchmarkListAdd(b *testing.B) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterTxConditional(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create an in memory state db to test against.
|
||||
memDb := rawdb.NewMemoryDatabase()
|
||||
db := state.NewDatabase(memDb)
|
||||
state, _ := state.New(common.Hash{}, db, nil)
|
||||
|
||||
// Create a private key to sign transactions.
|
||||
key, _ := crypto.GenerateKey()
|
||||
|
||||
// Create a list.
|
||||
list := newList(true)
|
||||
|
||||
// Create a transaction with no defined tx options
|
||||
// and add to the list.
|
||||
tx := transaction(0, 1000, key)
|
||||
list.Add(tx, DefaultConfig.PriceBump)
|
||||
|
||||
// There should be no drops at this point.
|
||||
// No state has been modified.
|
||||
drops := list.FilterTxConditional(state)
|
||||
|
||||
count := len(drops)
|
||||
require.Equal(t, 0, count, "got %d filtered by TxOptions when there should not be any", count)
|
||||
|
||||
// Create another transaction with a known account storage root tx option
|
||||
// and add to the list.
|
||||
tx2 := transaction(1, 1000, key)
|
||||
|
||||
var options types.OptionsAA4337
|
||||
|
||||
options.KnownAccounts = types.KnownAccounts{
|
||||
common.Address{19: 1}: &types.Value{
|
||||
Single: common.HexToRefHash("0xe734938daf39aae1fa4ee64dc3155d7c049f28b57a8ada8ad9e86832e0253bef"),
|
||||
},
|
||||
}
|
||||
|
||||
state.SetState(common.Address{19: 1}, common.Hash{}, common.Hash{30: 1})
|
||||
tx2.PutOptions(&options)
|
||||
list.Add(tx2, DefaultConfig.PriceBump)
|
||||
|
||||
// There should still be no drops as no state has been modified.
|
||||
drops = list.FilterTxConditional(state)
|
||||
|
||||
count = len(drops)
|
||||
require.Equal(t, 0, count, "got %d filtered by TxOptions when there should not be any", count)
|
||||
|
||||
// Set state that conflicts with tx2's policy
|
||||
state.SetState(common.Address{19: 1}, common.Hash{}, common.Hash{31: 1})
|
||||
|
||||
// tx2 should be the single transaction filtered out
|
||||
drops = list.FilterTxConditional(state)
|
||||
|
||||
count = len(drops)
|
||||
require.Equal(t, 1, count, "got %d filtered by TxOptions when there should be a single one", count)
|
||||
|
||||
require.Equal(t, tx2, drops[0], "Got %x, expected %x", drops[0].Hash(), tx2.Hash())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2303,10 +2303,19 @@ func (pool *TxPool) demoteUnexecutables() {
|
|||
pool.enqueueTx(hash, tx, false, false)
|
||||
}
|
||||
|
||||
pendingGauge.Dec(int64(oldsLen + dropsLen + invalidsLen))
|
||||
// Drop all transactions that no longer have valid TxOptions
|
||||
txConditionalsRemoved := list.FilterTxConditional(pool.currentState)
|
||||
|
||||
for _, tx := range txConditionalsRemoved {
|
||||
hash := tx.Hash()
|
||||
pool.all.Remove(hash)
|
||||
log.Trace("Removed invalid conditional transaction", "hash", hash)
|
||||
}
|
||||
|
||||
pendingGauge.Dec(int64(oldsLen + dropsLen + invalidsLen + len(txConditionalsRemoved)))
|
||||
|
||||
if pool.locals.contains(addr) {
|
||||
localGauge.Dec(int64(oldsLen + dropsLen + invalidsLen))
|
||||
localGauge.Dec(int64(oldsLen + dropsLen + invalidsLen + len(txConditionalsRemoved)))
|
||||
}
|
||||
// If there's a gap in front, alert (should never happen) and postpone all transactions
|
||||
if list.Len() > 0 && list.txs.Get(nonce) == nil {
|
||||
|
|
|
|||
|
|
@ -1993,6 +1993,7 @@ func TestUnderpricing(t *testing.T) {
|
|||
keys[i], _ = crypto.GenerateKey()
|
||||
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
|
||||
}
|
||||
|
||||
// Generate and queue a batch of transactions, both pending and queued
|
||||
txs := types.Transactions{}
|
||||
|
||||
|
|
@ -2023,6 +2024,7 @@ func TestUnderpricing(t *testing.T) {
|
|||
if err := validatePoolInternals(pool); err != nil {
|
||||
t.Fatalf("pool internal state corrupted: %v", err)
|
||||
}
|
||||
|
||||
// Ensure that adding an underpriced transaction on block limit fails
|
||||
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(1), keys[1])); !errors.Is(err, ErrUnderpriced) {
|
||||
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
|
||||
|
|
@ -2064,6 +2066,7 @@ func TestUnderpricing(t *testing.T) {
|
|||
if err := validatePoolInternals(pool); err != nil {
|
||||
t.Fatalf("pool internal state corrupted: %v", err)
|
||||
}
|
||||
|
||||
// Ensure that adding local transactions can push out even higher priced ones
|
||||
ltx = pricedTransaction(1, 100000, big.NewInt(0), keys[2])
|
||||
if err := pool.AddLocal(ltx); err != nil {
|
||||
|
|
@ -2263,6 +2266,7 @@ func TestUnderpricingDynamicFee(t *testing.T) {
|
|||
if err := validatePoolInternals(pool); err != nil {
|
||||
t.Fatalf("pool internal state corrupted: %v", err)
|
||||
}
|
||||
|
||||
// Ensure that adding local transactions can push out even higher priced ones
|
||||
ltx = dynamicFeeTx(1, 100000, big.NewInt(0), big.NewInt(0), keys[2])
|
||||
if err := pool.AddLocal(ltx); err != nil {
|
||||
|
|
|
|||
|
|
@ -188,6 +188,44 @@ func (h *Header) EmptyReceipts() bool {
|
|||
return h.ReceiptHash == EmptyReceiptsHash
|
||||
}
|
||||
|
||||
// ValidateBlockNumberOptions4337 validates the block range passed as in the options parameter in the conditional transaction (EIP-4337)
|
||||
func (h *Header) ValidateBlockNumberOptions4337(minBlockNumber *big.Int, maxBlockNumber *big.Int) error {
|
||||
currentBlockNumber := h.Number
|
||||
|
||||
if minBlockNumber != nil {
|
||||
if currentBlockNumber.Cmp(minBlockNumber) == -1 {
|
||||
return fmt.Errorf("current block number %v is less than minimum block number: %v", currentBlockNumber, minBlockNumber)
|
||||
}
|
||||
}
|
||||
|
||||
if maxBlockNumber != nil {
|
||||
if currentBlockNumber.Cmp(maxBlockNumber) == 1 {
|
||||
return fmt.Errorf("current block number %v is greater than maximum block number: %v", currentBlockNumber, maxBlockNumber)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateBlockNumberOptions4337 validates the timestamp range passed as in the options parameter in the conditional transaction (EIP-4337)
|
||||
func (h *Header) ValidateTimestampOptions4337(minTimestamp *uint64, maxTimestamp *uint64) error {
|
||||
currentBlockTime := h.Time
|
||||
|
||||
if minTimestamp != nil {
|
||||
if currentBlockTime < *minTimestamp {
|
||||
return fmt.Errorf("current block time %v is less than minimum timestamp: %v", currentBlockTime, minTimestamp)
|
||||
}
|
||||
}
|
||||
|
||||
if maxTimestamp != nil {
|
||||
if currentBlockTime > *maxTimestamp {
|
||||
return fmt.Errorf("current block time %v is greater than maximum timestamp: %v", currentBlockTime, maxTimestamp)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
|
|
|||
|
|
@ -463,3 +463,167 @@ func TestRlpDecodeParentHash(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateBlockNumberOptions4337(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testsPass := []struct {
|
||||
number string
|
||||
header Header
|
||||
minBlockNumber *big.Int
|
||||
maxBlockNumber *big.Int
|
||||
}{
|
||||
{
|
||||
"1",
|
||||
Header{Number: big.NewInt(10)},
|
||||
big.NewInt(0),
|
||||
big.NewInt(20),
|
||||
},
|
||||
{
|
||||
"2",
|
||||
Header{Number: big.NewInt(10)},
|
||||
big.NewInt(10),
|
||||
big.NewInt(10),
|
||||
},
|
||||
{
|
||||
"3",
|
||||
Header{Number: big.NewInt(10)},
|
||||
big.NewInt(10),
|
||||
big.NewInt(11),
|
||||
},
|
||||
{
|
||||
"4",
|
||||
Header{Number: big.NewInt(10)},
|
||||
big.NewInt(0),
|
||||
big.NewInt(10),
|
||||
},
|
||||
}
|
||||
|
||||
testsFail := []struct {
|
||||
number string
|
||||
header Header
|
||||
minBlockNumber *big.Int
|
||||
maxBlockNumber *big.Int
|
||||
}{
|
||||
{
|
||||
"5",
|
||||
Header{Number: big.NewInt(10)},
|
||||
big.NewInt(0),
|
||||
big.NewInt(0),
|
||||
},
|
||||
{
|
||||
"6",
|
||||
Header{Number: big.NewInt(10)},
|
||||
big.NewInt(0),
|
||||
big.NewInt(9),
|
||||
},
|
||||
{
|
||||
"7",
|
||||
Header{Number: big.NewInt(10)},
|
||||
big.NewInt(11),
|
||||
big.NewInt(9),
|
||||
},
|
||||
{
|
||||
"8",
|
||||
Header{Number: big.NewInt(10)},
|
||||
big.NewInt(11),
|
||||
big.NewInt(20),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testsPass {
|
||||
if err := test.header.ValidateBlockNumberOptions4337(test.minBlockNumber, test.maxBlockNumber); err != nil {
|
||||
t.Fatalf("test number %v should not have failed. err: %v", test.number, err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, test := range testsFail {
|
||||
if err := test.header.ValidateBlockNumberOptions4337(test.minBlockNumber, test.maxBlockNumber); err == nil {
|
||||
t.Fatalf("test number %v should have failed. err is nil", test.number)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateTimestampOptions4337(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
u64Ptr := func(n uint64) *uint64 {
|
||||
return &n
|
||||
}
|
||||
|
||||
testsPass := []struct {
|
||||
number string
|
||||
header Header
|
||||
minTimestamp *uint64
|
||||
maxTimestamp *uint64
|
||||
}{
|
||||
{
|
||||
"1",
|
||||
Header{Time: 1600000000},
|
||||
u64Ptr(1500000000),
|
||||
u64Ptr(1700000000),
|
||||
},
|
||||
{
|
||||
"2",
|
||||
Header{Time: 1600000000},
|
||||
u64Ptr(1600000000),
|
||||
u64Ptr(1600000000),
|
||||
},
|
||||
{
|
||||
"3",
|
||||
Header{Time: 1600000000},
|
||||
u64Ptr(1600000000),
|
||||
u64Ptr(1700000000),
|
||||
},
|
||||
{
|
||||
"4",
|
||||
Header{Time: 1600000000},
|
||||
u64Ptr(1500000000),
|
||||
u64Ptr(1600000000),
|
||||
},
|
||||
}
|
||||
|
||||
testsFail := []struct {
|
||||
number string
|
||||
header Header
|
||||
minTimestamp *uint64
|
||||
maxTimestamp *uint64
|
||||
}{
|
||||
{
|
||||
"5",
|
||||
Header{Time: 1600000000},
|
||||
u64Ptr(1500000000),
|
||||
u64Ptr(1500000000),
|
||||
},
|
||||
{
|
||||
"6",
|
||||
Header{Time: 1600000000},
|
||||
u64Ptr(1400000000),
|
||||
u64Ptr(1500000000),
|
||||
},
|
||||
{
|
||||
"7",
|
||||
Header{Time: 1600000000},
|
||||
u64Ptr(1700000000),
|
||||
u64Ptr(1500000000),
|
||||
},
|
||||
{
|
||||
"8",
|
||||
Header{Time: 1600000000},
|
||||
u64Ptr(1700000000),
|
||||
u64Ptr(1800000000),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testsPass {
|
||||
if err := test.header.ValidateTimestampOptions4337(test.minTimestamp, test.maxTimestamp); err != nil {
|
||||
t.Fatalf("test number %v should not have failed. err: %v", test.number, err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, test := range testsFail {
|
||||
if err := test.header.ValidateTimestampOptions4337(test.minTimestamp, test.maxTimestamp); err == nil {
|
||||
t.Fatalf("test number %v should have failed. err is nil", test.number)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,9 @@ type Transaction struct {
|
|||
inner TxData // Consensus contents of a transaction
|
||||
time time.Time // Time first seen locally (spam avoidance)
|
||||
|
||||
// knownAccounts (EIP-4337)
|
||||
optionsAA4337 *OptionsAA4337
|
||||
|
||||
// caches
|
||||
hash atomic.Pointer[common.Hash]
|
||||
size atomic.Pointer[uint64]
|
||||
|
|
@ -101,6 +104,16 @@ type TxData interface {
|
|||
effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int
|
||||
}
|
||||
|
||||
// PutOptions stores the optionsAA4337 field of the conditional transaction (EIP-4337)
|
||||
func (tx *Transaction) PutOptions(options *OptionsAA4337) {
|
||||
tx.optionsAA4337 = options
|
||||
}
|
||||
|
||||
// GetOptions returns the optionsAA4337 field of the conditional transaction (EIP-4337)
|
||||
func (tx *Transaction) GetOptions() *OptionsAA4337 {
|
||||
return tx.optionsAA4337
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder
|
||||
func (tx *Transaction) EncodeRLP(w io.Writer) error {
|
||||
if tx.Type() == LegacyTxType {
|
||||
|
|
|
|||
146
core/types/transaction_conditional.go
Normal file
146
core/types/transaction_conditional.go
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
type KnownAccounts map[common.Address]*Value
|
||||
|
||||
type Value struct {
|
||||
Single *common.Hash
|
||||
Storage map[common.Hash]common.Hash
|
||||
}
|
||||
|
||||
func SingleFromHex(hex string) *Value {
|
||||
return &Value{Single: common.HexToRefHash(hex)}
|
||||
}
|
||||
|
||||
func FromMap(m map[string]string) *Value {
|
||||
res := map[common.Hash]common.Hash{}
|
||||
|
||||
for k, v := range m {
|
||||
res[common.HexToHash(k)] = common.HexToHash(v)
|
||||
}
|
||||
|
||||
return &Value{Storage: res}
|
||||
}
|
||||
|
||||
func (v *Value) IsSingle() bool {
|
||||
return v != nil && v.Single != nil && !v.IsStorage()
|
||||
}
|
||||
|
||||
func (v *Value) IsStorage() bool {
|
||||
return v != nil && v.Storage != nil
|
||||
}
|
||||
|
||||
const EmptyValue = "{}"
|
||||
|
||||
func (v *Value) MarshalJSON() ([]byte, error) {
|
||||
if v.IsSingle() {
|
||||
return json.Marshal(v.Single)
|
||||
}
|
||||
|
||||
if v.IsStorage() {
|
||||
return json.Marshal(v.Storage)
|
||||
}
|
||||
|
||||
return []byte(EmptyValue), nil
|
||||
}
|
||||
|
||||
const hashTypeName = "Hash"
|
||||
|
||||
func (v *Value) UnmarshalJSON(data []byte) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var m map[string]json.RawMessage
|
||||
|
||||
err := json.Unmarshal(data, &m)
|
||||
if err != nil {
|
||||
// single Hash value case
|
||||
v.Single = new(common.Hash)
|
||||
|
||||
innerErr := json.Unmarshal(data, v.Single)
|
||||
if innerErr != nil {
|
||||
return fmt.Errorf("can't unmarshal to single value with error: %v value %q", innerErr, string(data))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
res := make(map[common.Hash]common.Hash, len(m))
|
||||
|
||||
for k, v := range m {
|
||||
// check k if it is a Hex value
|
||||
var kHash common.Hash
|
||||
|
||||
err = hexutil.UnmarshalFixedText(hashTypeName, []byte(k), kHash[:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w by key: %s with key %q and value %q", ErrKnownAccounts, err, k, string(v))
|
||||
}
|
||||
|
||||
// check v if it is a Hex value
|
||||
var vHash common.Hash
|
||||
|
||||
err = hexutil.UnmarshalFixedText("hashTypeName", bytes.Trim(v, "\""), vHash[:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w by value: %s with key %q and value %q", ErrKnownAccounts, err, k, string(v))
|
||||
}
|
||||
|
||||
res[kHash] = vHash
|
||||
}
|
||||
|
||||
v.Storage = res
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func InsertKnownAccounts[T common.Hash | map[common.Hash]common.Hash](accounts KnownAccounts, k common.Address, v T) {
|
||||
switch typedV := any(v).(type) {
|
||||
case common.Hash:
|
||||
accounts[k] = &Value{Single: &typedV}
|
||||
case map[common.Hash]common.Hash:
|
||||
accounts[k] = &Value{Storage: typedV}
|
||||
}
|
||||
}
|
||||
|
||||
type OptionsAA4337 struct {
|
||||
KnownAccounts KnownAccounts `json:"knownAccounts"`
|
||||
BlockNumberMin *big.Int `json:"blockNumberMin"`
|
||||
BlockNumberMax *big.Int `json:"blockNumberMax"`
|
||||
TimestampMin *uint64 `json:"timestampMin"`
|
||||
TimestampMax *uint64 `json:"timestampMax"`
|
||||
}
|
||||
|
||||
var ErrKnownAccounts = errors.New("an incorrect list of knownAccounts")
|
||||
|
||||
func (ka KnownAccounts) ValidateLength() error {
|
||||
if ka == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
length := 0
|
||||
|
||||
for _, v := range ka {
|
||||
// check if the value is hex string or an object
|
||||
if v.IsSingle() {
|
||||
length += 1
|
||||
} else {
|
||||
length += len(v.Storage)
|
||||
}
|
||||
}
|
||||
|
||||
if length >= 1000 {
|
||||
return fmt.Errorf("number of slots/accounts in KnownAccounts %v exceeds the limit of 1000", length)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
31
core/types/transaction_conditional_test.go
Normal file
31
core/types/transaction_conditional_test.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
func TestKnownAccounts(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
requestRaw := []byte(`{"0xadd1add1add1add1add1add1add1add1add1add1": "0x000000000000000000000000313aadca1750caadc7bcb26ff08175c95dcf8e38", "0xadd2add2add2add2add2add2add2add2add2add2": {"0x0000000000000000000000000000000000000000000000000000000000000aaa": "0x0000000000000000000000000000000000000000000000000000000000000bbb", "0x0000000000000000000000000000000000000000000000000000000000000ccc": "0x0000000000000000000000000000000000000000000000000000000000000ddd"}}`)
|
||||
|
||||
accs := &KnownAccounts{}
|
||||
|
||||
err := json.Unmarshal(requestRaw, accs)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := &KnownAccounts{
|
||||
common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add1"): SingleFromHex("0x000000000000000000000000313aadca1750caadc7bcb26ff08175c95dcf8e38"),
|
||||
common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add2"): FromMap(map[string]string{
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000aaa": "0x0000000000000000000000000000000000000000000000000000000000000bbb",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000ccc": "0x0000000000000000000000000000000000000000000000000000000000000ddd",
|
||||
}),
|
||||
}
|
||||
|
||||
require.Equal(t, expected, accs)
|
||||
}
|
||||
|
|
@ -313,6 +313,10 @@ func (b *EthAPIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscri
|
|||
}
|
||||
|
||||
func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||
if signedTx.GetOptions() != nil && !b.eth.Miner().GetWorker().IsRunning() {
|
||||
return errors.New("bundled transactions are not broadcasted therefore they will not submitted to the transaction pool")
|
||||
}
|
||||
|
||||
err := b.eth.txPool.AddLocal(signedTx)
|
||||
if err != nil {
|
||||
if unwrapped := errors.Unwrap(err); unwrapped != nil {
|
||||
|
|
|
|||
|
|
@ -361,6 +361,7 @@ func makeExtraData(extra []byte) []byte {
|
|||
return extra
|
||||
}
|
||||
|
||||
// PeerCount returns the number of connected peers.
|
||||
func (s *Ethereum) PeerCount() int {
|
||||
return s.p2pServer.PeerCount()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,10 @@ func (p *Peer) broadcastTransactions() {
|
|||
)
|
||||
|
||||
for i := 0; i < len(queue) && size < maxTxPacketSize; i++ {
|
||||
if tx := p.txpool.Get(queue[i]); tx != nil {
|
||||
tx := p.txpool.Get(queue[i])
|
||||
|
||||
// Skip EIP-4337 bundled transactions
|
||||
if tx != nil && tx.GetOptions() == nil {
|
||||
txs = append(txs, tx)
|
||||
size += common.StorageSize(tx.Size())
|
||||
}
|
||||
|
|
@ -158,7 +161,10 @@ func (p *Peer) announceTransactions() {
|
|||
)
|
||||
|
||||
for count = 0; count < len(queue) && size < maxTxPacketSize; count++ {
|
||||
if tx := p.txpool.Get(queue[count]); tx != nil {
|
||||
tx := p.txpool.Get(queue[count])
|
||||
|
||||
// Skip EIP-4337 bundled transactions
|
||||
if tx != nil && tx.GetOptions() == nil {
|
||||
pending = append(pending, queue[count])
|
||||
pendingTypes = append(pendingTypes, tx.Type())
|
||||
pendingSizes = append(pendingSizes, uint32(tx.Size()))
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
// GetRootHash returns root hash for given start and end block
|
||||
|
|
@ -52,16 +54,53 @@ func (s *BlockChainAPI) appendRPCMarshalBorTransaction(ctx context.Context, bloc
|
|||
return fields
|
||||
}
|
||||
|
||||
// EthereumAPI provides an API to access Ethereum related information.
|
||||
// BorAPI provides an API to access Bor related information.
|
||||
type BorAPI struct {
|
||||
b Backend
|
||||
}
|
||||
|
||||
// NewEthereumAPI creates a new Ethereum protocol API.
|
||||
// NewBorAPI creates a new Bor protocol API.
|
||||
func NewBorAPI(b Backend) *BorAPI {
|
||||
return &BorAPI{b}
|
||||
}
|
||||
|
||||
// SendRawTransactionConditional will add the signed transaction to the transaction pool.
|
||||
// The sender/bundler is responsible for signing the transaction
|
||||
func (api *BorAPI) SendRawTransactionConditional(ctx context.Context, input hexutil.Bytes, options types.OptionsAA4337) (common.Hash, error) {
|
||||
tx := new(types.Transaction)
|
||||
if err := tx.UnmarshalBinary(input); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
|
||||
currentHeader := api.b.CurrentHeader()
|
||||
currentState, _, _ := api.b.StateAndHeaderByNumber(ctx, rpc.BlockNumber(currentHeader.Number.Int64()))
|
||||
|
||||
// check block number range
|
||||
if err := currentHeader.ValidateBlockNumberOptions4337(options.BlockNumberMin, options.BlockNumberMax); err != nil {
|
||||
return common.Hash{}, &rpc.OptionsValidateError{Message: "out of block range. err: " + err.Error()}
|
||||
}
|
||||
|
||||
// check timestamp range
|
||||
if err := currentHeader.ValidateTimestampOptions4337(options.TimestampMin, options.TimestampMax); err != nil {
|
||||
return common.Hash{}, &rpc.OptionsValidateError{Message: "out of time range. err: " + err.Error()}
|
||||
}
|
||||
|
||||
// check knownAccounts length (number of slots/accounts) should be less than 1000
|
||||
if err := options.KnownAccounts.ValidateLength(); err != nil {
|
||||
return common.Hash{}, &rpc.KnownAccountsLimitExceededError{Message: "limit exceeded. err: " + err.Error()}
|
||||
}
|
||||
|
||||
// check knownAccounts
|
||||
if err := currentState.ValidateKnownAccounts(options.KnownAccounts); err != nil {
|
||||
return common.Hash{}, &rpc.OptionsValidateError{Message: "storage error. err: " + err.Error()}
|
||||
}
|
||||
|
||||
// put options data in Tx, to use it later while block building
|
||||
tx.PutOptions(&options)
|
||||
|
||||
return SubmitTransaction(ctx, api.b, tx)
|
||||
}
|
||||
|
||||
func (api *BorAPI) GetVoteOnHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64, hash string, milestoneId string) (bool, error) {
|
||||
return api.b.GetVoteOnHash(ctx, starBlockNr, endBlockNr, hash, milestoneId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5383,6 +5383,13 @@ var methods = function () {
|
|||
inputFormatter: [null]
|
||||
});
|
||||
|
||||
var sendRawTransactionConditional = new Method({
|
||||
name: 'sendRawTransactionConditional',
|
||||
call: 'eth_sendRawTransactionConditional',
|
||||
params: 2,
|
||||
inputFormatter: [null]
|
||||
});
|
||||
|
||||
var sendTransaction = new Method({
|
||||
name: 'sendTransaction',
|
||||
call: 'eth_sendTransaction',
|
||||
|
|
@ -5471,6 +5478,7 @@ var methods = function () {
|
|||
call,
|
||||
estimateGas,
|
||||
sendRawTransaction,
|
||||
sendRawTransactionConditional,
|
||||
signTransaction,
|
||||
sendTransaction,
|
||||
sign,
|
||||
|
|
|
|||
|
|
@ -65,6 +65,12 @@ web3._extend({
|
|||
call: 'bor_getVoteOnHash',
|
||||
params: 4,
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'sendRawTransactionConditional',
|
||||
call: 'bor_sendRawTransactionConditional',
|
||||
params: 2,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
]
|
||||
});
|
||||
`
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ type mockBackend struct {
|
|||
txPool *txpool.TxPool
|
||||
}
|
||||
|
||||
// PeerCount implements Backend.
|
||||
// PeerCount implements mockBackend.
|
||||
func (*mockBackend) PeerCount() int {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,6 +104,10 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even
|
|||
return miner
|
||||
}
|
||||
|
||||
func (miner *Miner) GetWorker() *worker {
|
||||
return miner.worker
|
||||
}
|
||||
|
||||
// update keeps track of the downloader events. Please be aware that this is a one shot type of update loop.
|
||||
// It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and
|
||||
// the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks
|
||||
|
|
@ -191,7 +195,7 @@ func (miner *Miner) Close() {
|
|||
}
|
||||
|
||||
func (miner *Miner) Mining() bool {
|
||||
return miner.worker.isRunning()
|
||||
return miner.worker.IsRunning()
|
||||
}
|
||||
|
||||
func (miner *Miner) Hashrate() uint64 {
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ type testWorkerBackend struct {
|
|||
uncleBlock *types.Block
|
||||
}
|
||||
|
||||
// PeerCount implements Backend.
|
||||
// PeerCount implements testWorkerBackend.
|
||||
func (*testWorkerBackend) PeerCount() int {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
|
@ -397,7 +397,7 @@ func (w *worker) mainLoopWithDelay(ctx context.Context, delay uint, opcodeDelay
|
|||
// If our sealing block contains less than 2 uncle blocks,
|
||||
// add the new uncle block if valid and regenerate a new
|
||||
// sealing block for higher profit.
|
||||
if w.isRunning() && w.current != nil && len(w.current.uncles) < 2 {
|
||||
if w.IsRunning() && w.current != nil && len(w.current.uncles) < 2 {
|
||||
start := time.Now()
|
||||
if err := w.commitUncle(w.current, ev.Block.Header()); err == nil {
|
||||
commitErr := w.commit(ctx, w.current.copy(), nil, true, start)
|
||||
|
|
@ -428,7 +428,7 @@ func (w *worker) mainLoopWithDelay(ctx context.Context, delay uint, opcodeDelay
|
|||
// already included in the current sealing block. These transactions will
|
||||
// be automatically eliminated.
|
||||
// nolint : nestif
|
||||
if !w.isRunning() && w.current != nil {
|
||||
if !w.IsRunning() && w.current != nil {
|
||||
// If block is already full, abort
|
||||
if gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas {
|
||||
continue
|
||||
|
|
@ -486,7 +486,7 @@ func (w *worker) commitWorkWithDelay(ctx context.Context, interrupt *int32, noem
|
|||
tracing.Exec(ctx, "", "worker.prepareWork", func(ctx context.Context, span trace.Span) {
|
||||
// Set the coinbase if the worker is running or it's required
|
||||
var coinbase common.Address
|
||||
if w.isRunning() {
|
||||
if w.IsRunning() {
|
||||
if w.coinbase == (common.Address{}) {
|
||||
log.Error("Refusing to mine without etherbase")
|
||||
return
|
||||
|
|
@ -873,7 +873,7 @@ mainloop:
|
|||
}
|
||||
}
|
||||
|
||||
if !w.isRunning() && len(coalescedLogs) > 0 {
|
||||
if !w.IsRunning() && len(coalescedLogs) > 0 {
|
||||
// We don't push the pendingLogsEvent while we are sealing. The reason is that
|
||||
// when we are sealing, the worker will regenerate a sealing block every 3 seconds.
|
||||
// In order to avoid pushing the repeated pendingLog, we disable the pending log pushing.
|
||||
|
|
|
|||
|
|
@ -487,7 +487,7 @@ func (w *worker) stop() {
|
|||
}
|
||||
|
||||
// isRunning returns an indicator whether worker is running or not.
|
||||
func (w *worker) isRunning() bool {
|
||||
func (w *worker) IsRunning() bool {
|
||||
return w.running.Load()
|
||||
}
|
||||
|
||||
|
|
@ -589,7 +589,7 @@ func (w *worker) newWorkLoop(ctx context.Context, recommit time.Duration) {
|
|||
case <-timer.C:
|
||||
// If sealing is running resubmit a new work cycle periodically to pull in
|
||||
// higher priced transactions. Disable this overhead for pending blocks.
|
||||
if w.isRunning() && (w.chainConfig.Clique == nil || w.chainConfig.Clique.Period > 0) {
|
||||
if w.IsRunning() && (w.chainConfig.Clique == nil || w.chainConfig.Clique.Period > 0) {
|
||||
// Short circuit if no new transaction arrives.
|
||||
if w.newTxs.Load() == 0 {
|
||||
timer.Reset(recommit)
|
||||
|
|
@ -691,7 +691,7 @@ func (w *worker) mainLoop(ctx context.Context) {
|
|||
}
|
||||
// If our mining block contains less than 2 uncle blocks,
|
||||
// add the new uncle block if valid and regenerate a mining block.
|
||||
if w.isRunning() && w.current != nil && len(w.current.uncles) < 2 {
|
||||
if w.IsRunning() && w.current != nil && len(w.current.uncles) < 2 {
|
||||
start := time.Now()
|
||||
if err := w.commitUncle(w.current, ev.Block.Header()); err == nil {
|
||||
commitErr := w.commit(ctx, w.current.copy(), nil, true, start)
|
||||
|
|
@ -721,7 +721,8 @@ func (w *worker) mainLoop(ctx context.Context) {
|
|||
// Note all transactions received may not be continuous with transactions
|
||||
// already included in the current sealing block. These transactions will
|
||||
// be automatically eliminated.
|
||||
if !w.isRunning() && w.current != nil {
|
||||
// nolint : nestif
|
||||
if !w.IsRunning() && w.current != nil {
|
||||
// If block is already full, abort
|
||||
if gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas {
|
||||
continue
|
||||
|
|
@ -1152,6 +1153,31 @@ mainloop:
|
|||
// during transaction acceptance is the transaction pool.
|
||||
from, _ := types.Sender(env.signer, tx)
|
||||
|
||||
// not prioritising conditional transaction, yet.
|
||||
//nolint:nestif
|
||||
if options := tx.GetOptions(); options != nil {
|
||||
if err := env.header.ValidateBlockNumberOptions4337(options.BlockNumberMin, options.BlockNumberMax); err != nil {
|
||||
log.Trace("Dropping conditional transaction", "from", from, "hash", tx.Hash(), "reason", err)
|
||||
txs.Pop()
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err := env.header.ValidateTimestampOptions4337(options.TimestampMin, options.TimestampMax); err != nil {
|
||||
log.Trace("Dropping conditional transaction", "from", from, "hash", tx.Hash(), "reason", err)
|
||||
txs.Pop()
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err := env.state.ValidateKnownAccounts(options.KnownAccounts); err != nil {
|
||||
log.Trace("Dropping conditional transaction", "from", from, "hash", tx.Hash(), "reason", err)
|
||||
txs.Pop()
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the tx is replay protected. If we're not in the EIP155 hf
|
||||
// phase, start ignoring the sender until we do.
|
||||
if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) {
|
||||
|
|
@ -1217,7 +1243,7 @@ mainloop:
|
|||
}
|
||||
|
||||
// nolint:nestif
|
||||
if EnableMVHashMap && w.isRunning() {
|
||||
if EnableMVHashMap && w.IsRunning() {
|
||||
close(chDeps)
|
||||
depsWg.Wait()
|
||||
|
||||
|
|
@ -1278,7 +1304,7 @@ mainloop:
|
|||
|
||||
}
|
||||
|
||||
if !w.isRunning() && len(coalescedLogs) > 0 {
|
||||
if !w.IsRunning() && len(coalescedLogs) > 0 {
|
||||
// We don't push the pendingLogsEvent while we are sealing. The reason is that
|
||||
// when we are sealing, the worker will regenerate a sealing block every 3 seconds.
|
||||
// In order to avoid pushing the repeated pendingLog, we disable the pending log pushing.
|
||||
|
|
@ -1692,7 +1718,7 @@ func (w *worker) commitWork(ctx context.Context, interrupt *atomic.Int32, noempt
|
|||
tracing.Exec(ctx, "", "worker.prepareWork", func(ctx context.Context, span trace.Span) {
|
||||
// Set the coinbase if the worker is running or it's required
|
||||
var coinbase common.Address
|
||||
if w.isRunning() {
|
||||
if w.IsRunning() {
|
||||
coinbase = w.etherbase()
|
||||
if coinbase == (common.Address{}) {
|
||||
log.Error("Refusing to mine without etherbase")
|
||||
|
|
@ -1807,7 +1833,7 @@ func getInterruptTimer(ctx context.Context, work *environment, current *types.Bl
|
|||
// Note the assumption is held that the mutation is allowed to the passed env, do
|
||||
// the deep copy first.
|
||||
func (w *worker) commit(ctx context.Context, env *environment, interval func(), update bool, start time.Time) error {
|
||||
if w.isRunning() {
|
||||
if w.IsRunning() {
|
||||
ctx, span := tracing.StartSpan(ctx, "commit")
|
||||
defer tracing.EndSpan(span)
|
||||
|
||||
|
|
|
|||
|
|
@ -133,3 +133,15 @@ type CustomError struct {
|
|||
func (e *CustomError) ErrorCode() int { return e.Code }
|
||||
|
||||
func (e *CustomError) Error() string { return e.ValidationError }
|
||||
|
||||
type OptionsValidateError struct{ Message string }
|
||||
|
||||
func (e *OptionsValidateError) ErrorCode() int { return -32003 }
|
||||
|
||||
func (e *OptionsValidateError) Error() string { return e.Message }
|
||||
|
||||
type KnownAccountsLimitExceededError struct{ Message string }
|
||||
|
||||
func (e *KnownAccountsLimitExceededError) ErrorCode() int { return -32005 }
|
||||
|
||||
func (e *KnownAccountsLimitExceededError) Error() string { return e.Message }
|
||||
|
|
|
|||
|
|
@ -375,6 +375,7 @@ func (h *handler) startCallProc(fn func(*callProc)) {
|
|||
h.executionPool.Submit(context.Background(), func() error {
|
||||
defer h.callWG.Done()
|
||||
defer cancel()
|
||||
|
||||
fn(&callProc{ctx: ctx})
|
||||
|
||||
h.executionPool.processed.Add(1)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ func (s *Server) ServeListener(l net.Listener) error {
|
|||
conn, err := l.Accept()
|
||||
if netutil.IsTemporaryError(err) {
|
||||
log.Warn("RPC accept error", "err", err)
|
||||
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
|
|
|
|||
Loading…
Reference in a new issue