diff --git a/.circleci/config.yml b/.circleci/config.yml index 97afcc2e4516..761351768b39 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -224,6 +224,12 @@ jobs: - run: name: Copy Plasma allocs to .devnet-plasma command: cp -r .devnet/ .devnet-plasma/ + - run: + name: Generate Generic Plasma allocs + command: DEVNET_PLASMA="true" GENERIC_PLASMA="true" make devnet-allocs + - run: + name: Copy Plasma allocs to .devnet-plasma + command: cp -r .devnet/ .devnet-plasma-generic/ - run: name: Generate non-FPAC allocs command: make devnet-allocs @@ -251,6 +257,11 @@ jobs: - ".devnet-plasma/allocs-l2.json" - ".devnet-plasma/allocs-l2-delta.json" - ".devnet-plasma/allocs-l2-ecotone.json" + - ".devnet-plasma-generic/allocs-l1.json" + - ".devnet-plasma-generic/addresses.json" + - ".devnet-plasma-generic/allocs-l2.json" + - ".devnet-plasma-generic/allocs-l2-delta.json" + - ".devnet-plasma-generic/allocs-l2-ecotone.json" - "packages/contracts-bedrock/deploy-config/devnetL1.json" - "packages/contracts-bedrock/deployments/devnetL1" - notify-failures-on-develop @@ -1268,6 +1279,16 @@ jobs: - run: name: Set DEVNET_PLASMA = true command: echo 'export DEVNET_PLASMA=true' >> $BASH_ENV + - when: + condition: + equal: ['plasma-generic', <>] + steps: + - run: + name: Set DEVNET_PLASMA = true + command: echo 'export DEVNET_PLASMA=true' >> $BASH_ENV + - run: + name: Set GENERIC_PLASMA = true + command: echo 'export GENERIC_PLASMA=true' >> $BASH_ENV - check-changed: patterns: op-(.+),packages,ops-bedrock,bedrock-devnet - run: @@ -1876,7 +1897,7 @@ workflows: - devnet: matrix: parameters: - fpac: ["legacy", "fault-proofs", "plasma"] + fpac: ["legacy", "fault-proofs", "plasma", "plasma-generic"] requires: - pnpm-monorepo - op-batcher-docker-build diff --git a/bedrock-devnet/devnet/__init__.py b/bedrock-devnet/devnet/__init__.py index 3565916d25a3..c9a4ee03606d 100644 --- a/bedrock-devnet/devnet/__init__.py +++ b/bedrock-devnet/devnet/__init__.py @@ -30,6 +30,7 @@ DEVNET_NO_BUILD = os.getenv('DEVNET_NO_BUILD') == "true" DEVNET_FPAC = os.getenv('DEVNET_FPAC') == "true" DEVNET_PLASMA = os.getenv('DEVNET_PLASMA') == "true" +GENERIC_PLASMA = os.getenv('GENERIC_PLASMA') == "true" class Bunch: def __init__(self, **kwds): @@ -135,6 +136,8 @@ def init_devnet_l1_deploy_config(paths, update_timestamp=False): deploy_config['faultGameWithdrawalDelay'] = 0 if DEVNET_PLASMA: deploy_config['usePlasma'] = True + if GENERIC_PLASMA: + deploy_config['daCommitmentType'] = "GenericCommitment" write_json(paths.devnet_config_path, deploy_config) def devnet_l1_allocs(paths): @@ -273,11 +276,17 @@ def devnet_deploy(paths): if DEVNET_PLASMA: docker_env['PLASMA_ENABLED'] = 'true' - docker_env['PLASMA_DA_SERVICE'] = 'false' else: docker_env['PLASMA_ENABLED'] = 'false' + + if GENERIC_PLASMA: + docker_env['PLASMA_GENERIC_DA'] = 'true' + docker_env['PLASMA_DA_SERVICE'] = 'true' + else: + docker_env['PLASMA_GENERIC_DA'] = 'false' docker_env['PLASMA_DA_SERVICE'] = 'false' + # Bring up the rest of the services. log.info('Bringing up `op-node`, `op-proposer` and `op-batcher`.') run_command(['docker', 'compose', 'up', '-d', 'op-node', 'op-proposer', 'op-batcher', 'artifact-server'], cwd=paths.ops_bedrock_dir, env=docker_env) @@ -287,7 +296,7 @@ def devnet_deploy(paths): log.info('Bringing up `op-challenger`.') run_command(['docker', 'compose', 'up', '-d', 'op-challenger'], cwd=paths.ops_bedrock_dir, env=docker_env) - # Optionally bring up OP Plasma. + # Optionally bring up Plasma Mode components. if DEVNET_PLASMA: log.info('Bringing up `da-server`, `sentinel`.') # TODO(10141): We don't have public sentinel images yet run_command(['docker', 'compose', 'up', '-d', 'da-server'], cwd=paths.ops_bedrock_dir, env=docker_env) diff --git a/op-chain-ops/genesis/config.go b/op-chain-ops/genesis/config.go index 3ad4c4db527a..14fa59f5a9a3 100644 --- a/op-chain-ops/genesis/config.go +++ b/op-chain-ops/genesis/config.go @@ -21,6 +21,7 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum-optimism/optimism/op-node/rollup" + plasma "github.com/ethereum-optimism/optimism/op-plasma" "github.com/ethereum-optimism/optimism/op-service/eth" ) @@ -271,6 +272,8 @@ type DeployConfig struct { CustomGasTokenAddress common.Address `json:"customGasTokenAddress"` // UsePlasma is a flag that indicates if the system is using op-plasma UsePlasma bool `json:"usePlasma"` + // DACommitmentType specifies the allowed commitment + DACommitmentType string `json:"daCommitmentType"` // DAChallengeWindow represents the block interval during which the availability of a data commitment can be challenged. DAChallengeWindow uint64 `json:"daChallengeWindow"` // DAResolveWindow represents the block interval during which a data availability challenge can be resolved. @@ -443,6 +446,9 @@ func (d *DeployConfig) Check() error { if d.DAResolveWindow == 0 { return fmt.Errorf("%w: DAResolveWindow cannot be 0 when using plasma mode", ErrInvalidDeployConfig) } + if !(d.DACommitmentType == plasma.KeccakCommitmentString || d.DACommitmentType == plasma.GenericCommitmentString) { + return fmt.Errorf("%w: DACommitmentType must be either KeccakCommtiment or GenericCommitment", ErrInvalidDeployConfig) + } } if d.UseCustomGasToken { if d.CustomGasTokenAddress == (common.Address{}) { @@ -513,9 +519,10 @@ func (d *DeployConfig) CheckAddresses() error { if d.OptimismPortalProxy == (common.Address{}) { return fmt.Errorf("%w: OptimismPortalProxy cannot be address(0)", ErrInvalidDeployConfig) } - if d.UsePlasma && d.DAChallengeProxy == (common.Address{}) { + if d.UsePlasma && d.DACommitmentType == plasma.KeccakCommitmentString && d.DAChallengeProxy == (common.Address{}) { return fmt.Errorf("%w: DAChallengeContract cannot be address(0) when using plasma mode", ErrInvalidDeployConfig) - + } else if d.UsePlasma && d.DACommitmentType == plasma.GenericCommitmentString && d.DAChallengeProxy != (common.Address{}) { + return fmt.Errorf("%w: DAChallengeContract must be address(0) when using generic commitments in plasma mode", ErrInvalidDeployConfig) } return nil } @@ -612,6 +619,7 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *types.Block, l2GenesisBlockHas var plasma *rollup.PlasmaConfig if d.UsePlasma { plasma = &rollup.PlasmaConfig{ + CommitmentType: d.DACommitmentType, DAChallengeAddress: d.DAChallengeProxy, DAChallengeWindow: d.DAChallengeWindow, DAResolveWindow: d.DAResolveWindow, diff --git a/op-chain-ops/genesis/testdata/test-deploy-config-full.json b/op-chain-ops/genesis/testdata/test-deploy-config-full.json index ffaf7c4c841b..c0aefac625ff 100644 --- a/op-chain-ops/genesis/testdata/test-deploy-config-full.json +++ b/op-chain-ops/genesis/testdata/test-deploy-config-full.json @@ -88,6 +88,7 @@ "useFaultProofs": false, "usePlasma": false, "daBondSize": 0, + "daCommitmentType": "KeccakCommtiment", "daChallengeProxy": "0x0000000000000000000000000000000000000000", "daChallengeWindow": 0, "daResolveWindow": 0, diff --git a/op-e2e/e2eutils/setup.go b/op-e2e/e2eutils/setup.go index 56fc1fdb1abb..448fdabffa87 100644 --- a/op-e2e/e2eutils/setup.go +++ b/op-e2e/e2eutils/setup.go @@ -16,6 +16,7 @@ import ( "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-e2e/config" "github.com/ethereum-optimism/optimism/op-node/rollup" + plasma "github.com/ethereum-optimism/optimism/op-plasma" "github.com/ethereum-optimism/optimism/op-service/eth" ) @@ -143,12 +144,13 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) * l2Genesis.Alloc[addr] = val } - var plasma *rollup.PlasmaConfig + var pcfg *rollup.PlasmaConfig if deployConf.UsePlasma { - plasma = &rollup.PlasmaConfig{ + pcfg = &rollup.PlasmaConfig{ DAChallengeAddress: l1Deployments.DataAvailabilityChallengeProxy, DAChallengeWindow: deployConf.DAChallengeWindow, DAResolveWindow: deployConf.DAResolveWindow, + CommitmentType: plasma.KeccakCommitmentString, } } @@ -180,7 +182,7 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) * EcotoneTime: deployConf.EcotoneTime(uint64(deployConf.L1GenesisBlockTimestamp)), FjordTime: deployConf.FjordTime(uint64(deployConf.L1GenesisBlockTimestamp)), InteropTime: deployConf.InteropTime(uint64(deployConf.L1GenesisBlockTimestamp)), - PlasmaConfig: plasma, + PlasmaConfig: pcfg, } require.NoError(t, rollupCfg.Check()) diff --git a/op-node/rollup/derive/engine_queue_test.go b/op-node/rollup/derive/engine_queue_test.go index 7161d04d9fc4..884ff5ad510b 100644 --- a/op-node/rollup/derive/engine_queue_test.go +++ b/op-node/rollup/derive/engine_queue_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/ethereum-optimism/optimism/op-node/node/safedb" + plasma "github.com/ethereum-optimism/optimism/op-plasma" "github.com/holiman/uint256" "github.com/stretchr/testify/require" @@ -1256,6 +1257,7 @@ func TestPlasmaFinalityData(t *testing.T) { plasmaCfg := &rollup.PlasmaConfig{ DAChallengeWindow: 90, DAResolveWindow: 90, + CommitmentType: plasma.KeccakCommitmentString, } // shoud return l1 finality if plasma is not enabled require.Equal(t, uint64(finalityLookback), calcFinalityLookback(cfg)) diff --git a/op-node/rollup/derive/plasma_data_source.go b/op-node/rollup/derive/plasma_data_source.go index 3ad075c5febc..9db4dd1cc553 100644 --- a/op-node/rollup/derive/plasma_data_source.go +++ b/op-node/rollup/derive/plasma_data_source.go @@ -92,7 +92,7 @@ func (s *PlasmaDataSource) Next(ctx context.Context) (eth.Data, error) { return nil, NewTemporaryError(fmt.Errorf("failed to fetch input data with comm %x from da service: %w", s.comm, err)) } // inputs are limited to a max size to ensure they can be challenged in the DA contract. - if len(data) > plasma.MaxInputSize { + if s.comm.CommitmentType() == plasma.Keccak256CommitmentType && len(data) > plasma.MaxInputSize { s.log.Warn("input data exceeds max size", "size", len(data), "max", plasma.MaxInputSize) s.comm = nil return s.Next(ctx) diff --git a/op-node/rollup/derive/plasma_data_source_test.go b/op-node/rollup/derive/plasma_data_source_test.go index b71b59067a3e..ae13be8e37f8 100644 --- a/op-node/rollup/derive/plasma_data_source_test.go +++ b/op-node/rollup/derive/plasma_data_source_test.go @@ -91,6 +91,7 @@ func TestPlasmaDataSource(t *testing.T) { PlasmaConfig: &rollup.PlasmaConfig{ DAChallengeWindow: pcfg.ChallengeWindow, DAResolveWindow: pcfg.ResolveWindow, + CommitmentType: plasma.KeccakCommitmentString, }, } // keep track of random input data to validate against @@ -333,6 +334,7 @@ func TestPlasmaDataSourceStall(t *testing.T) { PlasmaConfig: &rollup.PlasmaConfig{ DAChallengeWindow: pcfg.ChallengeWindow, DAResolveWindow: pcfg.ResolveWindow, + CommitmentType: plasma.KeccakCommitmentString, }, } @@ -451,6 +453,7 @@ func TestPlasmaDataSourceInvalidData(t *testing.T) { PlasmaConfig: &rollup.PlasmaConfig{ DAChallengeWindow: pcfg.ChallengeWindow, DAResolveWindow: pcfg.ResolveWindow, + CommitmentType: plasma.KeccakCommitmentString, }, } diff --git a/op-node/rollup/types.go b/op-node/rollup/types.go index a72a0c07e003..816181687567 100644 --- a/op-node/rollup/types.go +++ b/op-node/rollup/types.go @@ -52,6 +52,8 @@ type Genesis struct { type PlasmaConfig struct { // L1 DataAvailabilityChallenge contract proxy address DAChallengeAddress common.Address `json:"da_challenge_contract_address,omitempty"` + // CommitmentType specifies which commitment type can be used. Defaults to Keccak (type 0) if not present + CommitmentType string `json:"da_commitment_type"` // DA challenge window value set on the DAC contract. Used in plasma mode // to compute when a commitment can no longer be challenged. DAChallengeWindow uint64 `json:"da_challenge_window"` @@ -345,6 +347,18 @@ func validatePlasmaConfig(cfg *Config) error { if cfg.LegacyDAResolveWindow != cfg.PlasmaConfig.DAResolveWindow { return fmt.Errorf("LegacyDAResolveWindow (%v) != PlasmaConfig.DAResolveWindow (%v)", cfg.LegacyDAResolveWindow, cfg.PlasmaConfig.DAResolveWindow) } + if cfg.PlasmaConfig.CommitmentType != plasma.KeccakCommitmentString { + return errors.New("Cannot set CommitmentType with the legacy config") + } + } else if cfg.PlasmaConfig != nil { + if !(cfg.PlasmaConfig.CommitmentType == plasma.KeccakCommitmentString || cfg.PlasmaConfig.CommitmentType == plasma.GenericCommitmentString) { + return fmt.Errorf("invalid commitment type: %v", cfg.PlasmaConfig.CommitmentType) + } + if cfg.PlasmaConfig.CommitmentType == plasma.KeccakCommitmentString && cfg.PlasmaConfig.DAChallengeAddress == (common.Address{}) { + return errors.New("Must set da_challenge_contract_address for keccak commitments") + } else if cfg.PlasmaConfig.CommitmentType == plasma.GenericCommitmentString && cfg.PlasmaConfig.DAChallengeAddress != (common.Address{}) { + return errors.New("Must set empty da_challenge_contract_address for generic commitments") + } } return nil } @@ -485,19 +499,21 @@ func (c *Config) GetOPPlasmaConfig() (plasma.Config, error) { if c.PlasmaConfig == nil { return plasma.Config{}, errors.New("no plasma config") } - if c.PlasmaConfig.DAChallengeAddress == (common.Address{}) { - return plasma.Config{}, errors.New("missing DAChallengeAddress") - } if c.PlasmaConfig.DAChallengeWindow == uint64(0) { return plasma.Config{}, errors.New("missing DAChallengeWindow") } if c.PlasmaConfig.DAResolveWindow == uint64(0) { return plasma.Config{}, errors.New("missing DAResolveWindow") } + t, err := plasma.CommitmentTypeFromString(c.PlasmaConfig.CommitmentType) + if err != nil { + return plasma.Config{}, err + } return plasma.Config{ DAChallengeContractAddress: c.PlasmaConfig.DAChallengeAddress, ChallengeWindow: c.PlasmaConfig.DAChallengeWindow, ResolveWindow: c.PlasmaConfig.DAResolveWindow, + CommitmentType: t, }, nil } @@ -550,6 +566,9 @@ func (c *Config) Description(l2Chains map[string]string) string { banner += fmt.Sprintf(" - Interop: %s\n", fmtForkTimeOrUnset(c.InteropTime)) // Report the protocol version banner += fmt.Sprintf("Node supports up to OP-Stack Protocol Version: %s\n", OPStackSupport) + if c.PlasmaConfig != nil { + banner += fmt.Sprintf("Node supports Plasma Mode with CommitmentType %v\n", c.PlasmaConfig.CommitmentType) + } return banner } @@ -569,6 +588,7 @@ func (c *Config) LogDescription(log log.Logger, l2Chains map[string]string) { if networkL1 == "" { networkL1 = "unknown L1" } + log.Info("Rollup Config", "l2_chain_id", c.L2ChainID, "l2_network", networkL2, "l1_chain_id", c.L1ChainID, "l1_network", networkL1, "l2_start_time", c.Genesis.L2Time, "l2_block_hash", c.Genesis.L2.Hash.String(), "l2_block_number", c.Genesis.L2.Number, "l1_block_hash", c.Genesis.L1.Hash.String(), @@ -578,6 +598,7 @@ func (c *Config) LogDescription(log log.Logger, l2Chains map[string]string) { "ecotone_time", fmtForkTimeOrUnset(c.EcotoneTime), "fjord_time", fmtForkTimeOrUnset(c.FjordTime), "interop_time", fmtForkTimeOrUnset(c.InteropTime), + "plasma_mode", c.PlasmaConfig != nil, ) } diff --git a/op-plasma/cmd/daserver/entrypoint.go b/op-plasma/cmd/daserver/entrypoint.go index 0b3378cb1525..c8b03486b8c2 100644 --- a/op-plasma/cmd/daserver/entrypoint.go +++ b/op-plasma/cmd/daserver/entrypoint.go @@ -41,7 +41,7 @@ func StartDAServer(cliCtx *cli.Context) error { store = s3 } - server := plasma.NewDAServer(cliCtx.String(ListenAddrFlagName), cliCtx.Int(PortFlagName), store, l) + server := plasma.NewDAServer(cliCtx.String(ListenAddrFlagName), cliCtx.Int(PortFlagName), store, l, cfg.UseGenericComm) if err := server.Start(); err != nil { return fmt.Errorf("failed to start the DA server") diff --git a/op-plasma/cmd/daserver/flags.go b/op-plasma/cmd/daserver/flags.go index ee22d5587393..841a3c3a43a0 100644 --- a/op-plasma/cmd/daserver/flags.go +++ b/op-plasma/cmd/daserver/flags.go @@ -18,6 +18,7 @@ const ( S3AccessKeyIDFlagName = "s3.access-key-id" S3AccessKeySecretFlagName = "s3.access-key-secret" FileStorePathFlagName = "file.path" + GenericCommFlagName = "generic-commitment" ) const EnvVarPrefix = "OP_PLASMA_DA_SERVER" @@ -44,6 +45,12 @@ var ( Usage: "path to directory for file storage", EnvVars: prefixEnvVars("FILESTORE_PATH"), } + GenericCommFlag = &cli.BoolFlag{ + Name: GenericCommFlagName, + Usage: "enable generic commitments for testing. Not for production use.", + EnvVars: prefixEnvVars("GENERIC_COMMITMENT"), + Value: false, + } S3BucketFlag = &cli.StringFlag{ Name: S3BucketFlagName, Usage: "bucket name for S3 storage", @@ -80,6 +87,7 @@ var optionalFlags = []cli.Flag{ S3EndpointFlag, S3AccessKeyIDFlag, S3AccessKeySecretFlag, + GenericCommFlag, } func init() { @@ -96,6 +104,7 @@ type CLIConfig struct { S3Endpoint string S3AccessKeyID string S3AccessKeySecret string + UseGenericComm bool } func ReadCLIConfig(ctx *cli.Context) CLIConfig { @@ -105,6 +114,7 @@ func ReadCLIConfig(ctx *cli.Context) CLIConfig { S3Endpoint: ctx.String(S3EndpointFlagName), S3AccessKeyID: ctx.String(S3AccessKeyIDFlagName), S3AccessKeySecret: ctx.String(S3AccessKeySecretFlagName), + UseGenericComm: ctx.Bool(GenericCommFlagName), } } diff --git a/op-plasma/commitment.go b/op-plasma/commitment.go index 7d1e310d4d35..0edb4ad2f14c 100644 --- a/op-plasma/commitment.go +++ b/op-plasma/commitment.go @@ -3,6 +3,7 @@ package plasma import ( "bytes" "errors" + "fmt" "github.com/ethereum/go-ethereum/crypto" ) @@ -16,12 +17,25 @@ var ErrCommitmentMismatch = errors.New("commitment mismatch") // CommitmentType is the commitment type prefix. type CommitmentType byte +func CommitmentTypeFromString(s string) (CommitmentType, error) { + switch s { + case KeccakCommitmentString: + return Keccak256CommitmentType, nil + case GenericCommitmentString: + return GenericCommitmentType, nil + default: + return 0, fmt.Errorf("invalid commitment type: %s", s) + } +} + // CommitmentType describes the binary format of the commitment. -// KeccakCommitmentType is the default commitment type for the centralized DA storage. +// KeccakCommitmentStringType is the default commitment type for the centralized DA storage. // GenericCommitmentType indicates an opaque bytestring that the op-node never opens. const ( Keccak256CommitmentType CommitmentType = 0 GenericCommitmentType CommitmentType = 1 + KeccakCommitmentString string = "KeccakCommitment" + GenericCommitmentString string = "GenericCommitment" ) // CommitmentData is the binary representation of a commitment. diff --git a/op-plasma/daclient.go b/op-plasma/daclient.go index e3e8f57a1e26..39708b2fd25a 100644 --- a/op-plasma/daclient.go +++ b/op-plasma/daclient.go @@ -130,7 +130,7 @@ func (c *DAClient) setInput(ctx context.Context, img []byte) (CommitmentData, er return nil, err } - comm, err := DecodeGenericCommitment(b) + comm, err := DecodeCommitmentData(b) if err != nil { return nil, err } diff --git a/op-plasma/daclient_test.go b/op-plasma/daclient_test.go index c3ce4da6676b..8f5aa55053bc 100644 --- a/op-plasma/daclient_test.go +++ b/op-plasma/daclient_test.go @@ -51,7 +51,7 @@ func TestDAClientPrecomputed(t *testing.T) { ctx := context.Background() - server := NewDAServer("127.0.0.1", 0, store, logger) + server := NewDAServer("127.0.0.1", 0, store, logger, false) require.NoError(t, server.Start()) @@ -108,7 +108,7 @@ func TestDAClientService(t *testing.T) { ctx := context.Background() - server := NewDAServer("127.0.0.1", 0, store, logger) + server := NewDAServer("127.0.0.1", 0, store, logger, false) require.NoError(t, server.Start()) diff --git a/op-plasma/damgr.go b/op-plasma/damgr.go index c4eb0001f8d5..b3604ac1c7ef 100644 --- a/op-plasma/damgr.go +++ b/op-plasma/damgr.go @@ -51,6 +51,8 @@ type HeadSignalFn func(eth.L1BlockRef) type Config struct { // Required for filtering contract events DAChallengeContractAddress common.Address + // Allowed CommitmentType + CommitmentType CommitmentType // The number of l1 blocks after the input is committed during which one can challenge. ChallengeWindow uint64 // The number of l1 blocks after a commitment is challenged during which one can resolve. @@ -166,6 +168,10 @@ func (d *DA) Reset(ctx context.Context, base eth.L1BlockRef, baseCfg eth.SystemC // GetInput returns the input data for the given commitment bytes. blockNumber is required to lookup // the challenge status in the DataAvailabilityChallenge L1 contract. func (d *DA) GetInput(ctx context.Context, l1 L1Fetcher, comm CommitmentData, blockId eth.BlockID) (eth.Data, error) { + // If it's not the right commitment type, report it as an expired commitment in order to skip it + if d.cfg.CommitmentType != comm.CommitmentType() { + return nil, fmt.Errorf("invalid commitment type; expected: %v, got: %v: %w", d.cfg.CommitmentType, comm.CommitmentType(), ErrExpiredChallenge) + } // If the challenge head is ahead in the case of a pipeline reset or stall, we might have synced a // challenge event for this commitment. Otherwise we mark the commitment as part of the canonical // chain so potential future challenge events can be selected. @@ -205,6 +211,10 @@ func (d *DA) GetInput(ctx context.Context, l1 L1Fetcher, comm CommitmentData, bl if !notFound { return data, nil } + // Generic Commitments don't resolve from L1 so if we still can't find the data with out of luck + if comm.CommitmentType() == GenericCommitmentType { + return nil, ErrMissingPastWindow + } // data not found in storage, return from challenge resolved input resolvedInput, err := d.state.GetResolvedInput(comm.Encode()) if err != nil { @@ -310,31 +320,39 @@ func (d *DA) LoadChallengeEvents(ctx context.Context, l1 L1Fetcher, block eth.Bl d.log.Error("tx hash mismatch", "block", block.Number, "txIdx", i, "log", log.Index, "txHash", tx.Hash(), "receiptTxHash", log.TxHash) continue } - // Decode the input from resolver tx calldata - input, err := DecodeResolvedInput(tx.Data()) - if err != nil { - d.log.Error("failed to decode resolved input", "block", block.Number, "txIdx", i, "err", err) - continue - } - if err := comm.Verify(input); err != nil { - d.log.Error("failed to verify commitment", "block", block.Number, "txIdx", i, "err", err) - continue + var input []byte + if d.cfg.CommitmentType == Keccak256CommitmentType { + // Decode the input from resolver tx calldata + input, err = DecodeResolvedInput(tx.Data()) + if err != nil { + d.log.Error("failed to decode resolved input", "block", block.Number, "txIdx", i, "err", err) + continue + } + if err := comm.Verify(input); err != nil { + d.log.Error("failed to verify commitment", "block", block.Number, "txIdx", i, "err", err) + continue + } } - d.log.Debug("challenge resolved", "block", block, "txIdx", i) + d.log.Info("challenge resolved", "block", block, "txIdx", i, "comm", comm.Encode()) d.state.SetResolvedChallenge(comm.Encode(), input, log.BlockNumber) case ChallengeActive: - d.log.Info("detected new active challenge", "block", block) + d.log.Info("detected new active challenge", "block", block, "comm", comm.Encode()) d.state.SetActiveChallenge(comm.Encode(), log.BlockNumber, d.cfg.ResolveWindow) default: - d.log.Warn("skipping unknown challenge status", "block", block.Number, "tx", i, "log", log.Index, "status", status) + d.log.Warn("skipping unknown challenge status", "block", block.Number, "tx", i, "log", log.Index, "status", status, "comm", comm.Encode()) } } return nil } // fetchChallengeLogs returns logs for challenge events if any for the given block -func (d *DA) fetchChallengeLogs(ctx context.Context, l1 L1Fetcher, block eth.BlockID) ([]*types.Log, error) { //cached with deposits events call so not expensive +func (d *DA) fetchChallengeLogs(ctx context.Context, l1 L1Fetcher, block eth.BlockID) ([]*types.Log, error) { var logs []*types.Log + // Don't look at the challenge contract if there is no challenge contract. + if d.cfg.CommitmentType == GenericCommitmentType { + return logs, nil + } + //cached with deposits events call so not expensive _, receipts, err := l1.FetchReceipts(ctx, block.Hash) if err != nil { return nil, err diff --git a/op-plasma/daserver.go b/op-plasma/daserver.go index c1925d3e606d..6f163cb2f715 100644 --- a/op-plasma/daserver.go +++ b/op-plasma/daserver.go @@ -2,9 +2,12 @@ package plasma import ( "context" + "crypto/rand" + "encoding/hex" "errors" "fmt" "io" + "math/big" "net" "net/http" "path" @@ -13,7 +16,6 @@ import ( "github.com/ethereum-optimism/optimism/op-service/rpc" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" ) @@ -25,15 +27,16 @@ type KVStore interface { } type DAServer struct { - log log.Logger - endpoint string - store KVStore - tls *rpc.ServerTLSConfig - httpServer *http.Server - listener net.Listener + log log.Logger + endpoint string + store KVStore + tls *rpc.ServerTLSConfig + httpServer *http.Server + listener net.Listener + useGenericComm bool } -func NewDAServer(host string, port int, store KVStore, log log.Logger) *DAServer { +func NewDAServer(host string, port int, store KVStore, log log.Logger, useGenericComm bool) *DAServer { endpoint := net.JoinHostPort(host, strconv.Itoa(port)) return &DAServer{ log: log, @@ -42,6 +45,7 @@ func NewDAServer(host string, port int, store KVStore, log log.Logger) *DAServer httpServer: &http.Server{ Addr: endpoint, }, + useGenericComm: useGenericComm, } } @@ -118,9 +122,8 @@ func (d *DAServer) HandleGet(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) return } - - w.WriteHeader(http.StatusOK) } + func (d *DAServer) HandlePut(w http.ResponseWriter, r *http.Request) { d.log.Info("PUT", "url", r.URL) @@ -138,20 +141,34 @@ func (d *DAServer) HandlePut(w http.ResponseWriter, r *http.Request) { } if r.URL.Path == "/put" || r.URL.Path == "/put/" { // without commitment + var comm []byte + if d.useGenericComm { + n, err := rand.Int(rand.Reader, big.NewInt(99999999999999)) + if err != nil { + d.log.Error("Failed to generate commitment", "err", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + comm = append(comm, 0x01) + comm = append(comm, 0xff) + comm = append(comm, n.Bytes()...) - comm := GenericCommitment(crypto.Keccak256Hash(input).Bytes()) - if err = d.store.Put(r.Context(), comm.Encode(), input); err != nil { + } else { + comm = NewKeccak256Commitment(input).Encode() + } + + if err = d.store.Put(r.Context(), comm, input); err != nil { d.log.Error("Failed to store commitment to the DA server", "err", err, "comm", comm) w.WriteHeader(http.StatusInternalServerError) return } + d.log.Info("stored commitment", "key", hex.EncodeToString(comm), "input_len", len(input)) if _, err := w.Write(comm); err != nil { d.log.Error("Failed to write commitment request body", "err", err, "comm", comm) w.WriteHeader(http.StatusInternalServerError) return } - } else { key := path.Base(r.URL.Path) comm, err := hexutil.Decode(key) @@ -166,10 +183,8 @@ func (d *DAServer) HandlePut(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) return } + w.WriteHeader(http.StatusOK) } - - w.WriteHeader(http.StatusOK) - } func (b *DAServer) Endpoint() string { diff --git a/ops-bedrock/docker-compose.yml b/ops-bedrock/docker-compose.yml index 37a5b29ace33..64a8007b3396 100644 --- a/ops-bedrock/docker-compose.yml +++ b/ops-bedrock/docker-compose.yml @@ -206,6 +206,7 @@ services: --addr=0.0.0.0 --port=3100 --log.level=debug + --generic-commitment="${PLASMA_GENERIC_DA}" ports: - "3100:3100" volumes: diff --git a/packages/contracts-bedrock/deploy-config/devnetL1-template.json b/packages/contracts-bedrock/deploy-config/devnetL1-template.json index 88fe5509dc1c..557a6b763d1f 100644 --- a/packages/contracts-bedrock/deploy-config/devnetL1-template.json +++ b/packages/contracts-bedrock/deploy-config/devnetL1-template.json @@ -66,6 +66,7 @@ "respectedGameType": 0, "useFaultProofs": false, "usePlasma": false, + "daCommitmentType": "KeccakCommitment", "daChallengeWindow": 160, "daResolveWindow": 160, "daBondSize": 1000000, diff --git a/packages/contracts-bedrock/scripts/Deploy.s.sol b/packages/contracts-bedrock/scripts/Deploy.s.sol index 18cb83d9b8bb..b47492ecbedd 100644 --- a/packages/contracts-bedrock/scripts/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/Deploy.s.sol @@ -285,7 +285,11 @@ contract Deploy is Deployer { setupSuperchain(); console.log("set up superchain!"); if (cfg.usePlasma()) { - setupOpPlasma(); + bytes32 typeHash = keccak256(bytes(cfg.daCommitmentType())); + bytes32 keccakHash = keccak256(bytes("KeccakCommitment")); + if (typeHash == keccakHash) { + setupOpPlasma(); + } } setupOpChain(); console.log("set up op chain!"); diff --git a/packages/contracts-bedrock/scripts/DeployConfig.s.sol b/packages/contracts-bedrock/scripts/DeployConfig.s.sol index 80b9659ac775..3467d7e4d9a3 100644 --- a/packages/contracts-bedrock/scripts/DeployConfig.s.sol +++ b/packages/contracts-bedrock/scripts/DeployConfig.s.sol @@ -69,6 +69,7 @@ contract DeployConfig is Script { uint256 public respectedGameType; bool public useFaultProofs; bool public usePlasma; + string public daCommitmentType; uint256 public daChallengeWindow; uint256 public daResolveWindow; uint256 public daBondSize; @@ -147,6 +148,7 @@ contract DeployConfig is Script { preimageOracleChallengePeriod = stdJson.readUint(_json, "$.preimageOracleChallengePeriod"); usePlasma = _readOr(_json, "$.usePlasma", false); + daCommitmentType = _readOr(_json, "$.daCommitmentType", "KeccakCommitment"); daChallengeWindow = _readOr(_json, "$.daChallengeWindow", 1000); daResolveWindow = _readOr(_json, "$.daResolveWindow", 1000); daBondSize = _readOr(_json, "$.daBondSize", 1000000000); @@ -232,4 +234,16 @@ contract DeployConfig is Script { function _readOr(string memory json, string memory key, address defaultValue) internal view returns (address) { return vm.keyExists(json, key) ? stdJson.readAddress(json, key) : defaultValue; } + + function _readOr( + string memory json, + string memory key, + string memory defaultValue + ) + internal + view + returns (string memory) + { + return vm.keyExists(json, key) ? stdJson.readString(json, key) : defaultValue; + } }