mirror of
https://github.com/vacp2p/linea-monorepo.git
synced 2026-01-09 15:38:06 -05:00
Fix PI Initial Rolling Hash Update (#360)
* fix rolling hash updates in collectFields * fix initial rolling hash in piq * fix assignment rolling hash logic * docs give names to all checks * docs point out checks in circuit * refac check for chain id etc first --------- Co-authored-by: Arya Tabaie <15056835+Tabaie@users.noreply.github.com> Co-authored-by: AlexandreBelling <alexandrebelling8@gmail.com>
This commit is contained in:
@@ -58,14 +58,13 @@ func collectFields(cfg *config.Config, req *Request) (*CollectedFields, error) {
|
||||
for i, execReqFPath := range req.ExecutionProofs {
|
||||
|
||||
var (
|
||||
po = &execution.Response{}
|
||||
l2MessageHashes []string
|
||||
fpath = path.Join(cfg.Execution.DirTo(), execReqFPath)
|
||||
f = files.MustRead(fpath)
|
||||
initialBlockTimestamp uint64
|
||||
po execution.Response
|
||||
l2MessageHashes []string
|
||||
fpath = path.Join(cfg.Execution.DirTo(), execReqFPath)
|
||||
f = files.MustRead(fpath)
|
||||
)
|
||||
|
||||
if err := json.NewDecoder(f).Decode(po); err != nil {
|
||||
if err := json.NewDecoder(f).Decode(&po); err != nil {
|
||||
return nil, fmt.Errorf("fields collection, decoding %s, %w", execReqFPath, err)
|
||||
}
|
||||
|
||||
@@ -85,11 +84,9 @@ func collectFields(cfg *config.Config, req *Request) (*CollectedFields, error) {
|
||||
// This is purposefully overwritten at each iteration over i. We want to
|
||||
// keep the final value.
|
||||
cf.FinalBlockNumber = uint(po.FirstBlockNumber + len(po.BlocksData) - 1)
|
||||
rollingHashUpdateEvents := po.AllRollingHashEvent // check redundant data for discrepancy
|
||||
|
||||
for i, blockdata := range po.BlocksData {
|
||||
if i == 0 {
|
||||
initialBlockTimestamp = blockdata.TimeStamp
|
||||
}
|
||||
for _, blockdata := range po.BlocksData {
|
||||
|
||||
for i := range blockdata.L2ToL1MsgHashes {
|
||||
l2MessageHashes = append(l2MessageHashes, blockdata.L2ToL1MsgHashes[i].Hex())
|
||||
@@ -101,12 +98,29 @@ func collectFields(cfg *config.Config, req *Request) (*CollectedFields, error) {
|
||||
// The goal is that we want to keep the final value
|
||||
lastRollingHashEvent := blockdata.LastRollingHashUpdatedEvent
|
||||
if lastRollingHashEvent != (bridge.RollingHashUpdated{}) {
|
||||
if len(rollingHashUpdateEvents) == 0 {
|
||||
return nil, fmt.Errorf("data discrepancy: only %d rolling hash update events available in the conflation object, more available in the block data", len(po.AllRollingHashEvent))
|
||||
}
|
||||
|
||||
update := rollingHashUpdateEvents[0]
|
||||
rollingHashUpdateEvents = rollingHashUpdateEvents[1:]
|
||||
|
||||
if lastRollingHashEvent.RollingHash != update.RollingHash {
|
||||
return nil, fmt.Errorf("data discrepancy: rolling hash update from conflation: %x. from block: %x", update.RollingHash, lastRollingHashEvent.RollingHash)
|
||||
}
|
||||
if lastRollingHashEvent.MessageNumber != update.MessageNumber {
|
||||
return nil, fmt.Errorf("data discrepancy: rolling hash message number update from conflation: %d. from block: %d", update.MessageNumber, lastRollingHashEvent.MessageNumber)
|
||||
}
|
||||
|
||||
cf.L1RollingHash = lastRollingHashEvent.RollingHash.Hex()
|
||||
cf.L1RollingHashMessageNumber = uint(lastRollingHashEvent.MessageNumber)
|
||||
}
|
||||
|
||||
cf.FinalTimestamp = uint(blockdata.TimeStamp)
|
||||
}
|
||||
if len(rollingHashUpdateEvents) != 0 {
|
||||
return nil, fmt.Errorf("data discrepancy: %d rolling hash updates in conflation object but only %d collected from blocks", len(po.AllRollingHashEvent), len(po.AllRollingHashEvent)-len(rollingHashUpdateEvents))
|
||||
}
|
||||
|
||||
// Append the proof claim to the list of collected proofs
|
||||
if !cf.IsProoflessJob { // TODO @Tabaie @alexandre.belling proofless jobs will no longer be accepted post PI interconnection
|
||||
@@ -116,25 +130,11 @@ func collectFields(cfg *config.Config, req *Request) (*CollectedFields, error) {
|
||||
return nil, fmt.Errorf("could not parse the proof claim for `%v` : %w", fpath, err)
|
||||
}
|
||||
cf.ProofClaims = append(cf.ProofClaims, *pClaim)
|
||||
// TODO make sure this belongs in the if
|
||||
finalBlock := &po.BlocksData[len(po.BlocksData)-1]
|
||||
piq, err := public_input.ExecutionSerializable{
|
||||
InitialBlockTimestamp: initialBlockTimestamp,
|
||||
L2MsgHashes: l2MessageHashes,
|
||||
FinalStateRootHash: finalBlock.RootHash.Hex(),
|
||||
FinalBlockNumber: uint64(cf.FinalBlockNumber),
|
||||
FinalBlockTimestamp: finalBlock.TimeStamp,
|
||||
FinalRollingHash: cf.L1RollingHash,
|
||||
FinalRollingHashNumber: uint64(cf.L1RollingHashMessageNumber),
|
||||
L2MessageServiceAddr: po.L2BridgeAddress,
|
||||
ChainID: uint64(po.ChainID),
|
||||
}.Decode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if piq.L2MessageServiceAddr != types.EthAddress(cfg.Layer2.MsgSvcContract) {
|
||||
return nil, fmt.Errorf("execution #%d: expected L2 msg service addr %x, encountered %x", i, cfg.Layer2.MsgSvcContract, piq.L2MessageServiceAddr)
|
||||
pi := po.FuncInput()
|
||||
|
||||
if pi.L2MessageServiceAddr != types.EthAddress(cfg.Layer2.MsgSvcContract) {
|
||||
return nil, fmt.Errorf("execution #%d: expected L2 msg service addr %x, encountered %x", i, cfg.Layer2.MsgSvcContract, pi.L2MessageServiceAddr)
|
||||
}
|
||||
if po.ChainID != cfg.Layer2.ChainID {
|
||||
return nil, fmt.Errorf("execution #%d: expected chain ID %x, encountered %x", i, cfg.Layer2.ChainID, po.ChainID)
|
||||
@@ -145,7 +145,7 @@ func collectFields(cfg *config.Config, req *Request) (*CollectedFields, error) {
|
||||
return nil, fmt.Errorf("execution #%d: public input mismatch: given %x, computed %x", i, po.PublicInput, pi)
|
||||
}
|
||||
|
||||
cf.ExecutionPI = append(cf.ExecutionPI, piq)
|
||||
cf.ExecutionPI = append(cf.ExecutionPI, *pi)
|
||||
}
|
||||
|
||||
allL2MessageHashes = append(allL2MessageHashes, l2MessageHashes...)
|
||||
|
||||
@@ -67,6 +67,26 @@ func makeProof(
|
||||
return circuits.SerializeProofSolidityBn254(proofBn254), nil
|
||||
}
|
||||
|
||||
func (cf CollectedFields) AggregationPublicInput(cfg *config.Config) public_input.Aggregation {
|
||||
return public_input.Aggregation{
|
||||
FinalShnarf: cf.FinalShnarf,
|
||||
ParentAggregationFinalShnarf: cf.ParentAggregationFinalShnarf,
|
||||
ParentStateRootHash: cf.ParentStateRootHash,
|
||||
ParentAggregationLastBlockTimestamp: cf.ParentAggregationLastBlockTimestamp,
|
||||
FinalTimestamp: cf.FinalTimestamp,
|
||||
LastFinalizedBlockNumber: cf.LastFinalizedBlockNumber,
|
||||
FinalBlockNumber: cf.FinalBlockNumber,
|
||||
LastFinalizedL1RollingHash: cf.LastFinalizedL1RollingHash,
|
||||
L1RollingHash: cf.L1RollingHash,
|
||||
LastFinalizedL1RollingHashMessageNumber: cf.LastFinalizedL1RollingHashMessageNumber,
|
||||
L1RollingHashMessageNumber: cf.L1RollingHashMessageNumber,
|
||||
L2MsgRootHashes: cf.L2MsgRootHashes,
|
||||
L2MsgMerkleTreeDepth: utils.ToInt(cf.L2MsgTreeDepth),
|
||||
ChainID: uint64(cfg.Layer2.ChainID),
|
||||
L2MessageServiceAddr: types.EthAddress(cfg.Layer2.MsgSvcContract),
|
||||
}
|
||||
}
|
||||
|
||||
func makePiProof(cfg *config.Config, cf *CollectedFields) (plonk.Proof, witness.Witness, error) {
|
||||
|
||||
var setup circuits.Setup
|
||||
@@ -87,23 +107,7 @@ func makePiProof(cfg *config.Config, cf *CollectedFields) (plonk.Proof, witness.
|
||||
assignment, err := c.Assign(pi_interconnection.Request{
|
||||
Decompressions: cf.DecompressionPI,
|
||||
Executions: cf.ExecutionPI,
|
||||
Aggregation: public_input.Aggregation{
|
||||
FinalShnarf: cf.FinalShnarf,
|
||||
ParentAggregationFinalShnarf: cf.ParentAggregationFinalShnarf,
|
||||
ParentStateRootHash: cf.ParentStateRootHash,
|
||||
ParentAggregationLastBlockTimestamp: cf.ParentAggregationLastBlockTimestamp,
|
||||
FinalTimestamp: cf.FinalTimestamp,
|
||||
LastFinalizedBlockNumber: cf.LastFinalizedBlockNumber,
|
||||
FinalBlockNumber: cf.FinalBlockNumber,
|
||||
LastFinalizedL1RollingHash: cf.LastFinalizedL1RollingHash,
|
||||
L1RollingHash: cf.L1RollingHash,
|
||||
LastFinalizedL1RollingHashMessageNumber: cf.LastFinalizedL1RollingHashMessageNumber,
|
||||
L1RollingHashMessageNumber: cf.L1RollingHashMessageNumber,
|
||||
L2MsgRootHashes: cf.L2MsgRootHashes,
|
||||
L2MsgMerkleTreeDepth: utils.ToInt(cf.L2MsgTreeDepth),
|
||||
ChainID: uint64(cfg.Layer2.ChainID),
|
||||
L2MessageServiceAddr: types.EthAddress(cfg.Layer2.MsgSvcContract),
|
||||
},
|
||||
Aggregation: cf.AggregationPublicInput(cfg),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("could not assign the public input circuit: %w", err)
|
||||
|
||||
@@ -2,6 +2,7 @@ package execution
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
public_input "github.com/consensys/linea-monorepo/prover/public-input"
|
||||
"path"
|
||||
|
||||
"github.com/consensys/linea-monorepo/prover/backend/ethereum"
|
||||
@@ -9,7 +10,6 @@ import (
|
||||
"github.com/consensys/linea-monorepo/prover/backend/execution/statemanager"
|
||||
"github.com/consensys/linea-monorepo/prover/crypto/mimc"
|
||||
|
||||
"github.com/consensys/linea-monorepo/prover/circuits/execution"
|
||||
"github.com/consensys/linea-monorepo/prover/config"
|
||||
blob "github.com/consensys/linea-monorepo/prover/lib/compressor/blob/v1"
|
||||
"github.com/consensys/linea-monorepo/prover/utils"
|
||||
@@ -174,14 +174,13 @@ func (req *Request) collectSignatures() ([]ethereum.Signature, [][32]byte) {
|
||||
// are functionally useful to contextualize what the proof is proving. This
|
||||
// is used by the aggregation circuit to ensure that the execution proofs
|
||||
// relate to consecutive Linea block execution.
|
||||
func (rsp *Response) FuncInput() *execution.FunctionalPublicInput {
|
||||
func (rsp *Response) FuncInput() *public_input.Execution {
|
||||
|
||||
var (
|
||||
firstBlock = &rsp.BlocksData[0]
|
||||
lastBlock = &rsp.BlocksData[len(rsp.BlocksData)-1]
|
||||
fi = &execution.FunctionalPublicInput{
|
||||
fi = &public_input.Execution{
|
||||
L2MessageServiceAddr: types.EthAddress(rsp.L2BridgeAddress),
|
||||
MaxNbL2MessageHashes: rsp.MaxNbL2MessageHashes,
|
||||
ChainID: uint64(rsp.ChainID),
|
||||
FinalBlockTimestamp: lastBlock.TimeStamp,
|
||||
FinalBlockNumber: uint64(rsp.FirstBlockNumber + len(rsp.BlocksData) - 1),
|
||||
@@ -200,10 +199,10 @@ func (rsp *Response) FuncInput() *execution.FunctionalPublicInput {
|
||||
lastRHEvent = rsp.AllRollingHashEvent[len(rsp.AllRollingHashEvent)-1]
|
||||
)
|
||||
|
||||
fi.InitialRollingHash = firstRHEvent.RollingHash
|
||||
fi.FinalRollingHash = lastRHEvent.RollingHash
|
||||
fi.InitialRollingHashNumber = uint64(firstRHEvent.MessageNumber)
|
||||
fi.FinalRollingHashNumber = uint64(lastRHEvent.MessageNumber)
|
||||
fi.InitialRollingHashUpdate = firstRHEvent.RollingHash
|
||||
fi.FinalRollingHashUpdate = lastRHEvent.RollingHash
|
||||
fi.InitialRollingHashMsgNumber = uint64(firstRHEvent.MessageNumber)
|
||||
fi.FinalRollingHashMsgNumber = uint64(lastRHEvent.MessageNumber)
|
||||
}
|
||||
|
||||
return fi
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/consensys/linea-monorepo/prover/circuits/dummy"
|
||||
"github.com/consensys/linea-monorepo/prover/circuits/execution"
|
||||
"github.com/consensys/linea-monorepo/prover/config"
|
||||
public_input "github.com/consensys/linea-monorepo/prover/public-input"
|
||||
"github.com/consensys/linea-monorepo/prover/utils"
|
||||
"github.com/consensys/linea-monorepo/prover/utils/profiling"
|
||||
"github.com/consensys/linea-monorepo/prover/zkevm"
|
||||
@@ -13,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
type Witness struct {
|
||||
FuncInp *execution.FunctionalPublicInput
|
||||
FuncInp *public_input.Execution
|
||||
ZkEVM *zkevm.Witness
|
||||
}
|
||||
|
||||
@@ -144,7 +145,7 @@ func mustProveAndPass(
|
||||
}
|
||||
|
||||
// TODO: implements the collection of the functional inputs from the prover response
|
||||
return execution.MakeProof(setup, fullZkEvm.WizardIOP, proof, *w.FuncInp), setup.VerifyingKeyDigest()
|
||||
return execution.MakeProof(traces, setup, fullZkEvm.WizardIOP, proof, *w.FuncInp), setup.VerifyingKeyDigest()
|
||||
|
||||
case config.ProverModeBench:
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package execution
|
||||
|
||||
import (
|
||||
"github.com/consensys/linea-monorepo/prover/config"
|
||||
public_input "github.com/consensys/linea-monorepo/prover/public-input"
|
||||
"math/big"
|
||||
|
||||
"github.com/consensys/gnark-crypto/ecc"
|
||||
@@ -45,10 +47,10 @@ func Allocate(zkevm *zkevm.ZkEvm) CircuitExecution {
|
||||
extractor: zkevm.PublicInput.Extractor,
|
||||
FuncInputs: FunctionalPublicInputSnark{
|
||||
FunctionalPublicInputQSnark: FunctionalPublicInputQSnark{
|
||||
L2MessageHashes: NewL2MessageHashes(
|
||||
[][32]frontend.Variable{},
|
||||
zkevm.Limits().BlockL2L1Logs,
|
||||
),
|
||||
L2MessageHashes: L2MessageHashes{
|
||||
Values: make([][32]frontend.Variable, zkevm.Limits().BlockL2L1Logs),
|
||||
Length: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -56,19 +58,21 @@ func Allocate(zkevm *zkevm.ZkEvm) CircuitExecution {
|
||||
|
||||
// assign the wizard proof to the outer circuit
|
||||
func assign(
|
||||
limits *config.TracesLimits,
|
||||
comp *wizard.CompiledIOP,
|
||||
proof wizard.Proof,
|
||||
funcInputs FunctionalPublicInput,
|
||||
funcInputs public_input.Execution,
|
||||
) CircuitExecution {
|
||||
wizardVerifier := wizard.GetWizardVerifierCircuitAssignment(comp, proof)
|
||||
fpiSnark, err := funcInputs.ToSnarkType()
|
||||
if err != nil {
|
||||
panic(err) // TODO error handling
|
||||
}
|
||||
|
||||
return CircuitExecution{
|
||||
WizardVerifier: *wizardVerifier,
|
||||
FuncInputs: fpiSnark,
|
||||
PublicInput: new(big.Int).SetBytes(funcInputs.Sum(nil)),
|
||||
FuncInputs: FunctionalPublicInputSnark{
|
||||
FunctionalPublicInputQSnark: FunctionalPublicInputQSnark{
|
||||
L2MessageHashes: L2MessageHashes{Values: make([][32]frontend.Variable, limits.BlockL2L1Logs)}, // TODO use a maximum from config
|
||||
},
|
||||
},
|
||||
PublicInput: new(big.Int).SetBytes(funcInputs.Sum(nil)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,13 +93,14 @@ func (c *CircuitExecution) Define(api frontend.API) error {
|
||||
}
|
||||
|
||||
func MakeProof(
|
||||
limits *config.TracesLimits,
|
||||
setup circuits.Setup,
|
||||
comp *wizard.CompiledIOP,
|
||||
wproof wizard.Proof,
|
||||
funcInputs FunctionalPublicInput,
|
||||
funcInputs public_input.Execution,
|
||||
) string {
|
||||
|
||||
assignment := assign(comp, wproof, funcInputs)
|
||||
assignment := assign(limits, comp, wproof, funcInputs)
|
||||
witness, err := frontend.NewWitness(&assignment, ecc.BLS12_377.ScalarField())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@@ -1,46 +1,57 @@
|
||||
package execution
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
"slices"
|
||||
|
||||
"github.com/consensys/gnark/frontend"
|
||||
gnarkHash "github.com/consensys/gnark/std/hash"
|
||||
"github.com/consensys/gnark/std/rangecheck"
|
||||
"github.com/consensys/linea-monorepo/prover/circuits/internal"
|
||||
"github.com/consensys/linea-monorepo/prover/crypto/mimc"
|
||||
"github.com/consensys/linea-monorepo/prover/maths/field"
|
||||
public_input "github.com/consensys/linea-monorepo/prover/public-input"
|
||||
"github.com/consensys/linea-monorepo/prover/utils"
|
||||
"github.com/consensys/linea-monorepo/prover/utils/types"
|
||||
)
|
||||
|
||||
// FunctionalPublicInputQSnark the information on this execution that cannot be
|
||||
// extracted from other input in the same aggregation batch
|
||||
type FunctionalPublicInputQSnark struct {
|
||||
DataChecksum frontend.Variable
|
||||
L2MessageHashes L2MessageHashes
|
||||
InitialBlockTimestamp frontend.Variable
|
||||
FinalStateRootHash frontend.Variable
|
||||
FinalBlockNumber frontend.Variable
|
||||
FinalBlockTimestamp frontend.Variable
|
||||
FinalRollingHash [32]frontend.Variable
|
||||
FinalRollingHashNumber frontend.Variable
|
||||
DataChecksum frontend.Variable
|
||||
L2MessageHashes L2MessageHashes
|
||||
InitialBlockTimestamp frontend.Variable
|
||||
FinalStateRootHash frontend.Variable
|
||||
FinalBlockNumber frontend.Variable
|
||||
FinalBlockTimestamp frontend.Variable
|
||||
InitialRollingHashUpdate [32]frontend.Variable
|
||||
InitialRollingHashMsgNumber frontend.Variable
|
||||
FinalRollingHashUpdate [32]frontend.Variable
|
||||
FinalRollingHashMsgNumber frontend.Variable
|
||||
}
|
||||
|
||||
// L2MessageHashes is a wrapper for [Var32Slice] it is use to instantiate the
|
||||
// sequence of L2MessageHash that we extract from the arithmetization. The
|
||||
// reason we need a wrapper here is because we hash the L2MessageHashes in a
|
||||
// reason we need a wrapper here is that we hash the L2MessageHashes in a
|
||||
// specific way.
|
||||
type L2MessageHashes internal.Var32Slice
|
||||
|
||||
// NewL2MessageHashes constructs a new var slice
|
||||
func NewL2MessageHashes(v [][32]frontend.Variable, max int) L2MessageHashes {
|
||||
return L2MessageHashes(internal.NewSliceOf32Array(v, max))
|
||||
func (s *L2MessageHashes) Assign(values [][32]byte) error {
|
||||
if len(values) > len(s.Values) {
|
||||
return fmt.Errorf("%d values cannot fit in %d-long slice", len(values), len(s.Values))
|
||||
}
|
||||
for i := range values {
|
||||
utils.Copy(s.Values[i][:], values[i][:])
|
||||
}
|
||||
var zeros [32]byte
|
||||
for i := len(values); i < len(s.Values); i++ {
|
||||
utils.Copy(s.Values[i][:], zeros[:])
|
||||
}
|
||||
s.Length = len(values)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckSum returns the hash of the [L2MessageHashes]. The encoding is done as
|
||||
func (s *L2MessageHashes) RangeCheck(api frontend.API) {
|
||||
api.AssertIsLessOrEqual(s.Length, uint64(len(s.Values)))
|
||||
}
|
||||
|
||||
// CheckSumMiMC returns the hash of the [L2MessageHashes]. The encoding is done as
|
||||
// follows:
|
||||
//
|
||||
// - each L2 hash is decomposed in a hi and lo part: each over 16 bytes
|
||||
@@ -56,19 +67,19 @@ func NewL2MessageHashes(v [][32]frontend.Variable, max int) L2MessageHashes {
|
||||
// @alex: it would be nice to make that function compatible with the GKR hasher
|
||||
// factory though in practice this function will only create 32 calls to the
|
||||
// MiMC permutation which makes it a non-issue.
|
||||
func (l *L2MessageHashes) CheckSumMiMC(api frontend.API) frontend.Variable {
|
||||
func (s *L2MessageHashes) CheckSumMiMC(api frontend.API) frontend.Variable {
|
||||
|
||||
var (
|
||||
// sumIsUsed is used to count the number of non-zero hashes that we
|
||||
// found in l. It is to be tested against l.Length.
|
||||
// found in s. It is to be tested against s.Length.
|
||||
sumIsUsed = frontend.Variable(0)
|
||||
res = frontend.Variable(0)
|
||||
)
|
||||
|
||||
for i := range l.Values {
|
||||
for i := range s.Values {
|
||||
var (
|
||||
hi = internal.Pack(api, l.Values[i][:16], 128, 8)[0]
|
||||
lo = internal.Pack(api, l.Values[i][16:], 128, 8)[0]
|
||||
hi = internal.Pack(api, s.Values[i][:16], 128, 8)[0]
|
||||
lo = internal.Pack(api, s.Values[i][16:], 128, 8)[0]
|
||||
isUsed = api.Sub(
|
||||
1,
|
||||
api.Mul(
|
||||
@@ -85,145 +96,78 @@ func (l *L2MessageHashes) CheckSumMiMC(api frontend.API) frontend.Variable {
|
||||
sumIsUsed = api.Add(sumIsUsed, isUsed)
|
||||
}
|
||||
|
||||
api.AssertIsEqual(sumIsUsed, l.Length)
|
||||
api.AssertIsEqual(sumIsUsed, s.Length)
|
||||
return res
|
||||
}
|
||||
|
||||
type FunctionalPublicInputSnark struct {
|
||||
FunctionalPublicInputQSnark
|
||||
InitialStateRootHash frontend.Variable
|
||||
InitialBlockNumber frontend.Variable
|
||||
InitialRollingHash [32]frontend.Variable
|
||||
InitialRollingHashNumber frontend.Variable
|
||||
ChainID frontend.Variable
|
||||
L2MessageServiceAddr frontend.Variable
|
||||
}
|
||||
|
||||
type FunctionalPublicInput struct {
|
||||
DataChecksum [32]byte
|
||||
L2MessageHashes [][32]byte
|
||||
MaxNbL2MessageHashes int
|
||||
FinalStateRootHash [32]byte
|
||||
FinalBlockNumber uint64
|
||||
FinalBlockTimestamp uint64
|
||||
FinalRollingHash [32]byte
|
||||
FinalRollingHashNumber uint64
|
||||
InitialStateRootHash [32]byte
|
||||
InitialBlockNumber uint64
|
||||
InitialBlockTimestamp uint64
|
||||
InitialRollingHash [32]byte
|
||||
InitialRollingHashNumber uint64
|
||||
ChainID uint64
|
||||
L2MessageServiceAddr types.EthAddress
|
||||
InitialStateRootHash frontend.Variable
|
||||
InitialBlockNumber frontend.Variable
|
||||
ChainID frontend.Variable
|
||||
L2MessageServiceAddr frontend.Variable
|
||||
}
|
||||
|
||||
// RangeCheck checks that values are within range
|
||||
func (pi *FunctionalPublicInputQSnark) RangeCheck(api frontend.API) {
|
||||
func (spiq *FunctionalPublicInputQSnark) RangeCheck(api frontend.API) {
|
||||
// the length of the l2msg slice is range checked in Concat; no need to do it here; TODO do it here instead
|
||||
rc := rangecheck.New(api)
|
||||
for _, v := range pi.L2MessageHashes.Values {
|
||||
for _, v := range spiq.L2MessageHashes.Values {
|
||||
for i := range v {
|
||||
rc.Check(v[i], 8)
|
||||
}
|
||||
}
|
||||
for i := range pi.FinalRollingHash {
|
||||
rc.Check(pi.FinalRollingHash[i], 8)
|
||||
for i := range spiq.FinalRollingHashUpdate {
|
||||
rc.Check(spiq.FinalRollingHashUpdate[i], 8)
|
||||
}
|
||||
rc.Check(spiq.FinalBlockNumber, 64)
|
||||
rc.Check(spiq.FinalBlockTimestamp, 64)
|
||||
rc.Check(spiq.InitialBlockTimestamp, 64)
|
||||
rc.Check(spiq.InitialRollingHashMsgNumber, 64)
|
||||
rc.Check(spiq.FinalRollingHashMsgNumber, 64)
|
||||
|
||||
spiq.L2MessageHashes.RangeCheck(api)
|
||||
}
|
||||
|
||||
func (pi *FunctionalPublicInputSnark) Sum(api frontend.API, hsh gnarkHash.FieldHasher) frontend.Variable {
|
||||
func (spi *FunctionalPublicInputSnark) Sum(api frontend.API, hsh gnarkHash.FieldHasher) frontend.Variable {
|
||||
|
||||
var (
|
||||
finalRollingHash = internal.CombineBytesIntoElements(api, pi.FinalRollingHash)
|
||||
initialRollingHash = internal.CombineBytesIntoElements(api, pi.InitialRollingHash)
|
||||
l2MessagesSum = pi.L2MessageHashes.CheckSumMiMC(api)
|
||||
finalRollingHash = internal.CombineBytesIntoElements(api, spi.FinalRollingHashUpdate)
|
||||
initialRollingHash = internal.CombineBytesIntoElements(api, spi.InitialRollingHashUpdate)
|
||||
l2MessagesSum = spi.L2MessageHashes.CheckSumMiMC(api)
|
||||
)
|
||||
|
||||
hsh.Reset()
|
||||
hsh.Write(pi.DataChecksum, l2MessagesSum,
|
||||
pi.FinalStateRootHash, pi.FinalBlockNumber, pi.FinalBlockTimestamp, finalRollingHash[0], finalRollingHash[1], pi.FinalRollingHashNumber,
|
||||
pi.InitialStateRootHash, pi.InitialBlockNumber, pi.InitialBlockTimestamp, initialRollingHash[0], initialRollingHash[1], pi.InitialRollingHashNumber,
|
||||
pi.ChainID, pi.L2MessageServiceAddr)
|
||||
hsh.Write(spi.DataChecksum, l2MessagesSum,
|
||||
spi.FinalStateRootHash, spi.FinalBlockNumber, spi.FinalBlockTimestamp, finalRollingHash[0], finalRollingHash[1], spi.FinalRollingHashMsgNumber,
|
||||
spi.InitialStateRootHash, spi.InitialBlockNumber, spi.InitialBlockTimestamp, initialRollingHash[0], initialRollingHash[1], spi.InitialRollingHashMsgNumber,
|
||||
spi.ChainID, spi.L2MessageServiceAddr)
|
||||
|
||||
return hsh.Sum()
|
||||
}
|
||||
|
||||
func (pi *FunctionalPublicInput) ToSnarkType() (FunctionalPublicInputSnark, error) {
|
||||
res := FunctionalPublicInputSnark{
|
||||
FunctionalPublicInputQSnark: FunctionalPublicInputQSnark{
|
||||
DataChecksum: slices.Clone(pi.DataChecksum[:]),
|
||||
L2MessageHashes: L2MessageHashes(internal.NewSliceOf32Array(pi.L2MessageHashes, pi.MaxNbL2MessageHashes)),
|
||||
InitialBlockTimestamp: pi.InitialBlockTimestamp,
|
||||
FinalStateRootHash: slices.Clone(pi.FinalStateRootHash[:]),
|
||||
FinalBlockNumber: pi.FinalBlockNumber,
|
||||
FinalBlockTimestamp: pi.FinalBlockTimestamp,
|
||||
FinalRollingHashNumber: pi.FinalRollingHashNumber,
|
||||
},
|
||||
InitialStateRootHash: slices.Clone(pi.InitialStateRootHash[:]),
|
||||
InitialBlockNumber: pi.InitialBlockNumber,
|
||||
InitialRollingHashNumber: pi.InitialRollingHashNumber,
|
||||
ChainID: pi.ChainID,
|
||||
L2MessageServiceAddr: slices.Clone(pi.L2MessageServiceAddr[:]),
|
||||
}
|
||||
utils.Copy(res.FinalRollingHash[:], pi.FinalRollingHash[:])
|
||||
utils.Copy(res.InitialRollingHash[:], pi.InitialRollingHash[:])
|
||||
func (spi *FunctionalPublicInputSnark) Assign(pi *public_input.Execution) error {
|
||||
|
||||
var err error
|
||||
if nbMsg := len(pi.L2MessageHashes); nbMsg > pi.MaxNbL2MessageHashes {
|
||||
err = fmt.Errorf("has %d L2 message hashes but a maximum of %d is allowed", nbMsg, pi.MaxNbL2MessageHashes)
|
||||
}
|
||||
spi.InitialStateRootHash = pi.InitialStateRootHash[:]
|
||||
spi.InitialBlockNumber = pi.InitialBlockNumber
|
||||
spi.ChainID = pi.ChainID
|
||||
spi.L2MessageServiceAddr = pi.L2MessageServiceAddr[:]
|
||||
|
||||
return res, err
|
||||
return spi.FunctionalPublicInputQSnark.Assign(pi)
|
||||
}
|
||||
|
||||
func (pi *FunctionalPublicInput) Sum(hsh hash.Hash) []byte {
|
||||
if hsh == nil {
|
||||
hsh = mimc.NewMiMC()
|
||||
}
|
||||
func (spiq *FunctionalPublicInputQSnark) Assign(pi *public_input.Execution) error {
|
||||
|
||||
hsh.Reset()
|
||||
for i := range pi.L2MessageHashes {
|
||||
hsh.Write(pi.L2MessageHashes[i][:16])
|
||||
hsh.Write(pi.L2MessageHashes[i][16:])
|
||||
}
|
||||
l2MessagesSum := hsh.Sum(nil)
|
||||
spiq.DataChecksum = pi.DataChecksum[:]
|
||||
spiq.InitialBlockTimestamp = pi.InitialBlockTimestamp
|
||||
spiq.FinalStateRootHash = pi.FinalStateRootHash[:]
|
||||
spiq.FinalBlockNumber = pi.FinalBlockNumber
|
||||
spiq.FinalBlockTimestamp = pi.FinalBlockTimestamp
|
||||
spiq.InitialRollingHashMsgNumber = pi.InitialRollingHashMsgNumber
|
||||
spiq.FinalRollingHashMsgNumber = pi.FinalRollingHashMsgNumber
|
||||
|
||||
hsh.Reset()
|
||||
|
||||
hsh.Write(pi.DataChecksum[:])
|
||||
hsh.Write(l2MessagesSum)
|
||||
hsh.Write(pi.FinalStateRootHash[:])
|
||||
|
||||
writeNum(hsh, pi.FinalBlockNumber)
|
||||
writeNum(hsh, pi.FinalBlockTimestamp)
|
||||
hsh.Write(pi.FinalRollingHash[:16])
|
||||
hsh.Write(pi.FinalRollingHash[16:])
|
||||
writeNum(hsh, pi.FinalRollingHashNumber)
|
||||
hsh.Write(pi.InitialStateRootHash[:])
|
||||
writeNum(hsh, pi.InitialBlockNumber)
|
||||
writeNum(hsh, pi.InitialBlockTimestamp)
|
||||
hsh.Write(pi.InitialRollingHash[:16])
|
||||
hsh.Write(pi.InitialRollingHash[16:])
|
||||
writeNum(hsh, pi.InitialRollingHashNumber)
|
||||
writeNum(hsh, pi.ChainID)
|
||||
hsh.Write(pi.L2MessageServiceAddr[:])
|
||||
|
||||
return hsh.Sum(nil)
|
||||
utils.Copy(spiq.FinalRollingHashUpdate[:], pi.FinalRollingHashUpdate[:])
|
||||
utils.Copy(spiq.InitialRollingHashUpdate[:], pi.InitialRollingHashUpdate[:])
|
||||
|
||||
}
|
||||
|
||||
func (pi *FunctionalPublicInput) SumAsField() field.Element {
|
||||
|
||||
var (
|
||||
sumBytes = pi.Sum(nil)
|
||||
sum = new(field.Element).SetBytes(sumBytes)
|
||||
)
|
||||
|
||||
return *sum
|
||||
}
|
||||
|
||||
func writeNum(hsh hash.Hash, n uint64) {
|
||||
var b [8]byte
|
||||
binary.BigEndian.PutUint64(b[:], n)
|
||||
hsh.Write(b[:])
|
||||
return spiq.L2MessageHashes.Assign(pi.L2MessageHashes)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package execution
|
||||
|
||||
import (
|
||||
public_input "github.com/consensys/linea-monorepo/prover/public-input"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
|
||||
@@ -11,33 +12,36 @@ import (
|
||||
)
|
||||
|
||||
func TestPIConsistency(t *testing.T) {
|
||||
pi := FunctionalPublicInput{
|
||||
L2MessageHashes: make([][32]byte, 2),
|
||||
MaxNbL2MessageHashes: 3,
|
||||
FinalBlockNumber: 4,
|
||||
FinalBlockTimestamp: 5,
|
||||
FinalRollingHashNumber: 6,
|
||||
InitialBlockNumber: 1,
|
||||
InitialBlockTimestamp: 2,
|
||||
InitialRollingHashNumber: 3,
|
||||
ChainID: 7,
|
||||
pi := public_input.Execution{
|
||||
L2MessageHashes: make([][32]byte, 2),
|
||||
FinalBlockNumber: 4,
|
||||
FinalBlockTimestamp: 5,
|
||||
FinalRollingHashMsgNumber: 6,
|
||||
InitialBlockNumber: 1,
|
||||
InitialBlockTimestamp: 2,
|
||||
InitialRollingHashMsgNumber: 3,
|
||||
ChainID: 7,
|
||||
}
|
||||
|
||||
utils.FillRange(pi.DataChecksum[:], 10)
|
||||
utils.FillRange(pi.L2MessageHashes[0][:], 50)
|
||||
utils.FillRange(pi.L2MessageHashes[1][:], 90)
|
||||
utils.FillRange(pi.InitialStateRootHash[:], 130)
|
||||
utils.FillRange(pi.InitialRollingHash[:], 170)
|
||||
utils.FillRange(pi.InitialRollingHashUpdate[:], 170)
|
||||
utils.FillRange(pi.FinalStateRootHash[:], 210)
|
||||
utils.FillRange(pi.FinalRollingHash[:], 250)
|
||||
utils.FillRange(pi.FinalRollingHashUpdate[:], 250)
|
||||
utils.FillRange(pi.L2MessageServiceAddr[:], 40)
|
||||
|
||||
// state root hashes are field elements
|
||||
pi.InitialStateRootHash[0] &= 0x0f
|
||||
pi.FinalStateRootHash[0] &= 0x0f
|
||||
|
||||
snarkPi, err := pi.ToSnarkType()
|
||||
require.NoError(t, err)
|
||||
snarkPi := FunctionalPublicInputSnark{
|
||||
FunctionalPublicInputQSnark: FunctionalPublicInputQSnark{
|
||||
L2MessageHashes: L2MessageHashes{Values: make([][32]frontend.Variable, 3)},
|
||||
},
|
||||
}
|
||||
require.NoError(t, snarkPi.Assign(&pi))
|
||||
piSum := pi.Sum(nil)
|
||||
|
||||
snarkTestUtils.SnarkFunctionTest(func(api frontend.API) []frontend.Variable {
|
||||
|
||||
@@ -20,8 +20,8 @@ func checkPublicInputs(
|
||||
) {
|
||||
|
||||
var (
|
||||
finalRollingHash = internal.CombineBytesIntoElements(api, gnarkFuncInp.FinalRollingHash)
|
||||
initialRollingHash = internal.CombineBytesIntoElements(api, gnarkFuncInp.InitialRollingHash)
|
||||
finalRollingHash = internal.CombineBytesIntoElements(api, gnarkFuncInp.FinalRollingHashUpdate)
|
||||
initialRollingHash = internal.CombineBytesIntoElements(api, gnarkFuncInp.InitialRollingHashUpdate)
|
||||
execDataHash = execDataHash(api, wvc, wizardFuncInp)
|
||||
)
|
||||
|
||||
@@ -66,7 +66,7 @@ func checkPublicInputs(
|
||||
|
||||
api.AssertIsEqual(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.InitialRollingHashNumber.ID).Y,
|
||||
gnarkFuncInp.InitialRollingHashNumber,
|
||||
gnarkFuncInp.InitialRollingHashMsgNumber,
|
||||
)
|
||||
|
||||
api.AssertIsEqual(
|
||||
@@ -96,7 +96,7 @@ func checkPublicInputs(
|
||||
|
||||
api.AssertIsEqual(
|
||||
wvc.GetLocalPointEvalParams(wizardFuncInp.FinalRollingHashNumber.ID).Y,
|
||||
gnarkFuncInp.FinalRollingHashNumber,
|
||||
gnarkFuncInp.FinalRollingHashMsgNumber,
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"github.com/consensys/gnark/std/lookup/logderivlookup"
|
||||
"github.com/consensys/gnark/std/math/emulated"
|
||||
"github.com/consensys/linea-monorepo/prover/circuits/internal/plonk"
|
||||
"github.com/consensys/linea-monorepo/prover/utils"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
@@ -634,24 +633,6 @@ func Differences(api frontend.API, s []frontend.Variable) []frontend.Variable {
|
||||
return res
|
||||
}
|
||||
|
||||
func NewSliceOf32Array[T any](values [][32]T, maxLen int) Var32Slice {
|
||||
if maxLen < len(values) {
|
||||
panic("maxLen too small")
|
||||
}
|
||||
res := Var32Slice{
|
||||
Values: make([][32]frontend.Variable, maxLen),
|
||||
Length: len(values),
|
||||
}
|
||||
for i := range values {
|
||||
utils.Copy(res.Values[i][:], values[i][:])
|
||||
}
|
||||
var zeros [32]byte
|
||||
for i := len(values); i < maxLen; i++ {
|
||||
utils.Copy(res.Values[i][:], zeros[:])
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func Sum[T constraints.Integer](x ...T) T {
|
||||
var res T
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package pi_interconnection
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/consensys/linea-monorepo/prover/crypto/mimc"
|
||||
"hash"
|
||||
@@ -11,7 +10,6 @@ import (
|
||||
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
||||
"github.com/consensys/linea-monorepo/prover/backend/blobsubmission"
|
||||
decompression "github.com/consensys/linea-monorepo/prover/circuits/blobdecompression/v1"
|
||||
"github.com/consensys/linea-monorepo/prover/circuits/execution"
|
||||
"github.com/consensys/linea-monorepo/prover/circuits/internal"
|
||||
"github.com/consensys/linea-monorepo/prover/circuits/pi-interconnection/keccak"
|
||||
"github.com/consensys/linea-monorepo/prover/lib/compressor/blob"
|
||||
@@ -35,22 +33,22 @@ func (c *Compiled) Assign(r Request) (a Circuit, err error) {
|
||||
// TODO there is data duplication in the request. Check consistency
|
||||
|
||||
// infer config
|
||||
config, err := c.getConfig()
|
||||
cfg, err := c.getConfig()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
a = allocateCircuit(config)
|
||||
a = allocateCircuit(cfg)
|
||||
|
||||
if len(r.Decompressions) > config.MaxNbDecompression {
|
||||
err = errors.New("number of decompression proofs exceeds maximum")
|
||||
if len(r.Decompressions) > cfg.MaxNbDecompression {
|
||||
err = fmt.Errorf("failing CHECK_DECOMP_LIMIT:\n\t%d decompression proofs exceeds maximum of %d", len(r.Decompressions), cfg.MaxNbDecompression)
|
||||
return
|
||||
}
|
||||
if len(r.Executions) > config.MaxNbExecution {
|
||||
err = errors.New("number of execution proofs exceeds maximum")
|
||||
if len(r.Executions) > cfg.MaxNbExecution {
|
||||
err = fmt.Errorf("failing CHECK_EXEC_LIMIT:\n\t%d execution proofs exceeds maximum of %d", len(r.Executions), cfg.MaxNbExecution)
|
||||
return
|
||||
}
|
||||
if len(r.Decompressions)+len(r.Executions) > config.MaxNbCircuits && config.MaxNbCircuits > 0 {
|
||||
err = errors.New("total number of circuits exceeds maximum")
|
||||
if nbC := len(r.Decompressions) + len(r.Executions); nbC > cfg.MaxNbCircuits && cfg.MaxNbCircuits > 0 {
|
||||
err = fmt.Errorf("failing CHECK_CIRCUIT_LIMIT:\n\t%d circuits exceeds maximum of %d", nbC, cfg.MaxNbCircuits)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -68,8 +66,9 @@ func (c *Compiled) Assign(r Request) (a Circuit, err error) {
|
||||
}
|
||||
utils.Copy(a.ParentShnarf[:], prevShnarf)
|
||||
|
||||
hshM := mimc.NewMiMC()
|
||||
execDataChecksums := make([][]byte, 0, len(r.Executions))
|
||||
shnarfs := make([][]byte, config.MaxNbDecompression)
|
||||
shnarfs := make([][]byte, cfg.MaxNbDecompression)
|
||||
// Decompression FPI
|
||||
for i, p := range r.Decompressions {
|
||||
var blobData [1024 * 128]byte
|
||||
@@ -109,7 +108,7 @@ func (c *Compiled) Assign(r Request) (a Circuit, err error) {
|
||||
return
|
||||
}
|
||||
a.DecompressionFPIQ[i] = sfpi.FunctionalPublicInputQSnark
|
||||
if a.DecompressionPublicInput[i], err = fpi.Sum(); err != nil {
|
||||
if a.DecompressionPublicInput[i], err = fpi.Sum(decompression.WithHash(hshM)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -124,12 +123,12 @@ func (c *Compiled) Assign(r Request) (a Circuit, err error) {
|
||||
}
|
||||
|
||||
if prevShnarf = shnarf.Compute(); !bytes.Equal(prevShnarf, shnarfs[i]) {
|
||||
err = fmt.Errorf("shnarf mismatch, i:%d, shnarf: %x, prevShnarf: %x, ", i, shnarfs[i], prevShnarf)
|
||||
err = fmt.Errorf("decompression %d fails CHECK_SHNARF:\n\texpected: %x, computed: %x, ", i, shnarfs[i], prevShnarf)
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(execDataChecksums) != len(r.Executions) {
|
||||
err = errors.New("number of execution circuits does not match the number of batches in decompression circuits")
|
||||
err = fmt.Errorf("failing CHECK_NB_EXEC:\n\t%d execution circuits but %d batches in decompression circuits", len(r.Executions), len(execDataChecksums))
|
||||
return
|
||||
}
|
||||
var zero [32]byte
|
||||
@@ -169,127 +168,145 @@ func (c *Compiled) Assign(r Request) (a Circuit, err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO @Tabaie combine the following two checks
|
||||
if len(r.Decompressions) != 0 && !bytes.Equal(shnarfs[len(r.Decompressions)-1], aggregationFPI.FinalShnarf[:]) { // first condition is an edge case for tests
|
||||
err = errors.New("mismatch between decompression/aggregation-supplied shnarfs")
|
||||
err = fmt.Errorf("aggregation fails CHECK_FINAL_SHNARF:\n\tcomputed %x, given %x", shnarfs[len(r.Decompressions)-1], aggregationFPI.FinalShnarf)
|
||||
return
|
||||
}
|
||||
if len(r.Decompressions) == 0 && !bytes.Equal(aggregationFPI.ParentShnarf[:], aggregationFPI.FinalShnarf[:]) {
|
||||
err = fmt.Errorf("aggregation fails CHECK_FINAL_SHNARF:\n\tcomputed %x, given %x", aggregationFPI.ParentShnarf, aggregationFPI.FinalShnarf)
|
||||
return
|
||||
}
|
||||
aggregationFPI.NbDecompression = uint64(len(r.Decompressions))
|
||||
a.AggregationFPIQSnark = aggregationFPI.ToSnarkType().AggregationFPIQSnark
|
||||
|
||||
merkleNbLeaves := 1 << config.L2MsgMerkleDepth
|
||||
maxNbL2MessageHashes := config.L2MsgMaxNbMerkle * merkleNbLeaves
|
||||
merkleNbLeaves := 1 << cfg.L2MsgMerkleDepth
|
||||
maxNbL2MessageHashes := cfg.L2MsgMaxNbMerkle * merkleNbLeaves
|
||||
l2MessageHashes := make([][32]byte, 0, maxNbL2MessageHashes)
|
||||
|
||||
finalRollingHashNum, finalRollingHash := aggregationFPI.InitialRollingHashNumber, aggregationFPI.InitialRollingHash
|
||||
finalBlockTimestamp := aggregationFPI.LastFinalizedBlockTimestamp
|
||||
lastRollingHashUpdate, lastRollingHashMsg := aggregationFPI.LastFinalizedRollingHash, aggregationFPI.LastFinalizedRollingHashMsgNumber
|
||||
lastFinBlockNum, lastFinBlockTs := aggregationFPI.LastFinalizedBlockNumber, aggregationFPI.LastFinalizedBlockTimestamp
|
||||
lastFinalizedStateRootHash := aggregationFPI.InitialStateRootHash
|
||||
|
||||
// Execution FPI
|
||||
executionFPI := execution.FunctionalPublicInput{
|
||||
FinalStateRootHash: aggregationFPI.InitialStateRootHash,
|
||||
FinalBlockNumber: aggregationFPI.LastFinalizedBlockNumber,
|
||||
FinalBlockTimestamp: aggregationFPI.LastFinalizedBlockTimestamp,
|
||||
L2MessageServiceAddr: aggregationFPI.L2MessageServiceAddr,
|
||||
ChainID: aggregationFPI.ChainID,
|
||||
MaxNbL2MessageHashes: config.ExecutionMaxNbMsg,
|
||||
}
|
||||
|
||||
hshM := mimc.NewMiMC()
|
||||
for i := range a.ExecutionFPIQ {
|
||||
executionFPI.InitialRollingHash = [32]byte{}
|
||||
executionFPI.InitialRollingHashNumber = 0
|
||||
executionFPI.L2MessageHashes = nil
|
||||
|
||||
// pad things correctly to make the circuit's life a bit easier
|
||||
executionFPI.InitialBlockNumber = executionFPI.FinalBlockNumber + 1
|
||||
executionFPI.InitialStateRootHash = executionFPI.FinalStateRootHash
|
||||
executionFPI.InitialBlockTimestamp = executionFPI.FinalBlockTimestamp + 1
|
||||
// padding
|
||||
executionFPI := public_input.Execution{
|
||||
InitialBlockTimestamp: lastFinBlockTs + 1,
|
||||
InitialBlockNumber: lastFinBlockNum + 1,
|
||||
InitialStateRootHash: lastFinalizedStateRootHash,
|
||||
FinalStateRootHash: lastFinalizedStateRootHash,
|
||||
L2MessageServiceAddr: r.Aggregation.L2MessageServiceAddr,
|
||||
ChainID: r.Aggregation.ChainID,
|
||||
}
|
||||
executionFPI.FinalBlockNumber = executionFPI.InitialBlockNumber
|
||||
executionFPI.FinalBlockTimestamp = executionFPI.InitialBlockTimestamp
|
||||
|
||||
executionFPI.L2MessageServiceAddr = r.Aggregation.L2MessageServiceAddr
|
||||
executionFPI.ChainID = r.Aggregation.ChainID
|
||||
|
||||
a.ExecutionPublicInput[i] = 0 // unless...
|
||||
a.ExecutionPublicInput[i] = 0 // the aggregation circuit dictates that padded executions must have public input 0
|
||||
|
||||
if i < len(r.Executions) {
|
||||
if initial, final := r.Executions[i].InitialBlockTimestamp, finalBlockTimestamp; initial <= final {
|
||||
err = fmt.Errorf("execution #%d. initial block timestamp is not after the final block timestamp %d≤%d", i, initial, final)
|
||||
return
|
||||
}
|
||||
if initial, final := r.Executions[i].InitialBlockTimestamp, r.Executions[i].FinalBlockTimestamp; initial > final {
|
||||
err = fmt.Errorf("execution #%d. initial block timestamp is after the final block timestamp %d>%d", i, initial, final)
|
||||
return
|
||||
}
|
||||
executionFPI.InitialBlockTimestamp = r.Executions[i].InitialBlockTimestamp
|
||||
executionFPI.FinalRollingHash = r.Executions[i].FinalRollingHash
|
||||
executionFPI.FinalBlockNumber = r.Executions[i].FinalBlockNumber
|
||||
executionFPI.FinalBlockTimestamp = r.Executions[i].FinalBlockTimestamp
|
||||
finalBlockTimestamp = r.Executions[i].FinalBlockTimestamp
|
||||
executionFPI.FinalRollingHash = r.Executions[i].FinalRollingHash
|
||||
executionFPI.FinalRollingHashNumber = r.Executions[i].FinalRollingHashNumber
|
||||
executionFPI.FinalStateRootHash = r.Executions[i].FinalStateRootHash
|
||||
|
||||
executionFPI = r.Executions[i]
|
||||
copy(executionFPI.DataChecksum[:], execDataChecksums[i])
|
||||
executionFPI.L2MessageHashes = r.Executions[i].L2MsgHashes
|
||||
|
||||
l2MessageHashes = append(l2MessageHashes, r.Executions[i].L2MsgHashes...)
|
||||
|
||||
if got, want := &r.Executions[i].L2MessageServiceAddr, &r.Aggregation.L2MessageServiceAddr; *got != *want {
|
||||
err = fmt.Errorf("execution #%d. expected L2 service address %x, encountered %x", i, *want, *got)
|
||||
return
|
||||
}
|
||||
if got, want := executionFPI.ChainID, r.Aggregation.ChainID; got != want {
|
||||
err = fmt.Errorf("execution #%d. expected chain ID %x, encountered %x", i, want, got)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Executions[i].FinalRollingHashNumber != 0 { // if the rolling hash is being updated, record the change
|
||||
executionFPI.InitialRollingHash = finalRollingHash
|
||||
finalRollingHash = r.Executions[i].FinalRollingHash
|
||||
executionFPI.InitialRollingHashNumber = finalRollingHashNum
|
||||
finalRollingHashNum = r.Executions[i].FinalRollingHashNumber
|
||||
}
|
||||
|
||||
// compute the public input
|
||||
a.ExecutionPublicInput[i] = executionFPI.Sum(hshM)
|
||||
}
|
||||
|
||||
if snarkFPI, _err := executionFPI.ToSnarkType(); _err != nil {
|
||||
err = fmt.Errorf("execution #%d: %w", i, _err)
|
||||
if l := len(executionFPI.L2MessageHashes); l > cfg.ExecutionMaxNbMsg {
|
||||
err = fmt.Errorf("execution #%d fails CHECK_MSG_LIMIT:\n\thas %d messages. only %d allowed by config", i, l, cfg.ExecutionMaxNbMsg)
|
||||
return
|
||||
}
|
||||
l2MessageHashes = append(l2MessageHashes, executionFPI.L2MessageHashes...)
|
||||
|
||||
// consistency checks
|
||||
if initial := executionFPI.InitialStateRootHash; initial != lastFinalizedStateRootHash {
|
||||
err = fmt.Errorf("execution #%d fails CHECK_STATE_CONSEC:\n\tinitial state root hash does not match the last finalized\n\t%x≠%x", i, initial, lastFinalizedStateRootHash)
|
||||
return
|
||||
}
|
||||
if initial := executionFPI.InitialBlockNumber; initial != lastFinBlockNum+1 {
|
||||
err = fmt.Errorf("execution #%d fails CHECK_NUM_CONSEC:\n\tinitial block number %d is not right after to the last finalized %d", i, initial, lastFinBlockNum)
|
||||
return
|
||||
}
|
||||
if got, want := &executionFPI.L2MessageServiceAddr, &r.Aggregation.L2MessageServiceAddr; *got != *want {
|
||||
err = fmt.Errorf("execution #%d fails CHECK_SVC_ADDR:\n\texpected L2 service address %x, encountered %x", i, *want, *got)
|
||||
return
|
||||
}
|
||||
if got, want := executionFPI.ChainID, r.Aggregation.ChainID; got != want {
|
||||
err = fmt.Errorf("execution #%d fails CHECK_CHAIN_ID:\n\texpected %x, encountered %x", i, want, got)
|
||||
return
|
||||
}
|
||||
if initial := executionFPI.InitialBlockTimestamp; initial <= lastFinBlockTs {
|
||||
err = fmt.Errorf("execution #%d fails CHECK_TIME_INCREASE:\n\tinitial block timestamp is not after the final block timestamp from previous execution %d≤%d", i, initial, lastFinBlockTs)
|
||||
return
|
||||
}
|
||||
if first, last := executionFPI.InitialBlockNumber, executionFPI.FinalBlockNumber; first > last {
|
||||
err = fmt.Errorf("execution #%d fails CHECK_NUM_NODECREASE:\n\tinitial block number is greater than the final block number %d>%d", i, first, last)
|
||||
return
|
||||
}
|
||||
if first, last := executionFPI.InitialBlockTimestamp, executionFPI.FinalBlockTimestamp; first > last {
|
||||
err = fmt.Errorf("execution #%d fails CHECK_TIME_NODECREASE:\n\tinitial block timestamp is greater than the final block timestamp %d>%d", i, first, last)
|
||||
return
|
||||
}
|
||||
|
||||
// if there is a first, there shall be a last, no lesser than the first
|
||||
if executionFPI.FinalRollingHashMsgNumber < executionFPI.InitialRollingHashMsgNumber {
|
||||
err = fmt.Errorf("execution #%d fails CHECK_RHASH_NODECREASE:\n\tfinal rolling hash message number %d is less than the initial %d", i, executionFPI.FinalRollingHashMsgNumber, executionFPI.InitialRollingHashMsgNumber)
|
||||
return
|
||||
}
|
||||
|
||||
if (executionFPI.InitialRollingHashMsgNumber == 0) != (executionFPI.FinalRollingHashMsgNumber == 0) {
|
||||
err = fmt.Errorf("execution #%d fails CHECK_RHASH_FIRSTLAST:\n\tif there is a rolling hash update there must be both a first and a last.\n\tfirst update msg num = %d, last update msg num = %d", i, executionFPI.InitialRollingHashMsgNumber, executionFPI.FinalRollingHashMsgNumber)
|
||||
return
|
||||
}
|
||||
// TODO @Tabaie check that if the initial and final rolling hash msg nums were equal then so should the hashes, or decide not to
|
||||
|
||||
// consistency check and record keeping
|
||||
if executionFPI.InitialRollingHashMsgNumber != 0 { // there is an update
|
||||
if executionFPI.InitialRollingHashMsgNumber != lastRollingHashMsg+1 {
|
||||
err = fmt.Errorf("execution #%d fails CHECK_RHASH_CONSEC:\n\tinitial rolling hash message number %d is not right after the last finalized one %d", i, executionFPI.InitialRollingHashMsgNumber, lastRollingHashMsg)
|
||||
return
|
||||
}
|
||||
lastRollingHashMsg = executionFPI.FinalRollingHashMsgNumber
|
||||
lastRollingHashUpdate = executionFPI.FinalRollingHashUpdate
|
||||
}
|
||||
|
||||
lastFinBlockNum, lastFinBlockTs = executionFPI.FinalBlockNumber, executionFPI.FinalBlockTimestamp
|
||||
lastFinalizedStateRootHash = executionFPI.FinalStateRootHash
|
||||
|
||||
// convert to snark type
|
||||
if err = a.ExecutionFPIQ[i].Assign(&executionFPI); err != nil {
|
||||
err = fmt.Errorf("execution #%d: %w", i, err)
|
||||
return
|
||||
} else {
|
||||
a.ExecutionFPIQ[i] = snarkFPI.FunctionalPublicInputQSnark
|
||||
}
|
||||
}
|
||||
// consistency check
|
||||
if finalBlockTimestamp != aggregationFPI.FinalBlockTimestamp {
|
||||
err = fmt.Errorf("final block timestamps do not match: execution=%d, aggregation=%d",
|
||||
executionFPI.FinalBlockTimestamp, aggregationFPI.FinalBlockTimestamp)
|
||||
// consistency checks
|
||||
lastExec := &r.Executions[len(r.Executions)-1]
|
||||
|
||||
if lastExec.FinalBlockTimestamp != aggregationFPI.FinalBlockTimestamp {
|
||||
err = fmt.Errorf("aggregation fails CHECK_FINAL_TIME:\n\tfinal block timestamps do not match: execution=%d, aggregation=%d", lastExec.FinalBlockTimestamp, aggregationFPI.FinalBlockTimestamp)
|
||||
return
|
||||
}
|
||||
if executionFPI.FinalBlockNumber != aggregationFPI.FinalBlockNumber {
|
||||
err = fmt.Errorf("final block numbers do not match: execution=%d, aggregation=%d",
|
||||
executionFPI.FinalBlockNumber, aggregationFPI.FinalBlockNumber)
|
||||
if lastExec.FinalBlockNumber != aggregationFPI.FinalBlockNumber {
|
||||
err = fmt.Errorf("aggregation fails CHECK_FINAL_NUM:\n\tfinal block numbers do not match: execution=%d, aggregation=%d", lastExec.FinalBlockNumber, aggregationFPI.FinalBlockNumber)
|
||||
return
|
||||
}
|
||||
|
||||
if finalRollingHash != aggregationFPI.FinalRollingHash {
|
||||
err = fmt.Errorf("final rolling hashes do not match: execution=%x, aggregation=%x",
|
||||
executionFPI.FinalRollingHash, aggregationFPI.FinalRollingHash)
|
||||
if lastRollingHashUpdate != aggregationFPI.FinalRollingHash {
|
||||
err = fmt.Errorf("aggregation fails CHECK_FINAL_RHASH:\n\tfinal rolling hashes do not match: execution=%x, aggregation=%x", lastRollingHashUpdate, aggregationFPI.FinalRollingHash)
|
||||
return
|
||||
}
|
||||
|
||||
if finalRollingHashNum != aggregationFPI.FinalRollingHashNumber {
|
||||
err = fmt.Errorf("final rolling hash numbers do not match: execution=%v, aggregation=%v",
|
||||
executionFPI.FinalRollingHashNumber, aggregationFPI.FinalRollingHashNumber)
|
||||
if lastRollingHashMsg != aggregationFPI.FinalRollingHashNumber {
|
||||
err = fmt.Errorf("aggregation fails CHECK_FINAL_RHASH_NUM:\n\tfinal rolling hash numbers do not match: execution=%v, aggregation=%v", lastRollingHashMsg, aggregationFPI.FinalRollingHashNumber)
|
||||
return
|
||||
}
|
||||
|
||||
if len(l2MessageHashes) > maxNbL2MessageHashes {
|
||||
err = errors.New("too many L2 messages")
|
||||
err = fmt.Errorf("failing CHECK_MSG_TOTAL_LIMIT:\n\ttotal of %d L2 messages, more than the %d allowed by config", len(l2MessageHashes), maxNbL2MessageHashes)
|
||||
return
|
||||
}
|
||||
|
||||
if minNbRoots := (len(l2MessageHashes) + merkleNbLeaves - 1) / merkleNbLeaves; len(r.Aggregation.L2MsgRootHashes) < minNbRoots {
|
||||
err = fmt.Errorf("the %d merkle roots provided are too few to accommodate all %d execution messages. A minimum of %d is needed", len(r.Aggregation.L2MsgRootHashes), len(l2MessageHashes), minNbRoots)
|
||||
err = fmt.Errorf("failing CHECK_MERKLE_CAP0:\n\tthe %d merkle roots provided are too few to accommodate all %d execution messages. A minimum of %d is needed", len(r.Aggregation.L2MsgRootHashes), len(l2MessageHashes), minNbRoots)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -300,13 +317,13 @@ func (c *Compiled) Assign(r Request) (a Circuit, err error) {
|
||||
}
|
||||
computedRoot := MerkleRoot(&hshK, merkleNbLeaves, l2MessageHashes[i*merkleNbLeaves:min((i+1)*merkleNbLeaves, len(l2MessageHashes))])
|
||||
if !bytes.Equal(expectedRoot[:], computedRoot[:]) {
|
||||
err = errors.New("merkle root mismatch")
|
||||
err = fmt.Errorf("failing CHECK_MERKLE:\n\tcomputed merkle root %x, expected %x", computedRoot, expectedRoot)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// padding merkle root hashes
|
||||
emptyTree := make([][]byte, config.L2MsgMerkleDepth+1)
|
||||
emptyTree := make([][]byte, cfg.L2MsgMerkleDepth+1)
|
||||
emptyTree[0] = make([]byte, 64)
|
||||
hsh := sha3.NewLegacyKeccak256()
|
||||
for i := 1; i < len(emptyTree); i++ {
|
||||
@@ -317,15 +334,15 @@ func (c *Compiled) Assign(r Request) (a Circuit, err error) {
|
||||
}
|
||||
|
||||
// pad the merkle roots
|
||||
if len(r.Aggregation.L2MsgRootHashes) > config.L2MsgMaxNbMerkle {
|
||||
err = errors.New("more merkle trees than there is capacity")
|
||||
if len(r.Aggregation.L2MsgRootHashes) > cfg.L2MsgMaxNbMerkle {
|
||||
err = fmt.Errorf("failing CHECK_MERKLE_CAP1:\n\tgiven %d merkle roots, more than the %d allowed by config", len(r.Aggregation.L2MsgRootHashes), cfg.L2MsgMaxNbMerkle)
|
||||
return
|
||||
}
|
||||
|
||||
for i := len(r.Aggregation.L2MsgRootHashes); i < config.L2MsgMaxNbMerkle; i++ {
|
||||
for depth := config.L2MsgMerkleDepth; depth > 0; depth-- {
|
||||
for i := len(r.Aggregation.L2MsgRootHashes); i < cfg.L2MsgMaxNbMerkle; i++ {
|
||||
for depth := cfg.L2MsgMerkleDepth; depth > 0; depth-- {
|
||||
for j := 0; j < 1<<(depth-1); j++ {
|
||||
hshK.Skip(emptyTree[config.L2MsgMerkleDepth-depth])
|
||||
hshK.Skip(emptyTree[cfg.L2MsgMerkleDepth-depth])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,6 +355,13 @@ func (c *Compiled) Assign(r Request) (a Circuit, err error) {
|
||||
logrus.Infof("generating wizard proof for %d hashes from %d permutations", hshK.NbHashes(), hshK.MaxNbKeccakF())
|
||||
a.Keccak, err = hshK.Assign()
|
||||
|
||||
// These values are currently hard-coded in the circuit
|
||||
// This assignment is then redundant, but it helps with debugging in the test engine
|
||||
// TODO @Tabaie when we remove the hard-coding, this will still run correctly
|
||||
// but would be doubly redundant. We can remove it then.
|
||||
a.ChainID = r.Aggregation.ChainID
|
||||
a.L2MessageServiceAddr = r.Aggregation.L2MessageServiceAddr
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package pi_interconnection
|
||||
import (
|
||||
"errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"math"
|
||||
"math/big"
|
||||
"slices"
|
||||
|
||||
@@ -65,6 +64,10 @@ type Circuit struct {
|
||||
type compilationSuite = []func(*wizard.CompiledIOP)
|
||||
|
||||
func (c *Circuit) Define(api frontend.API) error {
|
||||
// TODO @Tabaie @alexandre.belling remove hard coded values once these are included in aggregation PI sum
|
||||
api.AssertIsEqual(c.ChainID, c.AggregationFPIQSnark.ChainID)
|
||||
api.AssertIsEqual(c.L2MessageServiceAddr[:], c.AggregationFPIQSnark.L2MessageServiceAddr)
|
||||
|
||||
maxNbDecompression, maxNbExecution := len(c.DecompressionPublicInput), len(c.ExecutionPublicInput)
|
||||
if len(c.DecompressionFPIQ) != maxNbDecompression || len(c.ExecutionFPIQ) != maxNbExecution {
|
||||
return errors.New("public / functional public input length mismatch")
|
||||
@@ -72,14 +75,15 @@ func (c *Circuit) Define(api frontend.API) error {
|
||||
|
||||
c.AggregationFPIQSnark.RangeCheck(api)
|
||||
|
||||
// implicit: CHECK_DECOMP_LIMIT
|
||||
rDecompression := internal.NewRange(api, c.NbDecompression, len(c.DecompressionPublicInput))
|
||||
hshK := c.Keccak.NewHasher(api)
|
||||
|
||||
// equivalently, nbBatchesSums[i] is the index of the first execution circuit associated with the i+1-st decompression circuit
|
||||
nbBatchesSums := rDecompression.PartialSumsF(func(i int) frontend.Variable { return c.DecompressionFPIQ[i].NbBatches })
|
||||
nbExecution := nbBatchesSums[len(nbBatchesSums)-1]
|
||||
nbExecution := nbBatchesSums[len(nbBatchesSums)-1] // implicit: CHECK_NB_EXEC
|
||||
|
||||
if c.MaxNbCircuits > 0 {
|
||||
if c.MaxNbCircuits > 0 { // CHECK_CIRCUIT_LIMIT
|
||||
api.AssertIsLessOrEqual(api.Add(nbExecution, c.NbDecompression), c.MaxNbCircuits)
|
||||
}
|
||||
|
||||
@@ -126,12 +130,15 @@ func (c *Circuit) Define(api frontend.API) error {
|
||||
}
|
||||
|
||||
shnarfs := ComputeShnarfs(&hshK, c.ParentShnarf, shnarfParams)
|
||||
// The circuit only has the last shnarf as input, therefore we do not perform
|
||||
// CHECK_SHNARF. However, since they are chained, the passing of CHECK_FINAL_SHNARF
|
||||
// implies that all shnarfs are correct.
|
||||
|
||||
// implicit: CHECK_EXEC_LIMIT
|
||||
rExecution := internal.NewRange(api, nbExecution, maxNbExecution)
|
||||
|
||||
initBlockNum, initRHashNum, initRHash := api.Add(c.LastFinalizedBlockNumber, 1), c.InitialRollingHashNumber, c.InitialRollingHash
|
||||
lastFinalizedBlockTime, initState := c.LastFinalizedBlockTimestamp, c.InitialStateRootHash
|
||||
finalRollingHash, finalRollingHashNum := c.InitialRollingHash, c.InitialRollingHashNumber
|
||||
finalBlockNum, finalRollingHashMsgNum, finalRollingHash := c.LastFinalizedBlockNumber, c.LastFinalizedRollingHashNumber, c.LastFinalizedRollingHash
|
||||
finalBlockTime, finalState := c.LastFinalizedBlockTimestamp, c.InitialStateRootHash
|
||||
var l2MessagesByByte [32][]internal.VarSlice
|
||||
|
||||
execMaxNbL2Msg := len(c.ExecutionFPIQ[0].L2MessageHashes.Values)
|
||||
@@ -143,41 +150,39 @@ func (c *Circuit) Define(api frontend.API) error {
|
||||
}
|
||||
}
|
||||
|
||||
comparator := cmp.NewBoundedComparator(api, new(big.Int).Lsh(big.NewInt(1), 64), false) // TODO does the "false" mean that the deltas are range checked?
|
||||
// we can "allow non-deterministic behavior" because all compared values have been range-checked
|
||||
comparator := cmp.NewBoundedComparator(api, new(big.Int).Lsh(big.NewInt(1), 65), true)
|
||||
// TODO try using lookups or crumb decomposition to make comparisons more efficient
|
||||
for i, piq := range c.ExecutionFPIQ {
|
||||
piq.RangeCheck(api)
|
||||
piq.RangeCheck(api) // CHECK_MSG_LIMIT
|
||||
|
||||
inRange := rExecution.InRange[i]
|
||||
rollingHashNotUpdated := api.Select(inRange, api.IsZero(piq.FinalRollingHashNumber), 1) // padded input past nbExecutions is not required to be 0. So we multiply by inRange
|
||||
|
||||
newFinalRollingHashNum := api.Select(rollingHashNotUpdated, finalRollingHashNum, piq.FinalRollingHashNumber)
|
||||
|
||||
nextExecInitBlockNum := api.Add(piq.FinalBlockNumber, 1)
|
||||
|
||||
api.AssertIsEqual(comparator.IsLess(lastFinalizedBlockTime, api.Select(inRange, piq.InitialBlockTimestamp, uint64(math.MaxUint64))), 1) // don't compare if not updating
|
||||
api.AssertIsEqual(comparator.IsLess(piq.InitialBlockTimestamp, api.Add(piq.FinalBlockTimestamp, 1)), 1)
|
||||
|
||||
api.AssertIsEqual(comparator.IsLess(initBlockNum, api.Select(inRange, nextExecInitBlockNum, uint64(math.MaxUint64))), 1)
|
||||
api.AssertIsEqual(comparator.IsLess(finalRollingHashNum, api.Add(newFinalRollingHashNum, rollingHashNotUpdated)), 1) // if the rolling hash is updated, check that it has increased
|
||||
|
||||
finalRollingHashNum = newFinalRollingHashNum
|
||||
copy(finalRollingHash[:], internal.SelectMany(api, rollingHashNotUpdated, finalRollingHash[:], piq.FinalRollingHash[:]))
|
||||
|
||||
pi := execution.FunctionalPublicInputSnark{
|
||||
FunctionalPublicInputQSnark: piq,
|
||||
InitialStateRootHash: initState,
|
||||
InitialBlockNumber: initBlockNum,
|
||||
InitialRollingHash: initRHash,
|
||||
InitialRollingHashNumber: api.Mul(initRHashNum, api.Sub(1, rollingHashNotUpdated)),
|
||||
ChainID: c.ChainID,
|
||||
L2MessageServiceAddr: c.L2MessageServiceAddr[:],
|
||||
InitialStateRootHash: finalState, // implicit CHECK_STATE_CONSEC
|
||||
InitialBlockNumber: api.Add(finalBlockNum, 1), // implicit CHECK_NUM_CONSEC
|
||||
ChainID: c.ChainID, // implicit CHECK_CHAIN_ID
|
||||
L2MessageServiceAddr: c.L2MessageServiceAddr[:], // implicit CHECK_SVC_ADDR
|
||||
}
|
||||
for j := range pi.InitialRollingHash {
|
||||
pi.InitialRollingHash[j] = api.Mul(initRHash[j], api.Sub(1, rollingHashNotUpdated))
|
||||
}
|
||||
initBlockNum, initRHashNum, initRHash = nextExecInitBlockNum, pi.FinalRollingHashNumber, pi.FinalRollingHash
|
||||
lastFinalizedBlockTime, initState = pi.FinalBlockTimestamp, pi.FinalStateRootHash
|
||||
|
||||
comparator.AssertIsLessEq(pi.InitialBlockTimestamp, pi.FinalBlockTimestamp) // CHECK_TIME_NODECREASE
|
||||
comparator.AssertIsLessEq(pi.InitialBlockNumber, pi.FinalBlockNumber) // CHECK_NUM_NODECREASE
|
||||
comparator.AssertIsLess(finalBlockTime, pi.InitialBlockTimestamp) // CHECK_TIME_INCREASE
|
||||
comparator.AssertIsLessEq(pi.InitialRollingHashMsgNumber, pi.FinalRollingHashMsgNumber) // CHECK_RHASH_NODECREASE
|
||||
|
||||
finalRhMsgNumZero := api.IsZero(piq.FinalRollingHashMsgNumber)
|
||||
api.AssertIsEqual(finalRhMsgNumZero, api.IsZero(piq.InitialRollingHashMsgNumber)) // CHECK_RHASH_FIRSTLAST
|
||||
rollingHashUpdated := api.Mul(inRange, api.Sub(1, finalRhMsgNumZero))
|
||||
|
||||
// CHECK_RHASH_CONSEC
|
||||
internal.AssertEqualIf(api, rollingHashUpdated, pi.InitialRollingHashMsgNumber, api.Add(finalRollingHashMsgNum, 1))
|
||||
finalRollingHashMsgNum = api.Select(rollingHashUpdated, pi.FinalRollingHashMsgNumber, finalRollingHashMsgNum)
|
||||
copy(finalRollingHash[:], internal.SelectMany(api, rollingHashUpdated, pi.FinalRollingHashUpdate[:], finalRollingHash[:]))
|
||||
|
||||
finalBlockTime = pi.FinalBlockTimestamp
|
||||
finalBlockNum = pi.FinalBlockNumber
|
||||
finalState = pi.FinalStateRootHash
|
||||
|
||||
api.AssertIsEqual(c.ExecutionPublicInput[i], api.Mul(rExecution.InRange[i], pi.Sum(api, hshM))) // "open" execution circuit public input
|
||||
|
||||
@@ -204,20 +209,23 @@ func (c *Circuit) Define(api frontend.API) error {
|
||||
}
|
||||
|
||||
pi := public_input.AggregationFPISnark{
|
||||
AggregationFPIQSnark: c.AggregationFPIQSnark,
|
||||
NbL2Messages: merkleLeavesConcat.Length,
|
||||
L2MsgMerkleTreeRoots: make([][32]frontend.Variable, c.L2MessageMaxNbMerkle),
|
||||
FinalBlockNumber: rExecution.LastF(func(i int) frontend.Variable { return c.ExecutionFPIQ[i].FinalBlockNumber }),
|
||||
AggregationFPIQSnark: c.AggregationFPIQSnark,
|
||||
NbL2Messages: merkleLeavesConcat.Length,
|
||||
L2MsgMerkleTreeRoots: make([][32]frontend.Variable, c.L2MessageMaxNbMerkle), // implicit CHECK_MERKLE_CAP1
|
||||
// implicit CHECK_FINAL_NUM
|
||||
FinalBlockNumber: rExecution.LastF(func(i int) frontend.Variable { return c.ExecutionFPIQ[i].FinalBlockNumber }),
|
||||
// implicit CHECK_FINAL_TIME
|
||||
FinalBlockTimestamp: rExecution.LastF(func(i int) frontend.Variable { return c.ExecutionFPIQ[i].FinalBlockTimestamp }),
|
||||
FinalShnarf: rDecompression.LastArray32(shnarfs),
|
||||
FinalRollingHash: finalRollingHash,
|
||||
FinalRollingHashNumber: finalRollingHashNum,
|
||||
FinalShnarf: rDecompression.LastArray32(shnarfs), // implicit CHECK_FINAL_SHNARF
|
||||
FinalRollingHash: finalRollingHash, // implicit CHECK_FINAL_RHASH
|
||||
FinalRollingHashNumber: finalRollingHashMsgNum, // implicit CHECK_FINAL_RHASH_NUM
|
||||
L2MsgMerkleTreeDepth: c.L2MessageMerkleDepth,
|
||||
}
|
||||
|
||||
quotient, remainder := internal.DivEuclidean(api, merkleLeavesConcat.Length, merkleNbLeaves)
|
||||
pi.NbL2MsgMerkleTreeRoots = api.Add(quotient, api.Sub(1, api.IsZero(remainder)))
|
||||
|
||||
comparator.AssertIsLessEq(pi.NbL2MsgMerkleTreeRoots, c.L2MessageMaxNbMerkle) // CHECK_MERKLE_CAP0
|
||||
// implicit CHECK_MERKLE
|
||||
for i := range pi.L2MsgMerkleTreeRoots {
|
||||
pi.L2MsgMerkleTreeRoots[i] = MerkleRootSnark(&hshK, merkleLeavesConcat.Values[i*merkleNbLeaves:(i+1)*merkleNbLeaves])
|
||||
}
|
||||
@@ -228,10 +236,6 @@ func (c *Circuit) Define(api frontend.API) error {
|
||||
api.AssertIsEqual(c.AggregationPublicInput[0], compress.ReadNum(api, aggregationPIBytes[:16], twoPow8))
|
||||
api.AssertIsEqual(c.AggregationPublicInput[1], compress.ReadNum(api, aggregationPIBytes[16:], twoPow8))
|
||||
|
||||
// TODO @Tabaie @alexandre.belling remove hard coded values once these are included in aggregation PI sum
|
||||
api.AssertIsEqual(c.ChainID, c.AggregationFPIQSnark.ChainID)
|
||||
api.AssertIsEqual(c.L2MessageServiceAddr[:], c.AggregationFPIQSnark.L2MessageServiceAddr)
|
||||
|
||||
return hshK.Finalize()
|
||||
}
|
||||
|
||||
@@ -275,9 +279,6 @@ func Compile(c config.PublicInput, wizardCompilationOpts ...func(iop *wizard.Com
|
||||
|
||||
circuit := allocateCircuit(c)
|
||||
circuit.Keccak = shc
|
||||
for i := range circuit.ExecutionFPIQ {
|
||||
circuit.ExecutionFPIQ[i].L2MessageHashes.Values = make([][32]frontend.Variable, c.ExecutionMaxNbMsg)
|
||||
}
|
||||
|
||||
return &Compiled{
|
||||
Circuit: &circuit,
|
||||
@@ -306,19 +307,25 @@ func (c *Compiled) getConfig() (config.PublicInput, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func allocateCircuit(c config.PublicInput) Circuit {
|
||||
return Circuit{
|
||||
DecompressionPublicInput: make([]frontend.Variable, c.MaxNbDecompression),
|
||||
ExecutionPublicInput: make([]frontend.Variable, c.MaxNbExecution),
|
||||
DecompressionFPIQ: make([]decompression.FunctionalPublicInputQSnark, c.MaxNbDecompression),
|
||||
ExecutionFPIQ: make([]execution.FunctionalPublicInputQSnark, c.MaxNbExecution),
|
||||
L2MessageMerkleDepth: c.L2MsgMerkleDepth,
|
||||
L2MessageMaxNbMerkle: c.L2MsgMaxNbMerkle,
|
||||
MaxNbCircuits: c.MaxNbCircuits,
|
||||
L2MessageServiceAddr: types.EthAddress(c.L2MsgServiceAddr),
|
||||
ChainID: c.ChainID,
|
||||
func allocateCircuit(cfg config.PublicInput) Circuit {
|
||||
res := Circuit{
|
||||
DecompressionPublicInput: make([]frontend.Variable, cfg.MaxNbDecompression),
|
||||
ExecutionPublicInput: make([]frontend.Variable, cfg.MaxNbExecution),
|
||||
DecompressionFPIQ: make([]decompression.FunctionalPublicInputQSnark, cfg.MaxNbDecompression),
|
||||
ExecutionFPIQ: make([]execution.FunctionalPublicInputQSnark, cfg.MaxNbExecution),
|
||||
L2MessageMerkleDepth: cfg.L2MsgMerkleDepth,
|
||||
L2MessageMaxNbMerkle: cfg.L2MsgMaxNbMerkle,
|
||||
MaxNbCircuits: cfg.MaxNbCircuits,
|
||||
L2MessageServiceAddr: types.EthAddress(cfg.L2MsgServiceAddr),
|
||||
ChainID: cfg.ChainID,
|
||||
UseGkrMimc: true,
|
||||
}
|
||||
|
||||
for i := range res.ExecutionFPIQ {
|
||||
res.ExecutionFPIQ[i].L2MessageHashes.Values = make([][32]frontend.Variable, cfg.ExecutionMaxNbMsg)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func newKeccakCompiler(c config.PublicInput) *keccak.StrictHasherCompiler {
|
||||
|
||||
@@ -107,7 +107,7 @@ func (c *testMerkleCircuit) Define(api frontend.API) error {
|
||||
|
||||
func TestMaxNbCircuitsSum(t *testing.T) {
|
||||
parameters := gopter.DefaultTestParameters()
|
||||
parameters.MinSuccessfulTests = 5
|
||||
parameters.MinSuccessfulTests = 3
|
||||
parameters.Rng.Seed(0x123456789abcdef0)
|
||||
|
||||
properties := gopter.NewProperties(parameters)
|
||||
|
||||
@@ -5,6 +5,7 @@ package pi_interconnection_test
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/require"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
@@ -63,28 +64,41 @@ func TestTinyTwoBatchBlob(t *testing.T) {
|
||||
|
||||
blob := blobtesting.TinyTwoBatchBlob(t)
|
||||
|
||||
const lastFinStateRootHash = 34
|
||||
stateRootHashes := [3][32]byte{
|
||||
internal.Uint64To32Bytes(lastFinStateRootHash),
|
||||
internal.Uint64To32Bytes(23),
|
||||
internal.Uint64To32Bytes(45),
|
||||
}
|
||||
|
||||
execReq := []public_input.Execution{{
|
||||
L2MsgHashes: [][32]byte{internal.Uint64To32Bytes(3)},
|
||||
InitialBlockTimestamp: 6,
|
||||
FinalStateRootHash: internal.Uint64To32Bytes(4),
|
||||
FinalBlockNumber: 5,
|
||||
FinalBlockTimestamp: 6,
|
||||
FinalRollingHash: internal.Uint64To32Bytes(7),
|
||||
FinalRollingHashNumber: 8,
|
||||
InitialBlockTimestamp: 6,
|
||||
FinalStateRootHash: stateRootHashes[1],
|
||||
FinalBlockNumber: 5,
|
||||
FinalBlockTimestamp: 6,
|
||||
FinalRollingHashUpdate: internal.Uint64To32Bytes(7),
|
||||
FinalRollingHashMsgNumber: 8,
|
||||
InitialRollingHashMsgNumber: 8,
|
||||
L2MessageHashes: [][32]byte{internal.Uint64To32Bytes(3)},
|
||||
InitialStateRootHash: stateRootHashes[0],
|
||||
InitialBlockNumber: 5,
|
||||
}, {
|
||||
L2MsgHashes: [][32]byte{internal.Uint64To32Bytes(9)},
|
||||
InitialBlockTimestamp: 7,
|
||||
FinalStateRootHash: internal.Uint64To32Bytes(10),
|
||||
FinalBlockNumber: 11,
|
||||
FinalBlockTimestamp: 12,
|
||||
FinalRollingHash: internal.Uint64To32Bytes(13),
|
||||
FinalRollingHashNumber: 14,
|
||||
L2MessageHashes: [][32]byte{internal.Uint64To32Bytes(9)},
|
||||
InitialBlockTimestamp: 7,
|
||||
FinalStateRootHash: stateRootHashes[2],
|
||||
FinalBlockNumber: 11,
|
||||
FinalBlockTimestamp: 12,
|
||||
FinalRollingHashUpdate: internal.Uint64To32Bytes(13),
|
||||
FinalRollingHashMsgNumber: 14,
|
||||
InitialRollingHashMsgNumber: 9,
|
||||
InitialStateRootHash: stateRootHashes[1],
|
||||
InitialBlockNumber: 6,
|
||||
}}
|
||||
|
||||
blobReq := blobsubmission.Request{
|
||||
Eip4844Enabled: true,
|
||||
CompressedData: base64.StdEncoding.EncodeToString(blob),
|
||||
ParentStateRootHash: utils.FmtIntHex32Bytes(1),
|
||||
ParentStateRootHash: utils.FmtIntHex32Bytes(lastFinStateRootHash),
|
||||
FinalStateRootHash: utils.HexEncodeToString(execReq[1].FinalStateRootHash[:]),
|
||||
PrevShnarf: utils.FmtIntHex32Bytes(2),
|
||||
}
|
||||
@@ -92,7 +106,7 @@ func TestTinyTwoBatchBlob(t *testing.T) {
|
||||
blobResp, err := blobsubmission.CraftResponse(&blobReq)
|
||||
assert.NoError(t, err)
|
||||
|
||||
merkleRoots := aggregation.PackInMiniTrees(circuittesting.BlocksToHex(execReq[0].L2MsgHashes, execReq[1].L2MsgHashes))
|
||||
merkleRoots := aggregation.PackInMiniTrees(circuittesting.BlocksToHex(execReq[0].L2MessageHashes, execReq[1].L2MessageHashes))
|
||||
|
||||
req := pi_interconnection.Request{
|
||||
Decompressions: []blobsubmission.Response{*blobResp},
|
||||
@@ -105,10 +119,10 @@ func TestTinyTwoBatchBlob(t *testing.T) {
|
||||
FinalTimestamp: uint(execReq[1].FinalBlockTimestamp),
|
||||
LastFinalizedBlockNumber: 4,
|
||||
FinalBlockNumber: uint(execReq[1].FinalBlockNumber),
|
||||
LastFinalizedL1RollingHash: utils.FmtIntHex32Bytes(7),
|
||||
L1RollingHash: utils.HexEncodeToString(execReq[1].FinalRollingHash[:]),
|
||||
LastFinalizedL1RollingHash: utils.FmtIntHex32Bytes(13),
|
||||
L1RollingHash: utils.HexEncodeToString(execReq[1].FinalRollingHashUpdate[:]),
|
||||
LastFinalizedL1RollingHashMessageNumber: 7,
|
||||
L1RollingHashMessageNumber: uint(execReq[1].FinalRollingHashNumber),
|
||||
L1RollingHashMessageNumber: uint(execReq[1].FinalRollingHashMsgNumber),
|
||||
L2MsgRootHashes: merkleRoots,
|
||||
L2MsgMerkleTreeDepth: 5,
|
||||
},
|
||||
@@ -121,37 +135,49 @@ func TestTwoTwoBatchBlobs(t *testing.T) {
|
||||
blobs := blobtesting.ConsecutiveBlobs(t, 2, 2)
|
||||
|
||||
execReq := []public_input.Execution{{
|
||||
L2MsgHashes: [][32]byte{internal.Uint64To32Bytes(3)},
|
||||
InitialBlockTimestamp: 6,
|
||||
FinalStateRootHash: internal.Uint64To32Bytes(4),
|
||||
FinalBlockNumber: 5,
|
||||
FinalBlockTimestamp: 6,
|
||||
FinalRollingHash: internal.Uint64To32Bytes(7),
|
||||
FinalRollingHashNumber: 8,
|
||||
L2MessageHashes: [][32]byte{internal.Uint64To32Bytes(3)},
|
||||
InitialBlockTimestamp: 6,
|
||||
FinalStateRootHash: internal.Uint64To32Bytes(4),
|
||||
FinalBlockNumber: 5,
|
||||
FinalBlockTimestamp: 6,
|
||||
FinalRollingHashUpdate: internal.Uint64To32Bytes(7),
|
||||
FinalRollingHashMsgNumber: 8,
|
||||
InitialStateRootHash: internal.Uint64To32Bytes(1),
|
||||
InitialBlockNumber: 5,
|
||||
InitialRollingHashMsgNumber: 8,
|
||||
}, {
|
||||
L2MsgHashes: [][32]byte{internal.Uint64To32Bytes(9)},
|
||||
InitialBlockTimestamp: 7,
|
||||
FinalStateRootHash: internal.Uint64To32Bytes(10),
|
||||
FinalBlockNumber: 11,
|
||||
FinalBlockTimestamp: 12,
|
||||
FinalRollingHash: internal.Uint64To32Bytes(13),
|
||||
FinalRollingHashNumber: 14,
|
||||
L2MessageHashes: [][32]byte{internal.Uint64To32Bytes(9)},
|
||||
InitialBlockTimestamp: 7,
|
||||
InitialStateRootHash: internal.Uint64To32Bytes(4),
|
||||
InitialBlockNumber: 6,
|
||||
InitialRollingHashMsgNumber: 9,
|
||||
FinalStateRootHash: internal.Uint64To32Bytes(10),
|
||||
FinalBlockNumber: 11,
|
||||
FinalBlockTimestamp: 12,
|
||||
FinalRollingHashUpdate: internal.Uint64To32Bytes(13),
|
||||
FinalRollingHashMsgNumber: 14,
|
||||
}, {
|
||||
L2MsgHashes: [][32]byte{internal.Uint64To32Bytes(15)},
|
||||
InitialBlockTimestamp: 13,
|
||||
FinalStateRootHash: internal.Uint64To32Bytes(16),
|
||||
FinalBlockNumber: 17,
|
||||
FinalBlockTimestamp: 18,
|
||||
FinalRollingHash: internal.Uint64To32Bytes(19),
|
||||
FinalRollingHashNumber: 20,
|
||||
L2MessageHashes: [][32]byte{internal.Uint64To32Bytes(15)},
|
||||
InitialBlockTimestamp: 13,
|
||||
InitialBlockNumber: 12,
|
||||
InitialStateRootHash: internal.Uint64To32Bytes(10),
|
||||
InitialRollingHashMsgNumber: 15,
|
||||
FinalStateRootHash: internal.Uint64To32Bytes(16),
|
||||
FinalBlockNumber: 17,
|
||||
FinalBlockTimestamp: 18,
|
||||
FinalRollingHashUpdate: internal.Uint64To32Bytes(19),
|
||||
FinalRollingHashMsgNumber: 20,
|
||||
}, {
|
||||
L2MsgHashes: [][32]byte{internal.Uint64To32Bytes(21)},
|
||||
InitialBlockTimestamp: 19,
|
||||
FinalStateRootHash: internal.Uint64To32Bytes(22),
|
||||
FinalBlockNumber: 23,
|
||||
FinalBlockTimestamp: 24,
|
||||
FinalRollingHash: internal.Uint64To32Bytes(25),
|
||||
FinalRollingHashNumber: 26,
|
||||
InitialBlockNumber: 18,
|
||||
InitialStateRootHash: internal.Uint64To32Bytes(16),
|
||||
L2MessageHashes: [][32]byte{internal.Uint64To32Bytes(21)},
|
||||
InitialBlockTimestamp: 19,
|
||||
InitialRollingHashMsgNumber: 21,
|
||||
FinalStateRootHash: internal.Uint64To32Bytes(22),
|
||||
FinalBlockNumber: 23,
|
||||
FinalBlockTimestamp: 24,
|
||||
FinalRollingHashUpdate: internal.Uint64To32Bytes(25),
|
||||
FinalRollingHashMsgNumber: 26,
|
||||
}}
|
||||
|
||||
blobReq0 := blobsubmission.Request{
|
||||
@@ -163,7 +189,7 @@ func TestTwoTwoBatchBlobs(t *testing.T) {
|
||||
}
|
||||
|
||||
blobResp0, err := blobsubmission.CraftResponse(&blobReq0)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
blobReq1 := blobsubmission.Request{
|
||||
Eip4844Enabled: true,
|
||||
@@ -174,9 +200,9 @@ func TestTwoTwoBatchBlobs(t *testing.T) {
|
||||
}
|
||||
|
||||
blobResp1, err := blobsubmission.CraftResponse(&blobReq1)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
merkleRoots := aggregation.PackInMiniTrees(circuittesting.BlocksToHex(execReq[0].L2MsgHashes, execReq[1].L2MsgHashes, execReq[2].L2MsgHashes, execReq[3].L2MsgHashes))
|
||||
merkleRoots := aggregation.PackInMiniTrees(circuittesting.BlocksToHex(execReq[0].L2MessageHashes, execReq[1].L2MessageHashes, execReq[2].L2MessageHashes, execReq[3].L2MessageHashes))
|
||||
|
||||
req := pi_interconnection.Request{
|
||||
Decompressions: []blobsubmission.Response{*blobResp0, *blobResp1},
|
||||
@@ -190,9 +216,9 @@ func TestTwoTwoBatchBlobs(t *testing.T) {
|
||||
LastFinalizedBlockNumber: 4,
|
||||
FinalBlockNumber: uint(execReq[3].FinalBlockNumber),
|
||||
LastFinalizedL1RollingHash: utils.FmtIntHex32Bytes(7),
|
||||
L1RollingHash: utils.HexEncodeToString(execReq[3].FinalRollingHash[:]),
|
||||
L1RollingHash: utils.HexEncodeToString(execReq[3].FinalRollingHashUpdate[:]),
|
||||
LastFinalizedL1RollingHashMessageNumber: 7,
|
||||
L1RollingHashMessageNumber: uint(execReq[3].FinalRollingHashNumber),
|
||||
L1RollingHashMessageNumber: uint(execReq[3].FinalRollingHashMsgNumber),
|
||||
L2MsgRootHashes: merkleRoots,
|
||||
L2MsgMerkleTreeDepth: 5,
|
||||
},
|
||||
|
||||
@@ -33,16 +33,19 @@ func AssignSingleBlockBlob(t require.TestingT) pi_interconnection.Request {
|
||||
assert.NoError(t, err)
|
||||
|
||||
execReq := public_input.Execution{
|
||||
L2MsgHashes: [][32]byte{internal.Uint64To32Bytes(4)},
|
||||
InitialBlockTimestamp: 7,
|
||||
FinalStateRootHash: finalStateRootHash,
|
||||
FinalBlockNumber: 9,
|
||||
FinalBlockTimestamp: 10,
|
||||
FinalRollingHash: internal.Uint64To32Bytes(11),
|
||||
FinalRollingHashNumber: 12,
|
||||
L2MessageHashes: [][32]byte{internal.Uint64To32Bytes(4)},
|
||||
InitialBlockTimestamp: 7,
|
||||
FinalStateRootHash: finalStateRootHash,
|
||||
FinalBlockNumber: 9,
|
||||
FinalBlockTimestamp: 10,
|
||||
FinalRollingHashUpdate: internal.Uint64To32Bytes(11),
|
||||
FinalRollingHashMsgNumber: 9,
|
||||
InitialRollingHashMsgNumber: 9,
|
||||
InitialBlockNumber: 6,
|
||||
InitialStateRootHash: internal.Uint64To32Bytes(1),
|
||||
}
|
||||
|
||||
merkleRoots := aggregation.PackInMiniTrees(test_utils.BlocksToHex(execReq.L2MsgHashes))
|
||||
merkleRoots := aggregation.PackInMiniTrees(test_utils.BlocksToHex(execReq.L2MessageHashes))
|
||||
|
||||
return pi_interconnection.Request{
|
||||
Decompressions: []blobsubmission.Response{*blobResp},
|
||||
@@ -56,9 +59,9 @@ func AssignSingleBlockBlob(t require.TestingT) pi_interconnection.Request {
|
||||
LastFinalizedBlockNumber: 5,
|
||||
FinalBlockNumber: uint(execReq.FinalBlockNumber),
|
||||
LastFinalizedL1RollingHash: utils.FmtIntHex32Bytes(7),
|
||||
L1RollingHash: utils.HexEncodeToString(execReq.FinalRollingHash[:]),
|
||||
L1RollingHash: utils.HexEncodeToString(execReq.FinalRollingHashUpdate[:]),
|
||||
LastFinalizedL1RollingHashMessageNumber: 8,
|
||||
L1RollingHashMessageNumber: uint(execReq.FinalRollingHashNumber),
|
||||
L1RollingHashMessageNumber: uint(execReq.FinalRollingHashMsgNumber),
|
||||
L2MsgRootHashes: merkleRoots,
|
||||
L2MsgMerkleTreeDepth: 5,
|
||||
},
|
||||
|
||||
@@ -96,33 +96,33 @@ func (p Aggregation) GetPublicInputHex() string {
|
||||
|
||||
// AggregationFPI holds the same info as public_input.Aggregation, except in parsed form
|
||||
type AggregationFPI struct {
|
||||
ParentShnarf [32]byte
|
||||
NbDecompression uint64
|
||||
InitialStateRootHash [32]byte
|
||||
LastFinalizedBlockNumber uint64
|
||||
LastFinalizedBlockTimestamp uint64
|
||||
InitialRollingHash [32]byte
|
||||
InitialRollingHashNumber uint64
|
||||
ChainID uint64 // for now we're forcing all executions to have the same chain ID
|
||||
L2MessageServiceAddr types.EthAddress
|
||||
NbL2Messages uint64 // TODO not used in hash. delete if not necessary
|
||||
L2MsgMerkleTreeRoots [][32]byte
|
||||
FinalBlockNumber uint64
|
||||
FinalBlockTimestamp uint64
|
||||
FinalRollingHash [32]byte
|
||||
FinalRollingHashNumber uint64
|
||||
FinalShnarf [32]byte
|
||||
L2MsgMerkleTreeDepth int
|
||||
ParentShnarf [32]byte
|
||||
NbDecompression uint64
|
||||
InitialStateRootHash [32]byte
|
||||
LastFinalizedBlockNumber uint64
|
||||
LastFinalizedBlockTimestamp uint64
|
||||
LastFinalizedRollingHash [32]byte
|
||||
LastFinalizedRollingHashMsgNumber uint64
|
||||
ChainID uint64 // for now we're forcing all executions to have the same chain ID
|
||||
L2MessageServiceAddr types.EthAddress
|
||||
NbL2Messages uint64 // TODO not used in hash. delete if not necessary
|
||||
L2MsgMerkleTreeRoots [][32]byte
|
||||
FinalBlockNumber uint64
|
||||
FinalBlockTimestamp uint64
|
||||
FinalRollingHash [32]byte
|
||||
FinalRollingHashNumber uint64
|
||||
FinalShnarf [32]byte
|
||||
L2MsgMerkleTreeDepth int
|
||||
}
|
||||
|
||||
func (pi *AggregationFPI) ToSnarkType() AggregationFPISnark {
|
||||
s := AggregationFPISnark{
|
||||
AggregationFPIQSnark: AggregationFPIQSnark{
|
||||
LastFinalizedBlockNumber: pi.LastFinalizedBlockNumber,
|
||||
LastFinalizedBlockTimestamp: pi.LastFinalizedBlockTimestamp,
|
||||
InitialRollingHash: [32]frontend.Variable{},
|
||||
InitialRollingHashNumber: pi.InitialRollingHashNumber,
|
||||
InitialStateRootHash: pi.InitialStateRootHash[:],
|
||||
LastFinalizedBlockNumber: pi.LastFinalizedBlockNumber,
|
||||
LastFinalizedBlockTimestamp: pi.LastFinalizedBlockTimestamp,
|
||||
LastFinalizedRollingHash: [32]frontend.Variable{},
|
||||
LastFinalizedRollingHashNumber: pi.LastFinalizedRollingHashMsgNumber,
|
||||
InitialStateRootHash: pi.InitialStateRootHash[:],
|
||||
|
||||
NbDecompression: pi.NbDecompression,
|
||||
ChainID: pi.ChainID,
|
||||
@@ -136,7 +136,7 @@ func (pi *AggregationFPI) ToSnarkType() AggregationFPISnark {
|
||||
}
|
||||
|
||||
utils.Copy(s.FinalRollingHash[:], pi.FinalRollingHash[:])
|
||||
utils.Copy(s.InitialRollingHash[:], pi.InitialRollingHash[:])
|
||||
utils.Copy(s.LastFinalizedRollingHash[:], pi.LastFinalizedRollingHash[:])
|
||||
utils.Copy(s.ParentShnarf[:], pi.ParentShnarf[:])
|
||||
utils.Copy(s.FinalShnarf[:], pi.FinalShnarf[:])
|
||||
|
||||
@@ -148,15 +148,15 @@ func (pi *AggregationFPI) ToSnarkType() AggregationFPISnark {
|
||||
}
|
||||
|
||||
type AggregationFPIQSnark struct {
|
||||
ParentShnarf [32]frontend.Variable
|
||||
NbDecompression frontend.Variable
|
||||
InitialStateRootHash frontend.Variable
|
||||
LastFinalizedBlockNumber frontend.Variable
|
||||
LastFinalizedBlockTimestamp frontend.Variable
|
||||
InitialRollingHash [32]frontend.Variable
|
||||
InitialRollingHashNumber frontend.Variable
|
||||
ChainID frontend.Variable // WARNING: Currently not bound in Sum
|
||||
L2MessageServiceAddr frontend.Variable // WARNING: Currently not bound in Sum
|
||||
ParentShnarf [32]frontend.Variable
|
||||
NbDecompression frontend.Variable
|
||||
InitialStateRootHash frontend.Variable
|
||||
LastFinalizedBlockNumber frontend.Variable
|
||||
LastFinalizedBlockTimestamp frontend.Variable
|
||||
LastFinalizedRollingHash [32]frontend.Variable
|
||||
LastFinalizedRollingHashNumber frontend.Variable
|
||||
ChainID frontend.Variable // WARNING: Currently not bound in Sum
|
||||
L2MessageServiceAddr frontend.Variable // WARNING: Currently not bound in Sum
|
||||
}
|
||||
|
||||
type AggregationFPISnark struct {
|
||||
@@ -176,16 +176,16 @@ type AggregationFPISnark struct {
|
||||
// NewAggregationFPI does NOT set all fields, only the ones covered in public_input.Aggregation
|
||||
func NewAggregationFPI(fpi *Aggregation) (s *AggregationFPI, err error) {
|
||||
s = &AggregationFPI{
|
||||
LastFinalizedBlockNumber: uint64(fpi.LastFinalizedBlockNumber),
|
||||
LastFinalizedBlockTimestamp: uint64(fpi.ParentAggregationLastBlockTimestamp),
|
||||
InitialRollingHashNumber: uint64(fpi.LastFinalizedL1RollingHashMessageNumber),
|
||||
L2MsgMerkleTreeRoots: make([][32]byte, len(fpi.L2MsgRootHashes)),
|
||||
FinalBlockNumber: uint64(fpi.FinalBlockNumber),
|
||||
FinalBlockTimestamp: uint64(fpi.FinalTimestamp),
|
||||
FinalRollingHashNumber: uint64(fpi.L1RollingHashMessageNumber),
|
||||
L2MsgMerkleTreeDepth: fpi.L2MsgMerkleTreeDepth,
|
||||
ChainID: fpi.ChainID,
|
||||
L2MessageServiceAddr: fpi.L2MessageServiceAddr,
|
||||
LastFinalizedBlockNumber: uint64(fpi.LastFinalizedBlockNumber),
|
||||
LastFinalizedBlockTimestamp: uint64(fpi.ParentAggregationLastBlockTimestamp),
|
||||
LastFinalizedRollingHashMsgNumber: uint64(fpi.LastFinalizedL1RollingHashMessageNumber),
|
||||
L2MsgMerkleTreeRoots: make([][32]byte, len(fpi.L2MsgRootHashes)),
|
||||
FinalBlockNumber: uint64(fpi.FinalBlockNumber),
|
||||
FinalBlockTimestamp: uint64(fpi.FinalTimestamp),
|
||||
FinalRollingHashNumber: uint64(fpi.L1RollingHashMessageNumber),
|
||||
L2MsgMerkleTreeDepth: fpi.L2MsgMerkleTreeDepth,
|
||||
ChainID: fpi.ChainID,
|
||||
L2MessageServiceAddr: fpi.L2MessageServiceAddr,
|
||||
}
|
||||
|
||||
if err = copyFromHex(s.InitialStateRootHash[:], fpi.ParentStateRootHash); err != nil {
|
||||
@@ -194,7 +194,7 @@ func NewAggregationFPI(fpi *Aggregation) (s *AggregationFPI, err error) {
|
||||
if err = copyFromHex(s.FinalRollingHash[:], fpi.L1RollingHash); err != nil {
|
||||
return
|
||||
}
|
||||
if err = copyFromHex(s.InitialRollingHash[:], fpi.LastFinalizedL1RollingHash); err != nil {
|
||||
if err = copyFromHex(s.LastFinalizedRollingHash[:], fpi.LastFinalizedL1RollingHash); err != nil {
|
||||
return
|
||||
}
|
||||
if err = copyFromHex(s.ParentShnarf[:], fpi.ParentAggregationFinalShnarf); err != nil {
|
||||
@@ -221,9 +221,9 @@ func (pi *AggregationFPISnark) Sum(api frontend.API, hash keccak.BlockHasher) [3
|
||||
utils.ToBytes(api, pi.FinalBlockTimestamp),
|
||||
utils.ToBytes(api, pi.LastFinalizedBlockNumber),
|
||||
utils.ToBytes(api, pi.FinalBlockNumber),
|
||||
pi.InitialRollingHash,
|
||||
pi.LastFinalizedRollingHash,
|
||||
pi.FinalRollingHash,
|
||||
utils.ToBytes(api, pi.InitialRollingHashNumber),
|
||||
utils.ToBytes(api, pi.LastFinalizedRollingHashNumber),
|
||||
utils.ToBytes(api, pi.FinalRollingHashNumber),
|
||||
utils.ToBytes(api, pi.L2MsgMerkleTreeDepth),
|
||||
hash.Sum(pi.NbL2MsgMerkleTreeRoots, pi.L2MsgMerkleTreeRoots...),
|
||||
@@ -237,7 +237,7 @@ func (pi *AggregationFPISnark) Sum(api frontend.API, hash keccak.BlockHasher) [3
|
||||
|
||||
func (pi *AggregationFPIQSnark) RangeCheck(api frontend.API) {
|
||||
rc := rangecheck.New(api)
|
||||
for _, v := range append(slices.Clone(pi.InitialRollingHash[:]), pi.ParentShnarf[:]...) {
|
||||
for _, v := range append(slices.Clone(pi.LastFinalizedRollingHash[:]), pi.ParentShnarf[:]...) {
|
||||
rc.Check(v, 8)
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ func (pi *AggregationFPIQSnark) RangeCheck(api frontend.API) {
|
||||
// each comparison in turn ensures that its final value is within a reasonable, less than 100 bit range
|
||||
rc.Check(pi.LastFinalizedBlockTimestamp, 64)
|
||||
rc.Check(pi.LastFinalizedBlockNumber, 64)
|
||||
rc.Check(pi.InitialRollingHashNumber, 64)
|
||||
rc.Check(pi.LastFinalizedRollingHashNumber, 64)
|
||||
// not checking L2MsgServiceAddr as its range is never assumed in the pi circuit
|
||||
// not checking NbDecompressions as the NewRange in the pi circuit range checks it; TODO do it here instead
|
||||
}
|
||||
|
||||
@@ -1,72 +1,80 @@
|
||||
package public_input
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
|
||||
"github.com/consensys/linea-monorepo/prover/crypto/mimc"
|
||||
"github.com/consensys/linea-monorepo/prover/maths/field"
|
||||
|
||||
"github.com/consensys/linea-monorepo/prover/utils"
|
||||
"github.com/consensys/linea-monorepo/prover/utils/types"
|
||||
)
|
||||
|
||||
type Execution struct {
|
||||
L2MsgHashes [][32]byte
|
||||
L2MessageServiceAddr types.EthAddress
|
||||
ChainID uint64
|
||||
InitialBlockTimestamp uint64
|
||||
FinalStateRootHash [32]byte
|
||||
FinalBlockNumber uint64
|
||||
FinalBlockTimestamp uint64
|
||||
FinalRollingHash [32]byte
|
||||
FinalRollingHashNumber uint64
|
||||
L2MessageServiceAddr types.EthAddress
|
||||
ChainID uint64
|
||||
InitialBlockTimestamp uint64
|
||||
FinalStateRootHash [32]byte
|
||||
FinalBlockNumber uint64
|
||||
FinalBlockTimestamp uint64
|
||||
FinalRollingHashUpdate [32]byte
|
||||
FinalRollingHashMsgNumber uint64
|
||||
InitialRollingHashUpdate [32]byte
|
||||
InitialRollingHashMsgNumber uint64
|
||||
DataChecksum [32]byte
|
||||
L2MessageHashes [][32]byte
|
||||
InitialStateRootHash [32]byte
|
||||
InitialBlockNumber uint64
|
||||
}
|
||||
|
||||
type ExecutionSerializable struct {
|
||||
ChainID uint64 `json:"chainID"`
|
||||
L2MessageServiceAddr types.EthAddress `json:"l2MessageServiceAddr"`
|
||||
InitialBlockTimestamp uint64 `json:"initialBlockTimestamp"`
|
||||
L2MsgHashes []string `json:"l2MsgHashes"`
|
||||
FinalStateRootHash string `json:"finalStateRootHash"`
|
||||
FinalBlockNumber uint64 `json:"finalBlockNumber"`
|
||||
FinalBlockTimestamp uint64 `json:"finalBlockTimestamp"`
|
||||
FinalRollingHash string `json:"finalRollingHash"`
|
||||
FinalRollingHashNumber uint64 `json:"finalRollingHashNumber"`
|
||||
func (pi *Execution) Sum(hsh hash.Hash) []byte {
|
||||
if hsh == nil {
|
||||
hsh = mimc.NewMiMC()
|
||||
}
|
||||
|
||||
hsh.Reset()
|
||||
for i := range pi.L2MessageHashes {
|
||||
hsh.Write(pi.L2MessageHashes[i][:16])
|
||||
hsh.Write(pi.L2MessageHashes[i][16:])
|
||||
}
|
||||
l2MessagesSum := hsh.Sum(nil)
|
||||
|
||||
hsh.Reset()
|
||||
|
||||
hsh.Write(pi.DataChecksum[:])
|
||||
hsh.Write(l2MessagesSum)
|
||||
hsh.Write(pi.FinalStateRootHash[:])
|
||||
|
||||
writeNum(hsh, pi.FinalBlockNumber)
|
||||
writeNum(hsh, pi.FinalBlockTimestamp)
|
||||
hsh.Write(pi.FinalRollingHashUpdate[:16])
|
||||
hsh.Write(pi.FinalRollingHashUpdate[16:])
|
||||
writeNum(hsh, pi.FinalRollingHashMsgNumber)
|
||||
hsh.Write(pi.InitialStateRootHash[:])
|
||||
writeNum(hsh, pi.InitialBlockNumber)
|
||||
writeNum(hsh, pi.InitialBlockTimestamp)
|
||||
hsh.Write(pi.InitialRollingHashUpdate[:16])
|
||||
hsh.Write(pi.InitialRollingHashUpdate[16:])
|
||||
writeNum(hsh, pi.InitialRollingHashMsgNumber)
|
||||
writeNum(hsh, pi.ChainID)
|
||||
hsh.Write(pi.L2MessageServiceAddr[:])
|
||||
|
||||
return hsh.Sum(nil)
|
||||
|
||||
}
|
||||
|
||||
func (e ExecutionSerializable) Decode() (decoded Execution, err error) {
|
||||
decoded = Execution{
|
||||
InitialBlockTimestamp: e.InitialBlockTimestamp,
|
||||
L2MsgHashes: make([][32]byte, len(e.L2MsgHashes)),
|
||||
FinalBlockNumber: e.FinalBlockNumber,
|
||||
FinalBlockTimestamp: e.FinalBlockTimestamp,
|
||||
FinalRollingHashNumber: e.FinalRollingHashNumber,
|
||||
L2MessageServiceAddr: e.L2MessageServiceAddr,
|
||||
ChainID: uint64(e.ChainID),
|
||||
}
|
||||
func (pi *Execution) SumAsField() field.Element {
|
||||
|
||||
fillWithHex := func(dst []byte, src string) {
|
||||
var d []byte
|
||||
if d, err = utils.HexDecodeString(src); err != nil {
|
||||
return
|
||||
}
|
||||
if len(d) > len(dst) {
|
||||
err = errors.New("decoded bytes too long")
|
||||
}
|
||||
n := len(dst) - len(d)
|
||||
copy(dst[n:], d)
|
||||
for n > 0 {
|
||||
n--
|
||||
dst[n] = 0
|
||||
}
|
||||
}
|
||||
var (
|
||||
sumBytes = pi.Sum(nil)
|
||||
sum = new(field.Element).SetBytes(sumBytes)
|
||||
)
|
||||
|
||||
for i, hex := range e.L2MsgHashes {
|
||||
if fillWithHex(decoded.L2MsgHashes[i][:], hex); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if fillWithHex(decoded.FinalStateRootHash[:], e.FinalStateRootHash); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fillWithHex(decoded.FinalRollingHash[:], e.FinalRollingHash)
|
||||
return
|
||||
return *sum
|
||||
}
|
||||
|
||||
func writeNum(hsh hash.Hash, n uint64) {
|
||||
var b [8]byte
|
||||
binary.BigEndian.PutUint64(b[:], n)
|
||||
hsh.Write(b[:])
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/consensys/gnark/frontend"
|
||||
snarkHash "github.com/consensys/gnark/std/hash"
|
||||
"hash"
|
||||
"io"
|
||||
"math"
|
||||
@@ -18,6 +16,9 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/consensys/gnark/frontend"
|
||||
snarkHash "github.com/consensys/gnark/std/hash"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -224,7 +225,7 @@ type ReaderHash struct {
|
||||
|
||||
func (r *ReaderHash) Write(p []byte) (n int, err error) {
|
||||
if rd := hashReadWrite(r.r); !bytes.Equal(rd, p) {
|
||||
panic(fmt.Errorf("ReaderHash.Write: mismatch %x≠%x", rd, p))
|
||||
panic(fmt.Errorf("ReaderHash.Write: expected %x, encountered %x", rd, p))
|
||||
}
|
||||
|
||||
return r.h.Write(p)
|
||||
@@ -343,11 +344,14 @@ func (r *ReaderHashSnark) CloseFile() {
|
||||
}
|
||||
}
|
||||
|
||||
func PrettyPrintHashes(filename string) {
|
||||
// PrettyPrintHashes reads a file of hashes and prints them in a human-readable format.
|
||||
// if out is nil, it will print to os.Stdout
|
||||
func PrettyPrintHashes(in string, out io.Writer) {
|
||||
const printIndexes = false
|
||||
nbResets := 0
|
||||
|
||||
v := make([]any, 0)
|
||||
f, err := os.Open(filename)
|
||||
f, err := os.Open(in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -360,7 +364,8 @@ func PrettyPrintHashes(filename string) {
|
||||
for _, err = f.Read(length[:]); err == nil; _, err = f.Read(length[:]) {
|
||||
l := int(length[0])*256 + int(length[1])
|
||||
if l == 65535 { // a reset
|
||||
v = append(v, "RESET")
|
||||
v = append(v, fmt.Sprintf("RESET #%d", nbResets))
|
||||
nbResets++
|
||||
i = 0
|
||||
continue
|
||||
}
|
||||
@@ -384,16 +389,31 @@ func PrettyPrintHashes(filename string) {
|
||||
if err != io.EOF {
|
||||
panic(err)
|
||||
}
|
||||
b, err := json.MarshalIndent(v, "", " ")
|
||||
if err != nil {
|
||||
|
||||
if out == nil {
|
||||
out = os.Stdout
|
||||
}
|
||||
encoder := json.NewEncoder(out)
|
||||
encoder.SetIndent("", " ")
|
||||
if err = encoder.Encode(v); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
|
||||
func PrettyPrintHashesToFile(in, out string) {
|
||||
var t FakeTestingT
|
||||
f, err := os.OpenFile(out, os.O_CREATE|os.O_WRONLY, 0600)
|
||||
require.NoError(t, err)
|
||||
PrettyPrintHashes(in, f)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func spaceOutFromRight(s string) string {
|
||||
var bb strings.Builder
|
||||
n := len(s) + (len(s)+15)/16 - 1
|
||||
if n < 0 {
|
||||
return ""
|
||||
}
|
||||
var bb strings.Builder
|
||||
bb.Grow(n)
|
||||
remainder := len(s) % 16
|
||||
first := true
|
||||
|
||||
Reference in New Issue
Block a user