Skip to content

Commit

Permalink
Merge branch 'l1sload' of github.com:scroll-tech/go-ethereum into l1s…
Browse files Browse the repository at this point in the history
…load-error-handle
  • Loading branch information
NazariiDenha committed Jul 15, 2024
2 parents a306ca2 + bb3fa97 commit aac7d47
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 18 deletions.
35 changes: 18 additions & 17 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -1167,8 +1167,15 @@ type l1sload struct {
l1Client L1Client
}

// RequiredGas returns the gas required to execute the pre-compiled contract.
// FIXED_GAS_COST + (number of storage slots) * PER_LOAD_GAS_COST
// input has format of 20-byte contract address + some number of 32-byte slots, so (len(input) / 32) is number of storage slots
func (c *l1sload) RequiredGas(input []byte) uint64 {
return 4000
numStorageSlots := len(input) / 32
if params.L1SloadMaxNumStorageSlots < numStorageSlots {
numStorageSlots = params.L1SloadMaxNumStorageSlots
}
return params.L1SloadBaseGas + uint64(numStorageSlots)*params.L1SloadPerLoadGas
}

func (c *l1sload) Run(state StateDB, input []byte) ([]byte, error) {
Expand All @@ -1178,31 +1185,25 @@ func (c *l1sload) Run(state StateDB, input []byte) ([]byte, error) {
log.Error("No L1Client in the l1sload")
return nil, ErrNoL1Client
}
if len(input) != 84 {
// verify that the input has 20-byte contract address and 1 to MAX_NUM_STORAGE_SLOTS 32-byte storage slots
numStorageSlots := len(input) / 32
if numStorageSlots < 1 || numStorageSlots > params.L1SloadMaxNumStorageSlots || len(input) != 20+32*numStorageSlots {
return nil, ErrInvalidInput
}

latestL1BlockNumberOnL2 := state.GetState(rcfg.L1BlocksAddress, rcfg.LatestBlockNumberSlot).Big().Uint64()

block := new(big.Int)
block.SetBytes(input[:32])
// load latest l1 block number known on l2
block := state.GetState(rcfg.L1BlocksAddress, rcfg.LatestBlockNumberSlot).Big()

if block.Uint64() > latestL1BlockNumberOnL2 {
log.Error("L1 block number too big", "blockNum", block.Uint64(), "latestL1BlockNumberOnL2", latestL1BlockNumberOnL2)
return nil, ErrInvalidL1BlockNumber
address := common.BytesToAddress(input[0:20])
keys := make([]common.Hash, numStorageSlots)
for i := range keys {
keys[i] = common.BytesToHash(input[20+32*i : 52+32*i])
}
if block.Uint64() <= latestL1BlockNumberOnL2-uint64(rcfg.L1BlockBufferSize) {
log.Error("L1 block number too small", "blockNum", block.Uint64(), "latestL1BlockNumberOnL2", latestL1BlockNumberOnL2)
return nil, ErrInvalidL1BlockNumber
}

address := common.BytesToAddress(input[32:52])
key := common.BytesToHash(input[52:84])

var res []byte
var err error
for i := 0; i < l1ClientRequestAttempts; i++ {
res, err = c.l1Client.StorageAt(context.Background(), address, key, block)
res, err = c.l1Client.StoragesAt(context.Background(), address, keys, block)
if err != nil {
continue
} else {
Expand Down
2 changes: 1 addition & 1 deletion core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (

// L1Client provides functionality provided by L1
type L1Client interface {
StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error)
StoragesAt(ctx context.Context, account common.Address, keys []common.Hash, blockNumber *big.Int) ([]byte, error)
}

// Config are the configuration options for the Interpreter
Expand Down
25 changes: 25 additions & 0 deletions ethclient/ethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,31 @@ func (ec *Client) StorageAt(ctx context.Context, account common.Address, key com
return result, err
}

// StoragesAt returns the values of keys in the contract storage of the given account.
// The block number can be nil, in which case the value is taken from the latest known block.
func (ec *Client) StoragesAt(ctx context.Context, account common.Address, keys []common.Hash, blockNumber *big.Int) ([]byte, error) {
results := make([]hexutil.Bytes, len(keys))
reqs := make([]rpc.BatchElem, len(keys))
for i := range reqs {
reqs[i] = rpc.BatchElem{
Method: "eth_getStorageAt",
Args: []interface{}{account, keys[i], toBlockNumArg(blockNumber)},
Result: &results[i],
}
}
if err := ec.c.BatchCallContext(ctx, reqs); err != nil {
return nil, err
}
output := make([]byte, 32*len(keys))
for i := range reqs {
if reqs[i].Error != nil {
return nil, reqs[i].Error
}
copy(output[i*32:], results[i])
}
return output, nil
}

// CodeAt returns the contract code of the given account.
// The block number can be nil, in which case the code is taken from the latest known block.
func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) {
Expand Down
11 changes: 11 additions & 0 deletions ethclient/ethclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,17 @@ func testAtFunctions(t *testing.T, client *rpc.Client) {
if !bytes.Equal(storage, penStorage) {
t.Fatalf("unexpected storage: %v %v", storage, penStorage)
}
// StoragesAt
storages, err := ec.StoragesAt(context.Background(), testAddr, []common.Hash{{}, {}}, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !bytes.Equal(storages[0:32], penStorage) {
t.Fatalf("unexpected storage: %v %v", storages[0:32], penStorage)
}
if !bytes.Equal(storages[32:64], penStorage) {
t.Fatalf("unexpected storage: %v %v", storages[32:64], penStorage)
}
// CodeAt
code, err := ec.CodeAt(context.Background(), testAddr, nil)
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ const (
Bls12381MapG1Gas uint64 = 5500 // Gas price for BLS12-381 mapping field element to G1 operation
Bls12381MapG2Gas uint64 = 110000 // Gas price for BLS12-381 mapping field element to G2 operation

L1SloadBaseGas uint64 = 2000 // Base price for L1Sload
L1SloadPerLoadGas uint64 = 2000 // Per-load price for loading one storage slot

// The Refund Quotient is the cap on how much of the used gas can be refunded. Before EIP-3529,
// up to half the consumed gas could be refunded. Redefined as 1/5th in EIP-3529
RefundQuotient uint64 = 2
Expand All @@ -165,6 +168,8 @@ const (
BlobTxBlobGaspriceUpdateFraction = 3338477 // Controls the maximum rate of change for blob gas price

BlobTxTargetBlobGasPerBlock = 3 * BlobTxBlobGasPerBlob // Target consumable blob gas for data blobs per block (for 1559-like pricing)

L1SloadMaxNumStorageSlots = 5 // Max number of storage slots requested in L1Sload precompile
)

// Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations
Expand Down
4 changes: 4 additions & 0 deletions rollup/rollup_sync_service/l1client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,7 @@ func (m *mockEthClient) BlockByHash(ctx context.Context, hash common.Hash) (*typ
func (m *mockEthClient) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
return nil, nil
}

func (m *mockEthClient) StoragesAt(ctx context.Context, account common.Address, keys []common.Hash, blockNumber *big.Int) ([]byte, error) {
return nil, nil
}
1 change: 1 addition & 0 deletions rollup/sync_service/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ type EthClient interface {
TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error)
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error)
StoragesAt(ctx context.Context, account common.Address, keys []common.Hash, blockNumber *big.Int) ([]byte, error)
}

0 comments on commit aac7d47

Please sign in to comment.