diff --git a/arbitrator/jit/src/machine.rs b/arbitrator/jit/src/machine.rs index ed22e12ef9..f3cc939af0 100644 --- a/arbitrator/jit/src/machine.rs +++ b/arbitrator/jit/src/machine.rs @@ -107,6 +107,7 @@ pub fn create(opts: &Opts, env: WasmEnv) -> (Instance, FunctionEnv, Sto "github.com/offchainlabs/nitro/wavmio.getGlobalStateU64" => func!(wavmio::get_global_state_u64), "github.com/offchainlabs/nitro/wavmio.setGlobalStateU64" => func!(wavmio::set_global_state_u64), "github.com/offchainlabs/nitro/wavmio.readInboxMessage" => func!(wavmio::read_inbox_message), + "github.com/offchainlabs/nitro/wavmio.readHotShotCommitment" => func!(wavmio::read_hotshot_commitment), "github.com/offchainlabs/nitro/wavmio.readDelayedInboxMessage" => func!(wavmio::read_delayed_inbox_message), "github.com/offchainlabs/nitro/wavmio.resolvePreImage" => { #[allow(deprecated)] // we're just keeping this around until we no longer need to validate old replay binaries @@ -184,6 +185,7 @@ impl From for Escape { pub type WasmEnvMut<'a> = FunctionEnvMut<'a, WasmEnv>; pub type Inbox = BTreeMap>; +pub type HotShotCommitmentMap = BTreeMap; pub type Preimages = BTreeMap>>; #[derive(Default)] @@ -202,6 +204,8 @@ pub struct WasmEnv { pub preimages: Preimages, /// The sequencer inbox's messages pub sequencer_messages: Inbox, + /// Mapping from batch positions to hotshot commitments + pub hotshot_comm_map: HotShotCommitmentMap, /// The delayed inbox's messages pub delayed_messages: Inbox, /// The purpose and connections of this process diff --git a/arbitrator/jit/src/wavmio.rs b/arbitrator/jit/src/wavmio.rs index a398cb22f5..a26ec38e9f 100644 --- a/arbitrator/jit/src/wavmio.rs +++ b/arbitrator/jit/src/wavmio.rs @@ -3,7 +3,7 @@ use crate::{ gostack::GoStack, - machine::{Escape, Inbox, MaybeEscape, WasmEnv, WasmEnvMut}, + machine::{Escape, HotShotCommitmentMap, Inbox, MaybeEscape, WasmEnv, WasmEnvMut}, socket, }; @@ -85,6 +85,14 @@ pub fn set_global_state_u64(mut env: WasmEnvMut, sp: u32) -> MaybeEscape { Ok(()) } +pub fn read_hotshot_commitment(mut env: WasmEnvMut, sp: u32) -> MaybeEscape { + let (sp, env) = GoStack::new(sp, &mut env); + ready_hostio(env)?; + let hotshot_comms = &env.hotshot_comm_map; + + read_hotshot_commitment_impl(&sp, hotshot_comms, "wavmio.readHotShotCommitment") +} + pub fn read_inbox_message(mut env: WasmEnvMut, sp: u32) -> MaybeEscape { let (sp, env) = GoStack::new(sp, &mut env); ready_hostio(env)?; @@ -101,6 +109,32 @@ pub fn read_delayed_inbox_message(mut env: WasmEnvMut, sp: u32) -> MaybeEscape { inbox_message_impl(&sp, inbox, "wavmio.readDelayedInboxMessage") } +// Reads a hotshot commitment +fn read_hotshot_commitment_impl( + sp: &GoStack, + comm_map: &HotShotCommitmentMap, + name: &str, +) -> MaybeEscape { + let msg_num = sp.read_u64(0); + let out_ptr = sp.read_u64(1); + let out_len = sp.read_u64(2); + if out_len != 32 { + eprintln!("Go trying to read header bytes with out len {out_len} in {name}"); + sp.write_u64(5, 0); + return Ok(()); + } + + let message = comm_map.get(&msg_num).unwrap_or(&[0; 32]); + + if out_ptr + 32 > sp.memory_size() { + let text = format!("memory bounds exceeded in {}", name); + return Escape::hostio(&text); + } + sp.write_slice(out_ptr, message); + sp.write_u64(5, 32); + Ok(()) +} + /// Reads an inbox message /// note: the order of the checks is very important. fn inbox_message_impl(sp: &GoStack, inbox: &Inbox, name: &str) -> MaybeEscape { @@ -273,7 +307,9 @@ fn ready_hostio(env: &mut WasmEnv) -> MaybeEscape { while socket::read_u8(stream)? == socket::ANOTHER { let position = socket::read_u64(stream)?; let message = socket::read_bytes(stream)?; + let hotshot_comm = socket::read_bytes32(stream)?; env.sequencer_messages.insert(position, message); + env.hotshot_comm_map.insert(position, hotshot_comm); } while socket::read_u8(stream)? == socket::ANOTHER { let position = socket::read_u64(stream)?; diff --git a/arbitrator/prover/src/host.rs b/arbitrator/prover/src/host.rs index c66052ad54..6ecf142de8 100644 --- a/arbitrator/prover/src/host.rs +++ b/arbitrator/prover/src/host.rs @@ -52,6 +52,7 @@ pub enum Hostio { WavmReadKeccakPreimage, WavmReadSha256Preimage, WavmReadInboxMessage, + WavmReadHotShotCommitment, WavmReadDelayedInboxMessage, WavmHaltAndSetFinished, } @@ -76,6 +77,7 @@ impl FromStr for Hostio { ("env", "wavm_read_sha2_256_preimage") => WavmReadSha256Preimage, ("env", "wavm_read_inbox_message") => WavmReadInboxMessage, ("env", "wavm_read_delayed_inbox_message") => WavmReadDelayedInboxMessage, + ("env", "wavm_read_hotshot_header") => WavmReadHotShotCommitment, ("env", "wavm_halt_and_set_finished") => WavmHaltAndSetFinished, _ => bail!("no such hostio {} in {}", name.red(), module.red()), }) @@ -109,6 +111,7 @@ impl Hostio { WavmSetGlobalStateBytes32 => func!([I32, I32]), WavmGetGlobalStateU64 => func!([I32], [I64]), WavmSetGlobalStateU64 => func!([I32, I64]), + WavmReadHotShotCommitment => func!([I32, I32]), WavmReadKeccakPreimage => func!([I32, I32], [I32]), WavmReadSha256Preimage => func!([I32, I32], [I32]), WavmReadInboxMessage => func!([I64, I32, I32], [I32]), @@ -180,6 +183,11 @@ impl Hostio { opcode!(LocalGet, 1); opcode!(ReadPreImage, PreimageType::Sha2_256); } + WavmReadHotShotCommitment => { + // TODO implement for fault proofs + // https://github.com/EspressoSystems/espresso-sequencer/issues/670 + unimplemented!() + } WavmReadInboxMessage => { opcode!(LocalGet, 0); opcode!(LocalGet, 1); diff --git a/arbitrator/wasm-libraries/host-io/src/lib.rs b/arbitrator/wasm-libraries/host-io/src/lib.rs index e8f59994ac..bc830938f3 100644 --- a/arbitrator/wasm-libraries/host-io/src/lib.rs +++ b/arbitrator/wasm-libraries/host-io/src/lib.rs @@ -71,6 +71,15 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_wavmio_setGlobalState wavm_set_globalstate_u64(idx, sp.read_u64(1)); } +#[no_mangle] +pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_wavmio_readHotShotCommitment( + sp: GoStack, +) { + // TODO implement for fault proofs + // https://github.com/EspressoSystems/espresso-sequencer/issues/671 + return unimplemented!(); +} + #[no_mangle] pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_wavmio_readInboxMessage(sp: GoStack) { let msg_num = sp.read_u64(0); @@ -120,7 +129,9 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_wavmio_readDelayedInb } #[no_mangle] -pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_wavmio_resolveTypedPreimage(sp: GoStack) { +pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_wavmio_resolveTypedPreimage( + sp: GoStack, +) { let preimage_type = sp.read_u8(0); let hash_ptr = sp.read_u64(1); let hash_len = sp.read_u64(2); diff --git a/arbos/arbostypes/incomingmessage.go b/arbos/arbostypes/incomingmessage.go index 04ce8ebe2e..b0fdf73d39 100644 --- a/arbos/arbostypes/incomingmessage.go +++ b/arbos/arbostypes/incomingmessage.go @@ -15,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" + "github.com/offchainlabs/nitro/arbos/espresso" "github.com/offchainlabs/nitro/arbos/util" "github.com/offchainlabs/nitro/util/arbmath" ) @@ -34,13 +35,19 @@ const ( const MaxL2MessageSize = 256 * 1024 +type EspressoBlockJustification struct { + Header espresso.Header + Proof espresso.NmtProof +} + type L1IncomingMessageHeader struct { - Kind uint8 `json:"kind"` - Poster common.Address `json:"sender"` - BlockNumber uint64 `json:"blockNumber"` - Timestamp uint64 `json:"timestamp"` - RequestId *common.Hash `json:"requestId" rlp:"nilList"` - L1BaseFee *big.Int `json:"baseFeeL1"` + Kind uint8 `json:"kind"` + Poster common.Address `json:"sender"` + BlockNumber uint64 `json:"blockNumber"` + Timestamp uint64 `json:"timestamp"` + RequestId *common.Hash `json:"requestId" rlp:"nilList"` + L1BaseFee *big.Int `json:"baseFeeL1"` + BlockJustification *EspressoBlockJustification `json:"justification,omitempty" rlp:"optional"` } func (h L1IncomingMessageHeader) SeqNum() (uint64, error) { @@ -228,6 +235,7 @@ func ParseIncomingL1Message(rd io.Reader, batchFetcher FallibleBatchFetcher) (*L timestamp, &requestId, baseFeeL1.Big(), + nil, }, data, nil, diff --git a/arbos/block_processor.go b/arbos/block_processor.go index 6f87864b61..ff3baf0f2f 100644 --- a/arbos/block_processor.go +++ b/arbos/block_processor.go @@ -12,6 +12,7 @@ import ( "github.com/offchainlabs/nitro/arbos/arbosState" "github.com/offchainlabs/nitro/arbos/arbostypes" + "github.com/offchainlabs/nitro/arbos/espresso" "github.com/offchainlabs/nitro/arbos/l2pricing" "github.com/offchainlabs/nitro/arbos/util" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" @@ -129,6 +130,7 @@ func ProduceBlock( message *arbostypes.L1IncomingMessage, delayedMessagesRead uint64, lastBlockHeader *types.Header, + lastHotShotCommitment *espresso.Commitment, statedb *state.StateDB, chainContext core.ChainContext, chainConfig *params.ChainConfig, @@ -158,7 +160,7 @@ func ProduceBlock( hooks := NoopSequencingHooks() return ProduceBlockAdvanced( - message.Header, txes, delayedMessagesRead, lastBlockHeader, statedb, chainContext, chainConfig, hooks, + message.Header, txes, delayedMessagesRead, lastBlockHeader, lastHotShotCommitment, statedb, chainContext, chainConfig, hooks, ) } @@ -168,6 +170,7 @@ func ProduceBlockAdvanced( txes types.Transactions, delayedMessagesRead uint64, lastBlockHeader *types.Header, + lastHotShotCommitment *espresso.Commitment, statedb *state.StateDB, chainContext core.ChainContext, chainConfig *params.ChainConfig, @@ -191,6 +194,29 @@ func ProduceBlockAdvanced( l1Timestamp: l1Header.Timestamp, } + // Espresso-specific validation + // TODO test: https://github.com/EspressoSystems/espresso-sequencer/issues/772 + if chainConfig.Espresso { + jst := l1Header.BlockJustification + if jst == nil { + return nil, nil, errors.New("batch missing espresso justification") + + } + hotshotHeader := jst.Header + if *lastHotShotCommitment != hotshotHeader.Commit() { + return nil, nil, errors.New("invalid hotshot header") + } + var roots = []*espresso.NmtRoot{&hotshotHeader.TransactionsRoot} + var proofs = []*espresso.NmtProof{&l1Header.BlockJustification.Proof} + // If the validation function below were not mocked, we would need to serialize the transactions + // in the batch here. To avoid the unnecessary overhead, we provide an empty array instead. + var txs []espresso.Bytes + err := espresso.ValidateBatchTransactions(chainConfig.ChainID.Uint64(), roots, proofs, txs) + if err != nil { + return nil, nil, errors.New("failed to validate namespace proof)") + } + } + header := createNewHeader(lastBlockHeader, l1Info, state, chainConfig) signer := types.MakeSigner(chainConfig, header.Number, header.Time) // Note: blockGasLeft will diverge from the actual gas left during execution in the event of invalid txs, diff --git a/espresso/client.go b/arbos/espresso/client.go similarity index 100% rename from espresso/client.go rename to arbos/espresso/client.go diff --git a/espresso/commit.go b/arbos/espresso/commit.go similarity index 100% rename from espresso/commit.go rename to arbos/espresso/commit.go diff --git a/espresso/hotshot/hotshot.go b/arbos/espresso/hotshot/hotshot.go similarity index 100% rename from espresso/hotshot/hotshot.go rename to arbos/espresso/hotshot/hotshot.go diff --git a/espresso/nmt.go b/arbos/espresso/nmt.go similarity index 100% rename from espresso/nmt.go rename to arbos/espresso/nmt.go diff --git a/espresso/query.go b/arbos/espresso/query.go similarity index 100% rename from espresso/query.go rename to arbos/espresso/query.go diff --git a/espresso/types.go b/arbos/espresso/types.go similarity index 100% rename from espresso/types.go rename to arbos/espresso/types.go diff --git a/espresso/types_test.go b/arbos/espresso/types_test.go similarity index 100% rename from espresso/types_test.go rename to arbos/espresso/types_test.go diff --git a/cmd/replay/main.go b/cmd/replay/main.go index 2fb13ceed8..3079e523a1 100644 --- a/cmd/replay/main.go +++ b/cmd/replay/main.go @@ -25,6 +25,7 @@ import ( "github.com/offchainlabs/nitro/arbos/arbosState" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbos/burn" + "github.com/offchainlabs/nitro/arbos/espresso" "github.com/offchainlabs/nitro/arbstate" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/cmd/chaininfo" @@ -46,6 +47,12 @@ func getBlockHeaderByHash(hash common.Hash) *types.Header { return header } +func getHotShotCommitment(seqNum uint64) *espresso.Commitment { + headerBytes := espresso.Commitment(wavmio.ReadHotShotCommitment(seqNum)) + log.Info("HotShot commitment", "commit", headerBytes) + return &headerBytes +} + type WavmChainContext struct{} func (c WavmChainContext) Engine() consensus.Engine { @@ -238,7 +245,12 @@ func main() { batchFetcher := func(batchNum uint64) ([]byte, error) { return wavmio.ReadInboxMessage(batchNum), nil } - newBlock, _, err = arbos.ProduceBlock(message.Message, message.DelayedMessagesRead, lastBlockHeader, statedb, chainContext, chainConfig, batchFetcher) + seqNum := wavmio.GetInboxPosition() + var hotShotCommitment *espresso.Commitment + if chainConfig.Espresso { + hotShotCommitment = getHotShotCommitment(seqNum) + } + newBlock, _, err = arbos.ProduceBlock(message.Message, message.DelayedMessagesRead, lastBlockHeader, hotShotCommitment, statedb, chainContext, chainConfig, batchFetcher) if err != nil { panic(err) } diff --git a/execution/gethexec/block_recorder.go b/execution/gethexec/block_recorder.go index a0f6d837e4..d238d282ac 100644 --- a/execution/gethexec/block_recorder.go +++ b/execution/gethexec/block_recorder.go @@ -140,6 +140,7 @@ func (r *BlockRecorder) RecordBlockCreation( msg.Message, msg.DelayedMessagesRead, prevHeader, + nil, recordingdb, chaincontext, chainConfig, diff --git a/execution/gethexec/espresso_sequencer.go b/execution/gethexec/espresso_sequencer.go index 1e7d48fd96..a481696207 100644 --- a/execution/gethexec/espresso_sequencer.go +++ b/execution/gethexec/espresso_sequencer.go @@ -8,7 +8,7 @@ import ( "encoding/json" "time" - "github.com/offchainlabs/nitro/espresso" + "github.com/offchainlabs/nitro/arbos/espresso" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/ethereum/go-ethereum/arbitrum_types" @@ -108,7 +108,10 @@ func (s *EspressoSequencer) createBlock(ctx context.Context) (returnValue bool) Timestamp: header.Timestamp, RequestId: nil, L1BaseFee: nil, - // TODO: add justification https://github.com/EspressoSystems/espresso-sequencer/issues/733 + BlockJustification: &arbostypes.EspressoBlockJustification{ + Header: header, + Proof: arbTxns.Proof, + }, } hooks := s.makeSequencingHooks() diff --git a/execution/gethexec/executionengine.go b/execution/gethexec/executionengine.go index 58e91a197e..2794615df4 100644 --- a/execution/gethexec/executionengine.go +++ b/execution/gethexec/executionengine.go @@ -17,6 +17,7 @@ import ( "github.com/offchainlabs/nitro/arbos" "github.com/offchainlabs/nitro/arbos/arbosState" "github.com/offchainlabs/nitro/arbos/arbostypes" + "github.com/offchainlabs/nitro/arbos/espresso" "github.com/offchainlabs/nitro/arbos/l1pricing" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/execution" @@ -287,6 +288,11 @@ func (s *ExecutionEngine) sequenceTransactionsWithBlockMutex(header *arbostypes. } delayedMessagesRead := lastBlockHeader.Nonce.Uint64() + jst := header.BlockJustification + var hotShotHeader espresso.Commitment + if jst != nil { + hotShotHeader = jst.Header.Commit() + } startTime := time.Now() block, receipts, err := arbos.ProduceBlockAdvanced( @@ -294,6 +300,7 @@ func (s *ExecutionEngine) sequenceTransactionsWithBlockMutex(header *arbostypes. txes, delayedMessagesRead, lastBlockHeader, + &hotShotHeader, statedb, s.bc, s.bc.Config(), @@ -441,11 +448,17 @@ func (s *ExecutionEngine) createBlockFromNextMessage(msg *arbostypes.MessageWith } statedb.StartPrefetcher("TransactionStreamer") defer statedb.StopPrefetcher() + jst := msg.Message.Header.BlockJustification + var hotShotCommitment espresso.Commitment + if jst != nil { + hotShotCommitment = jst.Header.Commit() + } block, receipts, err := arbos.ProduceBlock( msg.Message, msg.DelayedMessagesRead, currentHeader, + &hotShotCommitment, statedb, s.bc, s.bc.Config(), diff --git a/go-ethereum b/go-ethereum index d16fbb42f4..3a9014ea33 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit d16fbb42f4208332129a7f139e4787621d4784bc +Subproject commit 3a9014ea33eaac9869eaf7e6228cfa988a89e6ac diff --git a/scripts/espresso-ci-local b/scripts/espresso-ci-local new file mode 100755 index 0000000000..3ae4f86033 --- /dev/null +++ b/scripts/espresso-ci-local @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +make lint + +make test-go +make test-go-challenge + +container=$(docker run -d -p 6379:6379 redis) +trap "exit" INT TERM +trap "docker rm -f $container" EXIT + +make test-go-redis diff --git a/staker/block_validator.go b/staker/block_validator.go index 108d6d1d49..75b23c54ae 100644 --- a/staker/block_validator.go +++ b/staker/block_validator.go @@ -86,6 +86,8 @@ type BlockValidatorConfig struct { PendingUpgradeModuleRoot string `koanf:"pending-upgrade-module-root"` // TODO(magic) requires StatelessBlockValidator recreation on hot reload FailureIsFatal bool `koanf:"failure-is-fatal" reload:"hot"` Dangerous BlockValidatorDangerousConfig `koanf:"dangerous"` + // Espresso specific flags + Espresso bool `koanf:"espresso"` } func (c *BlockValidatorConfig) Validate() error { @@ -107,6 +109,7 @@ func BlockValidatorConfigAddOptions(prefix string, f *flag.FlagSet) { f.String(prefix+".current-module-root", DefaultBlockValidatorConfig.CurrentModuleRoot, "current wasm module root ('current' read from chain, 'latest' from machines/latest dir, or provide hash)") f.String(prefix+".pending-upgrade-module-root", DefaultBlockValidatorConfig.PendingUpgradeModuleRoot, "pending upgrade wasm module root to additionally validate (hash, 'latest' or empty)") f.Bool(prefix+".failure-is-fatal", DefaultBlockValidatorConfig.FailureIsFatal, "failing a validation is treated as a fatal error") + f.Bool(prefix+".espresso", DefaultBlockValidatorConfig.Espresso, "if true, hotshot header preimages will be added to validation entries to verify that transactions have been sequenced by espresso") BlockValidatorDangerousConfigAddOptions(prefix+".dangerous", f) } diff --git a/staker/stateless_block_validator.go b/staker/stateless_block_validator.go index c4968ca9e4..f00fbcaad6 100644 --- a/staker/stateless_block_validator.go +++ b/staker/stateless_block_validator.go @@ -11,6 +11,7 @@ import ( "sync" "testing" + "github.com/offchainlabs/nitro/arbos/espresso" "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/util/rpcclient" "github.com/offchainlabs/nitro/validator/server_api" @@ -259,6 +260,7 @@ func (v *StatelessBlockValidator) GetModuleRootsToValidate() []common.Hash { } func (v *StatelessBlockValidator) ValidationEntryRecord(ctx context.Context, e *validationEntry) error { + usingEspresso := v.config.Espresso if e.Stage != ReadyForRecord { return fmt.Errorf("validation entry should be ReadyForRecord, is: %v", e.Stage) } @@ -288,7 +290,7 @@ func (v *StatelessBlockValidator) ValidationEntryRecord(ctx context.Context, e * } e.DelayedMsg = delayedMsg } - for _, batch := range e.BatchInfo { + for i, batch := range e.BatchInfo { if len(batch.Data) <= 40 { continue } @@ -305,6 +307,13 @@ func (v *StatelessBlockValidator) ValidationEntryRecord(ctx context.Context, e * return err } } + if usingEspresso { + // TODO: implement client method to fetch real headers + // https://github.com/EspressoSystems/espresso-sequencer/issues/771 + hotShotHeader := espresso.Header{} + hotShotCommitment := hotShotHeader.Commit() + e.BatchInfo[i].HotShotCommitment = &hotShotCommitment + } } e.msg = nil // no longer needed diff --git a/system_tests/state_fuzz_test.go b/system_tests/state_fuzz_test.go index b14215fbf0..3dc6c54e1e 100644 --- a/system_tests/state_fuzz_test.go +++ b/system_tests/state_fuzz_test.go @@ -56,7 +56,7 @@ func BuildBlock( return seqBatch, nil } block, _, err := arbos.ProduceBlock( - l1Message, delayedMessagesRead, lastBlockHeader, statedb, chainContext, chainConfig, batchFetcher, + l1Message, delayedMessagesRead, lastBlockHeader, nil, statedb, chainContext, chainConfig, batchFetcher, ) return block, err } diff --git a/validator/server_jit/jit_machine.go b/validator/server_jit/jit_machine.go index f763ce3ea0..d04a3ad87c 100644 --- a/validator/server_jit/jit_machine.go +++ b/validator/server_jit/jit_machine.go @@ -151,6 +151,10 @@ func (machine *JitMachine) prove( // send inbox for _, batch := range entry.BatchInfo { + var hotShotCommitment [32]byte + if batch.HotShotCommitment != nil { + hotShotCommitment = *batch.HotShotCommitment + } if err := writeExact(another); err != nil { return state, err } @@ -160,6 +164,9 @@ func (machine *JitMachine) prove( if err := writeBytes(batch.Data); err != nil { return state, err } + if err := writeExact(hotShotCommitment[:]); err != nil { + return state, err + } } if err := writeExact(success); err != nil { return state, err diff --git a/validator/validation_entry.go b/validator/validation_entry.go index fed1940f1f..b79c613ac4 100644 --- a/validator/validation_entry.go +++ b/validator/validation_entry.go @@ -2,12 +2,14 @@ package validator import ( "github.com/ethereum/go-ethereum/common" + "github.com/offchainlabs/nitro/arbos/espresso" "github.com/offchainlabs/nitro/arbutil" ) type BatchInfo struct { - Number uint64 - Data []byte + Number uint64 + HotShotCommitment *espresso.Commitment + Data []byte } type ValidationInput struct { diff --git a/wavmio/higher.go b/wavmio/higher.go index 81fa4a5e3e..bfad69b33a 100644 --- a/wavmio/higher.go +++ b/wavmio/higher.go @@ -53,6 +53,11 @@ func ReadInboxMessage(msgNum uint64) []byte { }) } +func ReadHotShotCommitment(seqNum uint64) (commitment [32]byte) { + readHotShotCommitment(seqNum, commitment[:]) + return +} + func ReadDelayedInboxMessage(seqNum uint64) []byte { return readBuffer(func(offset uint32, buf []byte) uint32 { return readDelayedInboxMessage(seqNum, offset, buf) diff --git a/wavmio/raw.go b/wavmio/raw.go index f0462cbbe3..0624170165 100644 --- a/wavmio/raw.go +++ b/wavmio/raw.go @@ -11,5 +11,6 @@ func setGlobalStateBytes32(idx uint64, val []byte) func getGlobalStateU64(idx uint64) uint64 func setGlobalStateU64(idx uint64, val uint64) func readInboxMessage(msgNum uint64, offset uint32, output []byte) uint32 +func readHotShotCommitment(seqNum uint64, output []byte) uint32 func readDelayedInboxMessage(seqNum uint64, offset uint32, output []byte) uint32 func resolveTypedPreimage(ty uint8, hash []byte, offset uint32, output []byte) uint32 diff --git a/wavmio/raw.s b/wavmio/raw.s index 7347d13394..42576664dd 100644 --- a/wavmio/raw.s +++ b/wavmio/raw.s @@ -26,6 +26,10 @@ TEXT ·readInboxMessage(SB), NOSPLIT, $0 CallImport RET +TEXT ·readHotShotCommitment(SB), NOSPLIT, $0 + CallImport + RET + TEXT ·readDelayedInboxMessage(SB), NOSPLIT, $0 CallImport RET diff --git a/wavmio/stub.go b/wavmio/stub.go index 0893f35250..df2914940a 100644 --- a/wavmio/stub.go +++ b/wavmio/stub.go @@ -39,6 +39,7 @@ var ( delayedMsgs [][]byte delayedMsgFirstPos uint64 lastBlockHash common.Hash + hotShotHeader [32]byte preimages map[common.Hash][]byte seqAdvanced uint64 ) @@ -117,6 +118,14 @@ func GetLastBlockHash() (hash common.Hash) { return lastBlockHash } +func ReadHotShotCommitment(seqNum uint64) [32]byte { + if seqNum != seqMsgPos { + panic(fmt.Sprintf("hotshot header position should be consistent with the sequencer inbox position %d", seqNum)) + } + return hotShotHeader + +} + func ReadInboxMessage(msgNum uint64) []byte { if msgNum != seqMsgPos { panic(fmt.Sprintf("trying to read bad msg %d", msgNum))