Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions arbnode/batch_poster.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ type BatchPosterConfig struct {
EspressoTxSizeLimit int64 `koanf:"espresso-tx-size-limit"`
UserDataAttestationFile string `koanf:"user-data-attestation-file"`
QuoteFile string `koanf:"quote-file"`
AttestationServiceURL string `koanf:"attestation-service-url"`

// Fetch messages from HotShot block
HotShotBlock uint64 `koanf:"hotshot-block"`
Expand Down Expand Up @@ -299,6 +300,7 @@ func BatchPosterConfigAddOptions(prefix string, f *pflag.FlagSet) {
f.Duration(prefix+".resubmit-espresso-tx-deadline", DefaultBatchPosterConfig.ResubmitEspressoTxDeadline, "time threshold after which a transaction will be automatically resubmitted if no response is received")
f.String(prefix+".user-data-attestation-file", DefaultBatchPosterConfig.UserDataAttestationFile, "path to SGX user data attestation file")
f.String(prefix+".quote-file", DefaultBatchPosterConfig.QuoteFile, "path to SGX quote file")
f.String(prefix+".attestation-service-url", DefaultBatchPosterConfig.AttestationServiceURL, "URL of the attestation service to use for obtaining zk proof over attestation")
f.String(prefix+".parent-chain-eip7623", DefaultBatchPosterConfig.ParentChainEip7623, "if parent chain uses EIP7623 (\"yes\", \"no\", \"auto\")")
f.Bool(prefix+".delay-buffer-always-updatable", DefaultBatchPosterConfig.DelayBufferAlwaysUpdatable, "always treat delay buffer as updatable")
f.Int64(prefix+".espresso-tx-size-limit", DefaultBatchPosterConfig.EspressoTxSizeLimit, "specifies the maximum size of a transaction to be sent to the Espresso Network")
Expand Down Expand Up @@ -360,10 +362,10 @@ var DefaultBatchPosterConfig = BatchPosterConfig{
EspressoTeeType: "NITRO",
EspressoRegisterServiceConfig: espressotee.DefaultEspressoRegisterServiceConfig,
// EspressoTxSizeLimit is 1 MB, to have some buffer we set it to 900 KB
EspressoTxSizeLimit: 900 * 1024,
UserDataAttestationFile: "",
QuoteFile: "",

EspressoTxSizeLimit: 900 * 1024,
UserDataAttestationFile: "",
QuoteFile: "",
AttestationServiceURL: "",
HotShotBlock: 1,
HotShotFirstPostingBlock: 1,
InitBatcherAddresses: []string{},
Expand Down Expand Up @@ -725,7 +727,7 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e
submitterOptions,
// TODO: pass the persistent private key to the key manager in future
submitter.WithKeyManager(
espresso_key_manager.NewEspressoKeyManager(verifier, nitroVerifier, b.dataPoster, opts.DataSigner, teeType, espressotee.BatchPoster, cfg.EspressoRegisterServiceConfig, nil, opts.Config().UserDataAttestationFile, opts.Config().QuoteFile),
espresso_key_manager.NewEspressoKeyManager(verifier, nitroVerifier, b.dataPoster, opts.DataSigner, teeType, espressotee.BatchPoster, cfg.EspressoRegisterServiceConfig, nil, opts.Config().UserDataAttestationFile, opts.Config().QuoteFile, opts.Config().AttestationServiceURL),
),
)

Expand Down
7 changes: 6 additions & 1 deletion arbnode/espresso_caff_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ type EspressoCaffNodeConfig struct {
QuoteFile string `koanf:"quote-file"`
EspressoTEEVerifierAddr string `koanf:"espresso-tee-verifier-addr"`

// AWS Nitro Attestation Service URL
AttestationServiceURL string `koanf:"attestation-service-url"`

// Data poster config
DataPoster dataposter.DataPosterConfig `koanf:"data-poster" reload:"hot"`
ParentChainWallet genericconf.WalletConfig `koanf:"parent-chain-wallet"`
Expand Down Expand Up @@ -123,6 +126,7 @@ var DefaultEspressoCaffNodeConfig = EspressoCaffNodeConfig{
EspressoRegisterServiceConfig: espressotee.DefaultEspressoRegisterServiceConfig,
UserDataAttestationFile: "",
QuoteFile: "",
AttestationServiceURL: "",
EspressoTEEVerifierAddr: "",
DataPoster: dataposter.DefaultDataPosterConfig,
SnapshotChecksum: "",
Expand Down Expand Up @@ -155,6 +159,7 @@ func EspressoCaffNodeConfigAddOptions(prefix string, f *flag.FlagSet) {
f.String(prefix+".espresso-tee-type", DefaultEspressoCaffNodeConfig.EspressoTeeType, "The Trusted Execution Environment (TEE) that Caff node is running in")
f.String(prefix+".user-data-attestation-file", DefaultEspressoCaffNodeConfig.UserDataAttestationFile, "path to SGX user data attestation file")
f.String(prefix+".quote-file", DefaultEspressoCaffNodeConfig.QuoteFile, "path to SGX quote file")
f.String(prefix+".attestation-service-url", DefaultBatchPosterConfig.AttestationServiceURL, "URL of the attestation service to use for obtaining zk proof over attestation")
genericconf.WalletConfigAddOptions(prefix+".parent-chain-wallet", f, DefaultBatchPosterConfig.ParentChainWallet.Pathname)
f.String(prefix+".espresso-tee-verifier-addr", DefaultEspressoCaffNodeConfig.EspressoTEEVerifierAddr, "Address of the EspressoTEEVerifier contract utilize for handling cross chain NFT verification")
DangerousCaffNodeConfigAddOptions(prefix+".dangerous", f)
Expand Down Expand Up @@ -372,7 +377,7 @@ func NewEspressoCaffNode(
return nil, fmt.Errorf("failed to create data poster: %w", err)
}

keyManager = espresso_key_manager.NewEspressoKeyManager(verifier, nitroVerifier, dataPoster, nil, teeType, espressotee.CaffNode, configFetcher().EspressoRegisterServiceConfig, caffNodeInitArgs.CaffNodePrivateKey, configFetcher().UserDataAttestationFile, configFetcher().QuoteFile)
keyManager = espresso_key_manager.NewEspressoKeyManager(verifier, nitroVerifier, dataPoster, nil, teeType, espressotee.CaffNode, configFetcher().EspressoRegisterServiceConfig, caffNodeInitArgs.CaffNodePrivateKey, configFetcher().UserDataAttestationFile, configFetcher().QuoteFile, configFetcher().AttestationServiceURL)

}
initializeCaffNodeTags := false
Expand Down
8 changes: 8 additions & 0 deletions arbutil/espresso_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,3 +393,11 @@ func VerifyMessage(message []byte, signature []byte, pubKey *ecdsa.PublicKey) er
}
return nil
}

// StripHexPrefix removes "0x" prefix from hex strings if present
func StripHexPrefix(hexStr string) string {
if len(hexStr) >= 2 && hexStr[:2] == "0x" {
return hexStr[2:]
}
return hexStr
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package attestationverifierclient

import (
"bytes"
"context"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"

"github.com/offchainlabs/nitro/arbutil"
)

type EspressoAttestationVerifierClient struct {
baseURL string
}

type OnchainProof struct {
Zktype string `json:"zktype"`
ZkvmVersion string `json:"zkvm_version"`
ProgramID struct {
VerifierID string `json:"verifier_id"`
VerifierProofID string `json:"verifier_proof_id"`
AggregatorID string `json:"aggregator_id"`
} `json:"program_id"`
RawProof struct {
EncodedProof string `json:"encoded_proof"`
Journal string `json:"journal"`
} `json:"raw_proof"`
OnchainProof string `json:"onchain_proof"`
ProofType string `json:"proof_type"`
}

func NewEspressoAttestationVerifierClient(
attestationServiceURL string,
) *EspressoAttestationVerifierClient {
return &EspressoAttestationVerifierClient{
baseURL: strings.TrimSuffix(attestationServiceURL, "/"),
}
}

func (c *EspressoAttestationVerifierClient) GenerateZKProof(ctx context.Context, attestationBytes []byte) ([]byte, []byte, error) {
request, err := http.NewRequestWithContext(ctx, "POST", c.baseURL+"/generate_proof", bytes.NewBuffer(attestationBytes))
if err != nil {
return nil, nil, err
}
request.Header.Set("Content-Type", "application/octet-stream")

client := http.Client{
Timeout: 2 * time.Minute,
}
res, err := client.Do(request)
if err != nil {
return nil, nil, err
}
defer res.Body.Close()

responseData, err := io.ReadAll(res.Body)
if err != nil {
return nil, nil, err
}

if res.StatusCode != http.StatusOK {
return nil, nil, fmt.Errorf("attestation service returned status %d: %s", res.StatusCode, string(responseData))
}

var zkProof OnchainProof
err = json.Unmarshal(responseData, &zkProof)
if err != nil {
return nil, nil, err
}

journalBytes, err := hex.DecodeString(arbutil.StripHexPrefix(zkProof.RawProof.Journal))
if err != nil {
return nil, nil, fmt.Errorf("failed to decode journal hex string: %w", err)
}
onchainProofBytes, err := hex.DecodeString(arbutil.StripHexPrefix(zkProof.OnchainProof))
if err != nil {
return nil, nil, fmt.Errorf("failed to decode onchain proof hex string: %w", err)
}
return journalBytes, onchainProofBytes, nil
}
53 changes: 29 additions & 24 deletions espresso/key-manager/key_manager.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package keymanager

import (
"context"
"crypto/ecdsa"
"crypto/rand"
"encoding/json"
"errors"
"fmt"
"os"
"time"

"github.com/hf/nitrite"
"github.com/hf/nsm"
"github.com/hf/nsm/request"

Expand All @@ -21,6 +20,7 @@ import (
"github.com/offchainlabs/nitro/arbnode/dataposter"
"github.com/offchainlabs/nitro/arbutil"
"github.com/offchainlabs/nitro/espresso-tee-contracts/espressogen"
attestationverifierclient "github.com/offchainlabs/nitro/espresso/attestation_verifier_client"
"github.com/offchainlabs/nitro/espressotee"
"github.com/offchainlabs/nitro/util/signature"
)
Expand Down Expand Up @@ -58,7 +58,8 @@ type EspressoKeyManager struct {
userDataAttestationFile string
quoteFile string

hasRegistered bool
hasRegistered bool
espressoNitroAttestationVerifierClient *attestationverifierclient.EspressoAttestationVerifierClient
}

func NewEspressoKeyManager(
Expand All @@ -72,6 +73,7 @@ func NewEspressoKeyManager(
servicePersistentPrivateKey *ecdsa.PrivateKey,
userDataAttestationFile string,
quoteFile string,
zkAttestationServiceURL string,
) *EspressoKeyManager {
var pubKey *ecdsa.PublicKey
var err error
Expand Down Expand Up @@ -122,6 +124,12 @@ func NewEspressoKeyManager(
panic("Retry getting base fee delay cannot be more than 3 minutes")
}

if teeType == NITRO && zkAttestationServiceURL == "" {
panic("zk attestation service URL must be provided for nitro TEE type")
}

espressoNitroAttestationVerifierClient := attestationverifierclient.NewEspressoAttestationVerifierClient(zkAttestationServiceURL)

return &EspressoKeyManager{
pubKey: pubKey,
privKey: privKey,
Expand All @@ -138,9 +146,10 @@ func NewEspressoKeyManager(
GasLimitBufferIncreasePercent: registerSignerConfig.GasLimitBufferIncreasePercent,
MaxBaseFee: registerSignerConfig.MaxBaseFee,
},
userDataAttestationFile: userDataAttestationFile,
quoteFile: quoteFile,
serviceType: serviceType,
userDataAttestationFile: userDataAttestationFile,
quoteFile: quoteFile,
serviceType: serviceType,
espressoNitroAttestationVerifierClient: espressoNitroAttestationVerifierClient,
}
}

Expand Down Expand Up @@ -178,6 +187,7 @@ func (k *EspressoKeyManager) PrepareRegisterService(getAttestationFunc func([]by
if err != nil {
return nil, nil, fmt.Errorf("sgx signing failed: %w", err)
}

return attestationQuote, addr, nil

case NITRO:
Expand All @@ -188,17 +198,21 @@ func (k *EspressoKeyManager) PrepareRegisterService(getAttestationFunc func([]by
if err != nil {
return nil, nil, fmt.Errorf("nitro signing failed: %w", err)
}
if k.espressoNitroAttestationVerifierClient == nil {
return nil, nil, errors.New("attestation verifier client is not initialized")
}

attestation, data, err := k.espressoNitroTEEVerifier.VerifyAttestationAndCertificates(
attestationBytes,
k.dataPoster,
k.registerSignerOpts,
k.serviceType,
)
// this can only happen in tests where we don't have an attestation
if len(attestationBytes) == 0 {
return nil, nil, nil
}
journalBytes, onchainProofBytes, err := k.espressoNitroAttestationVerifierClient.GenerateZKProof(context.Background(), attestationBytes)
if err != nil {
return nil, nil, fmt.Errorf("attestation verification failed: %w", err)
return nil, nil, fmt.Errorf("failed to generate zk proof from nitro attestation: %w", err)
}
return attestation, data, nil

log.Info("successfully generated zk proof from nitro attestation")
return journalBytes, onchainProofBytes, nil
case TESTS:
addr := signerAddr.Bytes()
log.Info("TESTS signing address", "addr", signerAddr)
Expand Down Expand Up @@ -353,16 +367,7 @@ func (k *EspressoKeyManager) getNitroAttestation(pubKey []byte) ([]byte, error)
return nil, fmt.Errorf("no attestation document returned")
}

attestation, err := nitrite.Verify(res.Attestation.Document, nitrite.VerifyOptions{})
if err != nil {
return nil, fmt.Errorf("failed to verify attestation")
}

attestationBytes, err := json.Marshal(attestation)
if err != nil {
return nil, fmt.Errorf("failed to marshal attestation")
}
return attestationBytes, nil
return res.Attestation.Document, nil
}

// No-Op Signauture
Expand Down
Loading
Loading