Skip to content

Commit

Permalink
Merge pull request #23 from EspressoSystems/fix/justification
Browse files Browse the repository at this point in the history
Fixes after Justification changes
  • Loading branch information
jbearer authored Aug 11, 2023
2 parents 7260def + 824985a commit c0aa5ad
Show file tree
Hide file tree
Showing 17 changed files with 170 additions and 50 deletions.
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
COMPOSEFLAGS=-d
ITESTS_L2_HOST=http://localhost:9545
BEDROCK_TAGS_REMOTE?=origin
monorepo-base := $(realpath .)

build: build-go build-ts
.PHONY: build
Expand Down Expand Up @@ -28,6 +29,13 @@ op-bindings:
make -C ./op-bindings
.PHONY: op-bindings

make op-bindings-docker:
docker run -v $(monorepo-base):/work -it us-docker.pkg.dev/oplabs-tools-artifacts/images/ci-builder bash -c "env FORGE_BUILD_ARGS='--force' make -C /work op-bindings"
echo "Asking for root permissions to set owner of files to ${USER} after docker run"
sudo chown -R ${USER} $(monorepo-base)

.PHONY: op-bindings-docker

op-node:
make -C ./op-node op-node
.PHONY: op-node
Expand Down
27 changes: 22 additions & 5 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@
description = "A Nix-flake-based Go 1.17 development environment";

inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# Be consistent with CI, which uses an older version of geth.
inputs.nixpkgs-geth.url = "github:NixOS/nixpkgs/611bf8f183e6360c2a215fa70dfd659943a9857f";
inputs.flake-utils.url = "github:numtide/flake-utils";
inputs.flake-compat.url = "github:edolstra/flake-compat";
inputs.flake-compat.flake = false;

inputs.foundry.url = "github:shazow/foundry.nix/monthly"; # Use monthly branch for permanent releases
# Closes commit in foundry.nix to forge 3b1129b used in CI.
inputs.foundry.url = "github:shazow/foundry.nix/fef36a77f0838fe278cc01ccbafbab8cd38ad26f";

outputs = { self, flake-utils, nixpkgs, foundry, ... }:

outputs = { self, flake-utils, nixpkgs, nixpkgs-geth, foundry, ... }:
let
goVersion = 19; # Change this to update the whole stack
overlays = [
Expand All @@ -19,6 +23,7 @@
nodejs = prev.nodejs-16_x;
pnpm = prev.nodePackages.pnpm;
yarn = prev.nodePackages.yarn;
go-ethereum = nixpkgs-geth.legacyPackages.${prev.system}.go-ethereum;
})
foundry.overlay
];
Expand Down
4 changes: 2 additions & 2 deletions op-bindings/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SHELL := /bin/bash
SHELL := /usr/bin/env bash

pkg := bindings
monorepo-base := $(shell dirname $(realpath .))
Expand All @@ -12,7 +12,7 @@ version:

compile:
cd $(contracts-dir) && \
pnpm build
forge build ${FORGE_BUILD_ARGS}

bindings: compile bindings-build

Expand Down
28 changes: 14 additions & 14 deletions op-bindings/bindings/l1block.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion op-bindings/bindings/l1block_more.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions op-node/rollup/derive/fuzz_parsers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func FuzzL1InfoAgainstContract(f *testing.F) {
BatcherAddr: common.BytesToAddress(batcherHash),
L1FeeOverhead: eth.Bytes32(common.BytesToHash(l1FeeOverhead)),
L1FeeScalar: eth.Bytes32(common.BytesToHash(l1FeeScalar)),
Justification: nil,
}

