Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a new RPC endpoint (bor_sendRawTransactionConditional) to support EIP-4337 Bundled Transactions #8229

Merged
merged 22 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
883ed31
initial commit to implement bor_sendRawTransactionConditional
pratikspatil024 Sep 15, 2023
b6b5a38
addressed comments
pratikspatil024 Sep 18, 2023
74e53a4
removed duplicate line
pratikspatil024 Sep 19, 2023
d257b26
fix go.mod & go.sum
pratikspatil024 Oct 3, 2023
6c446e8
added func HexToRefHash() in erigon-lib
pratikspatil024 Oct 3, 2023
0a45d29
small fix
pratikspatil024 Oct 3, 2023
7d77722
made optionsAA4337 public
pratikspatil024 Oct 3, 2023
8a91bea
added check to check nil account data
pratikspatil024 Oct 3, 2023
cc8ddac
renamed OptionsAA4337 to TransactionConditions
pratikspatil024 Oct 3, 2023
afbfb8b
renamed KnownAccounts to KnownAccountStorageConditions and Value to K…
pratikspatil024 Oct 3, 2023
0326018
renaming of Single/Storage and relevent functions
pratikspatil024 Oct 3, 2023
f67a504
inlined 2 constants
pratikspatil024 Oct 3, 2023
75f7622
moved ErrKnownAccounts to func UnmarshalJSON
pratikspatil024 Oct 3, 2023
f6d97dd
separate length calculation and validation logic of KnownAccountStora…
pratikspatil024 Oct 3, 2023
220acfd
moved errors to a better place
pratikspatil024 Oct 3, 2023
3d28edd
moved transaction_conditional to erigon/types
pratikspatil024 Oct 3, 2023
cc63de0
generalised validate block range and timestamp range functions
pratikspatil024 Oct 4, 2023
254e288
refactored the 4 validation functions into 1
pratikspatil024 Oct 4, 2023
c095f93
made InsertKnownAccounts private for test
pratikspatil024 Oct 5, 2023
5ae1dd4
meaningful variable names
pratikspatil024 Oct 5, 2023
a6a66ad
moved tempAccount check outside switch
pratikspatil024 Oct 19, 2023
3c5c466
only checking options on the RPC level, removed unwanted code, will c…
pratikspatil024 Oct 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions core/state/intra_block_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/ledgerwatch/erigon-lib/chain"
libcommon "github.com/ledgerwatch/erigon-lib/common"
types2 "github.com/ledgerwatch/erigon-lib/types"
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/u256"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/types/accounts"
Expand Down Expand Up @@ -827,3 +828,46 @@ func (sdb *IntraBlockState) AddressInAccessList(addr libcommon.Address) bool {
func (sdb *IntraBlockState) SlotInAccessList(addr libcommon.Address, slot libcommon.Hash) (addressPresent bool, slotPresent bool) {
return sdb.accessList.Contains(addr, slot)
}

// ValidateKnownAccounts validates the knownAccounts passed in the options parameter in the conditional transaction (EIP-4337)
func (sdb *IntraBlockState) ValidateKnownAccounts(knownAccounts types2.KnownAccountStorageConditions) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be decoupled from IntraBlockState, see this thread:
#8229 (comment)

if knownAccounts == nil {
return nil
}

for address, condition := range knownAccounts {
tempAccount, err := sdb.stateReader.ReadAccountData(address)
if err != nil {
return fmt.Errorf("error reading account data at: %v", address)
}

if tempAccount == nil {
return fmt.Errorf("Storage Trie is nil for: %v", address)
}

// check if the value is hex string or an object
switch {
Comment on lines +848 to +849
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like now we could move the tempAccount check and else logic outside switch before it, is it ok for you?

if tempAccount == nil {
    return fmt.Errorf("Storage Trie is nil for: %v", address)
}

// check if the value is hex string or an object
switch {
...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

case condition.IsSingle():
if *condition.StorageRootHash != tempAccount.Root {
return fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", address, condition.StorageRootHash, tempAccount.Root)
}
case condition.IsStorage():
for slot, value := range condition.StorageSlotHashes {
slot := slot
tempByte, err := sdb.stateReader.ReadAccountStorage(address, tempAccount.Incarnation, &slot)
if err != nil {
return fmt.Errorf("error reading account storage at: %v slot: %v", address, slot)
}

actualValue := libcommon.BytesToHash(common.LeftPadBytes(tempByte, 32))
if value != actualValue {
return fmt.Errorf("invalid slot value at address: %v slot: %v value: %v actual value: %v", address, slot, value, actualValue)
}
}
default:
return fmt.Errorf("impossible to validate known accounts: %v", address)
}
}

return nil
}
74 changes: 74 additions & 0 deletions core/state/intra_block_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ import (
"github.com/holiman/uint256"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/kv/memdb"
types2 "github.com/ledgerwatch/erigon-lib/types"
"github.com/ledgerwatch/erigon/core/types"
"github.com/stretchr/testify/require"
)

func TestSnapshotRandom(t *testing.T) {
Expand Down Expand Up @@ -348,3 +350,75 @@ func TestTransientStorage(t *testing.T) {
t.Fatalf("transient storage mismatch: have %x, want %x", got, exp)
}
}

func insertKnownAccounts[T libcommon.Hash | map[libcommon.Hash]libcommon.Hash](accounts KnownAccountStorageConditions, k libcommon.Address, v T) {
switch typedV := any(v).(type) {
case libcommon.Hash:
accounts[k] = &KnownAccountStorageCondition{StorageRootHash: &typedV}
case map[libcommon.Hash]libcommon.Hash:
accounts[k] = &KnownAccountStorageCondition{StorageSlotHashes: typedV}
}
}

func TestValidateKnownAccounts(t *testing.T) {
t.Parallel()

knownAccounts := make(types2.KnownAccountStorageConditions)

insertKnownAccounts(knownAccounts, libcommon.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add1"), libcommon.HexToHash("0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1ce"))
insertKnownAccounts(knownAccounts, libcommon.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add2"), map[libcommon.Hash]libcommon.Hash{
libcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000aaa"): libcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000bbb"),
libcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ccc"): libcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ddd"),
})

