Repository navigation
Espresso 3b: TEE batcher (re-hosted) #459
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 55 commits
c61a61c
492b911
d6e953e
999eee7
1c9f472
1d683d5
47dc77e
776bcf4
51dec95
c35d734
e15ada2
783a0d1
b99e2e8
24fb92d
b851926
7fdb93d
eaf1478
f6ea10a
7081501
3160255
699aea2
f14d95c
fefb070
3caefa6
dc01558
9be65a6
f2c18bb
fd30eb5
4925920
14ba61a
a0b7798
422a0c4
2b0715e
0e6f4dd
f50b0cb
b46fecc
b54e81f
d79a805
79e4a5e
1a6e6ec
64802f0
80d2204
4b822fe
059809c
eaa0e67
5258dde
6314621
5c63f37
666fa17
f4aaa83
54da7e2
769b0fa
099f5d5
158237f
ccef597
7674c68
a178aac
4a7cc89
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| # Builds and publishes the two images that EspressoSystems/tee-image-builder's | ||
| # nitro-op-poster workflow consumes to produce the TEE batch poster: | ||
| # | ||
| # ghcr.io/celo-org/optimism/op-batcher-enclave-app — wrapped into the Nitro EIF | ||
| # ghcr.io/celo-org/optimism/op-batcher-tee — enclave-tools is extracted from it | ||
|
|
||
| name: Build and Push Espresso Enclave Images | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
|
|
||
| env: | ||
| REGISTRY: ghcr.io | ||
| IMAGE_PREFIX: ghcr.io/celo-org/optimism | ||
|
|
||
| jobs: | ||
| build: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| packages: write | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| include: | ||
| - image: op-batcher-enclave-app | ||
| target: op-batcher-enclave-app | ||
| - image: op-batcher-tee | ||
| target: op-batcher-enclave-target | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@v3 | ||
|
|
||
| - name: Login to GitHub Container Registry | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ${{ env.REGISTRY }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Extract metadata | ||
| id: meta | ||
| uses: docker/metadata-action@v5 | ||
| with: | ||
| images: ${{ env.IMAGE_PREFIX }}/${{ matrix.image }} | ||
| tags: | | ||
| type=ref,event=branch | ||
| type=ref,event=pr | ||
| type=sha,prefix={{branch}}-,enable=${{ github.event_name == 'push' }} | ||
|
|
||
| - name: Build and push ${{ matrix.image }} | ||
| uses: docker/build-push-action@v5 | ||
| with: | ||
| context: . | ||
| file: ops/docker/op-stack-go/Dockerfile | ||
| target: ${{ matrix.target }} | ||
| platforms: linux/amd64 | ||
| push: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} | ||
| tags: ${{ steps.meta.outputs.tags }} | ||
| labels: ${{ steps.meta.outputs.labels }} | ||
| build-args: | | ||
| GIT_COMMIT=${{ github.sha }} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,213 @@ | ||
| package espresso | ||
|
|
||
| import ( | ||
| "crypto/ecdsa" | ||
| "fmt" | ||
| "strings" | ||
| "time" | ||
|
|
||
| "github.com/ethereum/go-ethereum/common" | ||
| "github.com/ethereum/go-ethereum/crypto" | ||
|
|
||
| "github.com/urfave/cli/v2" | ||
| ) | ||
|
|
||
| // espressoFlags returns the flag names for espresso | ||
| func espressoFlags(v string) string { | ||
| return "espresso." + v | ||
| } | ||
|
|
||
| func espressoEnvs(envprefix, v string) []string { | ||
| return []string{envprefix + "_ESPRESSO_" + v} | ||
| } | ||
|
|
||
| // Default values for batch submission receipt verification tuning. | ||
| // Defined here so that both the CLI flag defaults and the batcher logic | ||
| // can reference a single source of truth. | ||
| // | ||
| // Note: DefaultBatchAuthLookbackWindow lives in constants.go (mips64-clean | ||
| // build target shared with the derivation pipeline). | ||
| const ( | ||
| DefaultVerifyReceiptMaxBlocks uint64 = 5 | ||
| DefaultVerifyReceiptSafetyTimeout time.Duration = 5 * time.Minute | ||
| DefaultVerifyReceiptRetryDelay time.Duration = 100 * time.Millisecond | ||
| DefaultMaxInFlightRequestsToEspresso = 128 | ||
| ) | ||
|
|
||
| var ( | ||
| EnabledFlagName = espressoFlags("enabled") | ||
| PollIntervalFlagName = espressoFlags("poll-interval") | ||
| QueryServiceUrlsFlagName = espressoFlags("urls") | ||
| LightClientAddrFlagName = espressoFlags("light-client-addr") | ||
| L1UrlFlagName = espressoFlags("l1-url") | ||
| TestingBatcherPrivateKeyFlagName = espressoFlags("testing-batcher-private-key") | ||
| CaffeinationHeightEspresso = espressoFlags("origin-height-espresso") | ||
| CaffeinationHeightL2 = espressoFlags("origin-height-l2") | ||
| AttestationServiceFlagName = espressoFlags("espresso-attestation-service") | ||
| VerifyReceiptMaxBlocksFlagName = espressoFlags("verify-receipt-max-blocks") | ||
| VerifyReceiptSafetyTimeoutFlagName = espressoFlags("verify-receipt-safety-timeout") | ||
| VerifyReceiptRetryDelayFlagName = espressoFlags("verify-receipt-retry-delay") | ||
| ) | ||
|
|
||
| func CLIFlags(envPrefix string, category string) []cli.Flag { | ||
| return []cli.Flag{ | ||
| &cli.BoolFlag{ | ||
| Name: EnabledFlagName, | ||
| Usage: "Enable Espresso mode", | ||
| Value: false, | ||
| EnvVars: espressoEnvs(envPrefix, "ENABLED"), | ||
| Category: category, | ||
| }, | ||
| &cli.DurationFlag{ | ||
| Name: PollIntervalFlagName, | ||
| Usage: "Polling interval for Espresso queries", | ||
| Value: 250 * time.Millisecond, | ||
| EnvVars: espressoEnvs(envPrefix, "POLL_INTERVAL"), | ||
| Category: category, | ||
| }, | ||
| &cli.StringSliceFlag{ | ||
| Name: QueryServiceUrlsFlagName, | ||
| Usage: "Comma-separated list of Espresso query service URLs", | ||
| EnvVars: espressoEnvs(envPrefix, "URLS"), | ||
| Category: category, | ||
| }, | ||
| &cli.StringFlag{ | ||
| Name: LightClientAddrFlagName, | ||
| Usage: "Address of the Espresso light client", | ||
| EnvVars: espressoEnvs(envPrefix, "LIGHT_CLIENT_ADDR"), | ||
| Category: category, | ||
| }, | ||
| &cli.StringFlag{ | ||
| Name: L1UrlFlagName, | ||
| Usage: "L1 RPC URL the Espresso light client is deployed on; defaults to the batcher's L1 RPC when unset", | ||
| EnvVars: espressoEnvs(envPrefix, "L1_URL"), | ||
| Category: category, | ||
| }, | ||
| &cli.StringFlag{ | ||
| Name: TestingBatcherPrivateKeyFlagName, | ||
| Usage: "Pre-approved batcher ephemeral key (testing only)", | ||
| EnvVars: espressoEnvs(envPrefix, "TESTING_BATCHER_PRIVATE_KEY"), | ||
| Category: category, | ||
| }, | ||
| &cli.Uint64Flag{ | ||
| Name: CaffeinationHeightEspresso, | ||
| Usage: "Espresso transactions below this height will not be considered", | ||
| EnvVars: espressoEnvs(envPrefix, "ORIGIN_HEIGHT_ESPRESSO"), | ||
| Category: category, | ||
| }, | ||
| &cli.Uint64Flag{ | ||
| Name: CaffeinationHeightL2, | ||
| Usage: "L2 batch position at which the Espresso streamer starts emitting batches. " + | ||
| "Operational parameter for restarting batchers mid-chain; set it explicitly when " + | ||
| "taking over from the fallback batcher. " + | ||
| "When zero, no floor is enforced and the streamer anchors at the current local-safe head. " + | ||
| "Independent of the EspressoTime hardfork, which gates derivation semantics.", | ||
| Value: 0, | ||
| EnvVars: espressoEnvs(envPrefix, "ORIGIN_HEIGHT_L2"), | ||
| Category: category, | ||
| }, | ||
| &cli.StringFlag{ | ||
| Name: AttestationServiceFlagName, | ||
| Usage: "URL of the Espresso attestation service", | ||
| EnvVars: espressoEnvs(envPrefix, "ESPRESSO_ATTESTATION_SERVICE"), | ||
| Category: category, | ||
| }, | ||
| &cli.Uint64Flag{ | ||
| Name: VerifyReceiptMaxBlocksFlagName, | ||
| Usage: "Number of HotShot blocks to wait for a submitted transaction to become queryable before re-submitting", | ||
| Value: DefaultVerifyReceiptMaxBlocks, | ||
| EnvVars: espressoEnvs(envPrefix, "VERIFY_RECEIPT_MAX_BLOCKS"), | ||
| Category: category, | ||
| }, | ||
| &cli.DurationFlag{ | ||
| Name: VerifyReceiptSafetyTimeoutFlagName, | ||
| Usage: "Wall-clock backstop for receipt verification; re-submits the transaction if this duration is exceeded", | ||
| Value: DefaultVerifyReceiptSafetyTimeout, | ||
| EnvVars: espressoEnvs(envPrefix, "VERIFY_RECEIPT_SAFETY_TIMEOUT"), | ||
| Category: category, | ||
| }, | ||
| &cli.DurationFlag{ | ||
| Name: VerifyReceiptRetryDelayFlagName, | ||
| Usage: "Delay between receipt verification retries", | ||
| Value: DefaultVerifyReceiptRetryDelay, | ||
| EnvVars: espressoEnvs(envPrefix, "VERIFY_RECEIPT_RETRY_DELAY"), | ||
| Category: category, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| type CLIConfig struct { | ||
| Enabled bool | ||
| PollInterval time.Duration | ||
| QueryServiceURLs []string | ||
| LightClientAddr common.Address | ||
| L1URL string | ||
| TestingBatcherPrivateKey *ecdsa.PrivateKey | ||
| CaffeinationHeightEspresso uint64 | ||
| CaffeinationHeightL2 uint64 | ||
| EspressoAttestationService string | ||
|
|
||
| // Batch submission receipt verification tuning | ||
| VerifyReceiptMaxBlocks uint64 | ||
| VerifyReceiptSafetyTimeout time.Duration | ||
| VerifyReceiptRetryDelay time.Duration | ||
| } | ||
|
|
||
| func (c CLIConfig) Check() error { | ||
| if c.Enabled { | ||
|
piersy marked this conversation as resolved.
philippecamacho marked this conversation as resolved.
|
||
| // Check required fields when Espresso is enabled | ||
| if len(c.QueryServiceURLs) == 0 { | ||
| return fmt.Errorf("query service URLs are required when Espresso is enabled") | ||
| } | ||
| if c.LightClientAddr == (common.Address{}) { | ||
| return fmt.Errorf("light client address is required when Espresso is enabled") | ||
| } | ||
| if c.L1URL == "" { | ||
| return fmt.Errorf("L1 URL is required when Espresso is enabled") | ||
| } | ||
| if c.EspressoAttestationService == "" { | ||
| return fmt.Errorf("attestation service URL is required when Espresso is enabled") | ||
| } | ||
|
Comment on lines
+168
to
+170
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Espresso runs outside Nitro with Useful? React with 👍 / 👎. |
||
| if c.PollInterval <= 0 { | ||
| return fmt.Errorf("poll interval must be > 0") | ||
| } | ||
| if c.VerifyReceiptMaxBlocks == 0 { | ||
| return fmt.Errorf("verify-receipt-max-blocks must be > 0") | ||
| } | ||
| if c.VerifyReceiptSafetyTimeout <= 0 { | ||
| return fmt.Errorf("verify-receipt-safety-timeout must be > 0") | ||
| } | ||
| if c.VerifyReceiptRetryDelay <= 0 { | ||
| return fmt.Errorf("verify-receipt-retry-delay must be > 0") | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func ReadCLIConfig(c *cli.Context) CLIConfig { | ||
| config := CLIConfig{ | ||
| Enabled: c.Bool(EnabledFlagName), | ||
| PollInterval: c.Duration(PollIntervalFlagName), | ||
| L1URL: c.String(L1UrlFlagName), | ||
| CaffeinationHeightEspresso: c.Uint64(CaffeinationHeightEspresso), | ||
| CaffeinationHeightL2: c.Uint64(CaffeinationHeightL2), | ||
| EspressoAttestationService: c.String(AttestationServiceFlagName), | ||
| VerifyReceiptMaxBlocks: c.Uint64(VerifyReceiptMaxBlocksFlagName), | ||
| VerifyReceiptSafetyTimeout: c.Duration(VerifyReceiptSafetyTimeoutFlagName), | ||
| VerifyReceiptRetryDelay: c.Duration(VerifyReceiptRetryDelayFlagName), | ||
| } | ||
|
|
||
| config.QueryServiceURLs = c.StringSlice(QueryServiceUrlsFlagName) | ||
|
|
||
| addrStr := c.String(LightClientAddrFlagName) | ||
| config.LightClientAddr = common.HexToAddress(addrStr) | ||
|
|
||
| pkStr := c.String(TestingBatcherPrivateKeyFlagName) | ||
| pkStr = strings.TrimPrefix(pkStr, "0x") | ||
| pk, err := crypto.HexToECDSA(pkStr) | ||
| if err == nil { | ||
| config.TestingBatcherPrivateKey = pk | ||
| } | ||
|
|
||
| return config | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When this workflow is manually dispatched with a tag ref—which GitHub explicitly supports for
workflow_dispatch—neither the branch nor PR rule generates a Docker tag, while the SHA rule is disabled becausegithub.event_nameisworkflow_dispatch, notpush. The build step still enablespush, so it has no named GHCR image target to publish. Addtype=ref,event=tagor enable the SHA tag for manual dispatches. GitHub workflow dispatch reference metadata-action ref semanticsUseful? React with 👍 / 👎.