// Setup opts
Expand All @@ -100,6 +101,9 @@ func FuzzL1InfoAgainstContract(f *testing.F) {
common.BytesToAddress(batcherHash).Hash(),
common.BytesToHash(l1FeeOverhead).Big(),
common.BytesToHash(l1FeeScalar).Big(),
// Since we set `Justification: nil`, the RLP encoded bytes will encode an empty list.
// This is encoded by `c0` to signify a list followed by no elements.
[]byte{0xc0},
)
if err != nil {
t.Fatalf("Failed to create the transaction: %v", err)
Expand Down
53 changes: 41 additions & 12 deletions op-node/rollup/derive/l1_block_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"errors"
"fmt"
"io"
"math/big"

"github.com/ethereum/go-ethereum/common"
Expand All @@ -18,14 +17,15 @@ import (
)

const (
L1InfoFuncSignature = "setL1BlockValues(uint64,uint64,uint256,bytes32,uint64,bytes32,uint256,uint256)"
L1InfoFuncSignature = "setL1BlockValues(uint64,uint64,uint256,bytes32,uint64,bytes32,uint256,uint256,bytes)"
L1InfoArguments = 8
)

var (
L1InfoFuncBytes4 = crypto.Keccak256([]byte(L1InfoFuncSignature))[:4]
L1InfoDepositerAddress = common.HexToAddress("0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001")
L1BlockAddress = predeploys.L1BlockAddr
L1InfoFuncBytes4 = crypto.Keccak256([]byte(L1InfoFuncSignature))[:4]
L1InfoDepositerAddress = common.HexToAddress("0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001")
L1InfoJustificationOffset = new(big.Int).SetUint64(288) // See Binary Format table below
L1BlockAddress = predeploys.L1BlockAddr
)

const (
Expand Down Expand Up @@ -61,6 +61,9 @@ type L1BlockInfo struct {
// | 32 | BatcherAddr |
// | 32 | L1FeeOverhead |
// | 32 | L1FeeScalar |
// | 32 | L1InfoJustificationOffset|
// | | (this is how dynamic |
// | | types are ABI encoded) |
// | variable| Justification |
// +---------+--------------------------+

Expand Down Expand Up @@ -93,9 +96,28 @@ func (info *L1BlockInfo) MarshalBinary() ([]byte, error) {
if err := solabi.WriteEthBytes32(w, info.L1FeeScalar); err != nil {
return nil, err
}
if err := rlp.Encode(w, info.Justification); err != nil {

// For simplicity, we don't ABI-encode the whole structure of the Justification. We RLP-encode
// it and then ABI-encode the resulting byte string. This means the Justification can be
// accessed by parsing calldata, but cannot (easily) by inspected on-chain.
rlpBytes, err := rlp.EncodeToBytes(info.Justification)
if err != nil {
return nil, err
}
// The ABI-encoding of function parameters is that of a tuple, which requires that dynamic types
// (such as `bytes`) are represented in the initial list of items as a uint256 with the offset
// from the start of the encoding to the start of the payload of the dynamic type, which follows
// the initial list of static types and dynamic type offsets. In this case, we only have one
// item of dynamic type, and it is at the end of the list of items, so we will encode it by its
// offset, which is just the length of the static section of the list, followed by the item
// itself.
if err := solabi.WriteUint256(w, L1InfoJustificationOffset); err != nil {
return nil, err
}
if err := solabi.WriteBytes(w, rlpBytes); err != nil {
return nil, err
}

return w.Bytes(), nil
}

Expand Down Expand Up @@ -131,15 +153,22 @@ func (info *L1BlockInfo) UnmarshalBinary(data []byte) error {
return err
}

// If the remaining bytes are the RLP encoding of an empty list (which represents a `nil`
// pointer) skip the Justification. The RLP library automatically handles `nil` pointers as
// struct fields with the `rlp:"nil"` attribute, but here it is not a nested field which might
// be `nil` but the top-level object, and the RLP library does not allow that.
rlpBytes, err := io.ReadAll(reader)
// Read the offset of the Justification bytes followed by the bytes themselves.
rlpOffset, err := solabi.ReadUint256(reader)
if err != nil {
return err
}
if rlpOffset.Cmp(L1InfoJustificationOffset) != 0 {
return fmt.Errorf("invalid justification offset (%d, expected %d)", rlpOffset, L1InfoJustificationOffset)
}
rlpBytes, err := solabi.ReadBytes(reader)
if err != nil {
return err
}
// If not RLP encoding of empty list...
// If the remaining bytes are the RLP encoding of an empty list (0xc, which represents a `nil`
// pointer) skip the Justification. The RLP library automatically handles `nil` pointers as
// struct fields with the `rlp:"nil"` attribute, but here it is not a nested field which might
// be `nil` but the top-level object, and the RLP library does not allow that.
if !(len(rlpBytes) == 1 && rlpBytes[0] == 0xc0) {
if err := rlp.DecodeBytes(rlpBytes, &info.Justification); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion op-node/rollup/derive/l1_block_info_tob_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func FuzzParseL1InfoDepositTxDataValid(f *testing.F) {
require.Equal(t, res.BatcherAddr, sysCfg.BatcherAddr)
require.Equal(t, res.L1FeeOverhead, sysCfg.Overhead)
require.Equal(t, res.L1FeeScalar, sysCfg.Scalar)
require.Equal(t, res.Justification, justification)
require.Equal(t, res.Justification, &justification)
})
}

Expand Down
51 changes: 51 additions & 0 deletions op-service/solabi/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,33 @@ func ReadUint256(r io.Reader) (*big.Int, error) {
return new(big.Int).SetBytes(n[:]), nil
}

func ReadBytes(r io.Reader) ([]byte, error) {
// First read the length.
dataLen, err := ReadUint256(r)
if err != nil {
return nil, err
}
// Validate length.
if !dataLen.IsUint64() {
return nil, fmt.Errorf("bytes array is too long: %d", dataLen)
}
bufLen := dataLen.Uint64()

// Read the payload.
b := make([]byte, bufLen)
if _, err := io.ReadFull(r, b); err != nil {
return nil, err
}
// Consume extra padding bytes if necessary.
if paddingLen := bytesPadding(bufLen); paddingLen != 0 {
padding := make([]byte, paddingLen)
if _, err := io.ReadFull(r, padding); err != nil {
return nil, err
}
}
return b, nil
}

func EmptyReader(r io.Reader) bool {
var t [1]byte
n, err := r.Read(t[:])
Expand Down Expand Up @@ -132,3 +159,27 @@ func WriteUint64(w io.Writer, n uint64) error {
}
return nil
}

func WriteBytes(w io.Writer, b []byte) error {
// The ABI encoding for a dynamic byte array consists of the length of the array, encoded as a
// uint256, followed by the array contents, followed by zeros padding to a multiple of 32 bytes.
dataLen := uint64(len(b))
if err := WriteUint256(w, new(big.Int).SetUint64(dataLen)); err != nil {
return err
}
if _, err := w.Write(b); err != nil {
return err
}
// Figure out how many bytes of padding we need.
if paddingLen := bytesPadding(dataLen); paddingLen != 0 {
padding := make([]byte, paddingLen)
if _, err := w.Write(padding); err != nil {
return err
}
}
return nil
}

func bytesPadding(dataLen uint64) uint64 {
return (32 - (dataLen % 32)) % 32
}
Loading

0 comments on commit c0aa5ad

Please sign in to comment.