stateobjaddr1 := libcommon.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add1")
stateobjaddr2 := libcommon.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add2")

storageaddr1 := libcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000zzz")
storageaddr21 := libcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000aaa")
storageaddr22 := libcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ccc")

data1 := libcommon.BytesToHash([]byte{24})
data21 := libcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000bbb")
data22 := libcommon.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))

insertKnownAccounts(knownAccounts, libcommon.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add2"), libcommon.HexToHash("0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1cf"))

stateobjaddr3 := libcommon.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add2")
storageaddr3 := libcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000yyy")
data3 := libcommon.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"
insertKnownAccounts(knownAccounts, libcommon.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add2"), libcommon.HexToHash("0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1ce"))
insertKnownAccounts(knownAccounts, libcommon.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add3"), map[libcommon.Hash]libcommon.Hash{
libcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000aaa"): libcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000bbb"),
libcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ccc"): libcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ddd"),
})

stateobjaddr4 := libcommon.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add3")
storageaddr41 := libcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000aaa")
storageaddr42 := libcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ccc")
data4 := libcommon.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")
}
164 changes: 164 additions & 0 deletions core/types/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,3 +512,167 @@ func TestCopyTxs(t *testing.T) {
copies := CopyTxs(txs)
assert.Equal(t, txs, copies)
}

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 := types2.BigIntIsWithinRange(test.header.Number, 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 := types2.BigIntIsWithinRange(test.header.Number, 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 := types2.Uint64IsWithinRange(&test.header.Time, 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 := types2.Uint64IsWithinRange(&test.header.Time, test.minTimestamp, test.maxTimestamp); err == nil {
t.Fatalf("test number %v should have failed. err is nil", test.number)
}
}
}
6 changes: 6 additions & 0 deletions erigon-lib/common/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,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(hexutility.FromHex(s)) }

func HexToRefHash(s string) *Hash {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to match the method above:

Suggested change
func HexToRefHash(s string) *Hash {
func HexToHashRef(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[:] }

Expand Down
Loading