From 12e9eb9ede3f66212ca7197a6b5db3d4484b0c85 Mon Sep 17 00:00:00 2001 From: Sneh Koul Date: Tue, 12 Nov 2024 12:46:21 -0500 Subject: [PATCH 1/7] Add attestation quote in batch poster --- arbnode/batch_poster.go | 53 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 9a83a9d126..5267729966 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -10,8 +10,10 @@ import ( "encoding/hex" "errors" "fmt" + "github.com/ethereum/go-ethereum/crypto" "math" "math/big" + "os" "strings" "sync/atomic" "time" @@ -1084,11 +1086,16 @@ func (b *BatchPoster) encodeAddBatch( var calldata []byte var kzgBlobs []kzg4844.Blob var err error + var userData []byte if use4844 { kzgBlobs, err = blobs.EncodeBlobs(l2MessageData) if err != nil { return nil, nil, fmt.Errorf("failed to encode blobs: %w", err) } + _, blobHashes, err := blobs.ComputeCommitmentsAndHashes(kzgBlobs) + if err != nil { + return nil, nil, fmt.Errorf("failed to compute blob hashes: %w", err) + } // EIP4844 transactions to the sequencer inbox will not use transaction calldata for L2 info. calldata, err = method.Inputs.Pack( seqNum, @@ -1097,6 +1104,19 @@ func (b *BatchPoster) encodeAddBatch( new(big.Int).SetUint64(uint64(prevMsgNum)), new(big.Int).SetUint64(uint64(newMsgNum)), ) + // userData has blobHashes along with other calldata for EIP-4844 transactions + userData, err = method.Inputs.Pack( + seqNum, + new(big.Int).SetUint64(delayedMsg), + b.config().gasRefunder, + new(big.Int).SetUint64(uint64(prevMsgNum)), + new(big.Int).SetUint64(uint64(newMsgNum)), + blobHashes, + ) + _, err = getAttestationQuote(userData) + if err != nil { + return nil, nil, fmt.Errorf("failed to get attestation quote: %w", err) + } } else { calldata, err = method.Inputs.Pack( seqNum, @@ -1106,15 +1126,42 @@ func (b *BatchPoster) encodeAddBatch( new(big.Int).SetUint64(uint64(prevMsgNum)), new(big.Int).SetUint64(uint64(newMsgNum)), ) + + _, err = getAttestationQuote(calldata) + if err != nil { + return nil, nil, fmt.Errorf("failed to get attestation quote: %w", err) + } } - if err != nil { - return nil, nil, err - } + // TODO: when contract is updated add attestationQuote to the calldata fullCalldata := append([]byte{}, method.ID...) fullCalldata = append(fullCalldata, calldata...) return fullCalldata, kzgBlobs, nil } +func getAttestationQuote(userData []byte) ([]byte, error) { + // keccak256 hash of userData + userDataHash := crypto.Keccak256(userData) + // Write the message to "/dev/attestation/user_report_data" + file, err := os.Create("/dev/attestation/user_report_data") + if err != nil { + return []byte{}, fmt.Errorf("failed to create user report data file: %w", err) + } + defer file.Close() + + _, err = file.Write(userDataHash) + if err != nil { + return []byte{}, fmt.Errorf("failed to write user report data file: %w", err) + } + // Read the quote from "/dev/attestation/quote" + attestationQuote, err := os.ReadFile("/dev/attestation/quote") + if err != nil { + return []byte{}, fmt.Errorf("failed to read quote file: %w", err) + } + + log.Info("Attestation quote generated", "quote", attestationQuote) + return attestationQuote, nil +} + var ErrNormalGasEstimationFailed = errors.New("normal gas estimation failed") type estimateGasParams struct { From dafc31fb203b2fc5c188f875cf484e7fc40102a7 Mon Sep 17 00:00:00 2001 From: Sneh Koul Date: Tue, 12 Nov 2024 14:02:52 -0500 Subject: [PATCH 2/7] write file --- arbnode/batch_poster.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 5267729966..06a08f2bd3 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -10,7 +10,6 @@ import ( "encoding/hex" "errors" "fmt" - "github.com/ethereum/go-ethereum/crypto" "math" "math/big" "os" @@ -18,6 +17,8 @@ import ( "sync/atomic" "time" + "github.com/ethereum/go-ethereum/crypto" + "github.com/andybalholm/brotli" "github.com/spf13/pflag" @@ -1142,16 +1143,11 @@ func getAttestationQuote(userData []byte) ([]byte, error) { // keccak256 hash of userData userDataHash := crypto.Keccak256(userData) // Write the message to "/dev/attestation/user_report_data" - file, err := os.Create("/dev/attestation/user_report_data") + err := os.WriteFile("/dev/attestation/user_report_data", userDataHash, 0644) if err != nil { return []byte{}, fmt.Errorf("failed to create user report data file: %w", err) } - defer file.Close() - _, err = file.Write(userDataHash) - if err != nil { - return []byte{}, fmt.Errorf("failed to write user report data file: %w", err) - } // Read the quote from "/dev/attestation/quote" attestationQuote, err := os.ReadFile("/dev/attestation/quote") if err != nil { From e8496a27a293778500c0e743009a7d04f42144fd Mon Sep 17 00:00:00 2001 From: Sneh Koul Date: Wed, 13 Nov 2024 15:18:58 -0500 Subject: [PATCH 3/7] update batch poster --- arbnode/batch_poster.go | 53 ++++++++++++++++++++++++++------- arbnode/transaction_streamer.go | 2 +- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 15ba059d2c..8272ccc412 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -185,10 +185,10 @@ type BatchPosterConfig struct { Dangerous BatchPosterDangerousConfig `koanf:"dangerous"` ReorgResistanceMargin time.Duration `koanf:"reorg-resistance-margin" reload:"hot"` CheckBatchCorrectness bool `koanf:"check-batch-correctness"` - - gasRefunder common.Address - l1BlockBound l1BlockBound - + UserDataAttestationFile string `koanf:"user-data-attestation-file"` + QuoteFile string `koanf:"quote-file"` + gasRefunder common.Address + l1BlockBound l1BlockBound // Espresso specific flags LightClientAddress string `koanf:"light-client-address"` HotShotUrl string `koanf:"hotshot-url"` @@ -244,6 +244,8 @@ func BatchPosterConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Uint64(prefix+".gas-estimate-base-fee-multiple-bips", uint64(DefaultBatchPosterConfig.GasEstimateBaseFeeMultipleBips), "for gas estimation, use this multiple of the basefee (measured in basis points) as the max fee per gas") f.Duration(prefix+".reorg-resistance-margin", DefaultBatchPosterConfig.ReorgResistanceMargin, "do not post batch if its within this duration from layer 1 minimum bounds. Requires l1-block-bound option not be set to \"ignore\"") f.Bool(prefix+".check-batch-correctness", DefaultBatchPosterConfig.CheckBatchCorrectness, "setting this to true will run the batch against an inbox multiplexer and verifies that it produces the correct set of messages") + f.String(prefix+".user-data-attestation-file", DefaultBatchPosterConfig.UserDataAttestationFile, "specifies the file containing the user data attestation") + f.String(prefix+".quote-file", DefaultBatchPosterConfig.QuoteFile, "specifies the file containing the quote") redislock.AddConfigOptions(prefix+".redis-lock", f) dataposter.DataPosterConfigAddOptions(prefix+".data-poster", f, dataposter.DefaultDataPosterConfig) genericconf.WalletConfigAddOptions(prefix+".parent-chain-wallet", f, DefaultBatchPosterConfig.ParentChainWallet.Pathname) @@ -275,6 +277,8 @@ var DefaultBatchPosterConfig = BatchPosterConfig{ GasEstimateBaseFeeMultipleBips: arbmath.OneInUBips * 3 / 2, ReorgResistanceMargin: 10 * time.Minute, CheckBatchCorrectness: true, + UserDataAttestationFile: "", + QuoteFile: "", } var DefaultBatchPosterL1WalletConfig = genericconf.WalletConfig{ @@ -1137,6 +1141,9 @@ func (b *BatchPoster) encodeAddBatch( new(big.Int).SetUint64(uint64(prevMsgNum)), new(big.Int).SetUint64(uint64(newMsgNum)), ) + if err != nil { + return nil, nil, fmt.Errorf("failed to pack calldata: %w", err) + } // userData has blobHashes along with other calldata for EIP-4844 transactions userData, err = method.Inputs.Pack( seqNum, @@ -1146,7 +1153,10 @@ func (b *BatchPoster) encodeAddBatch( new(big.Int).SetUint64(uint64(newMsgNum)), blobHashes, ) - _, err = getAttestationQuote(userData) + if err != nil { + return nil, nil, fmt.Errorf("failed to pack user data: %w", err) + } + _, err = b.getAttestationQuote(userData) if err != nil { return nil, nil, fmt.Errorf("failed to get attestation quote: %w", err) } @@ -1160,7 +1170,11 @@ func (b *BatchPoster) encodeAddBatch( new(big.Int).SetUint64(uint64(newMsgNum)), ) - _, err = getAttestationQuote(calldata) + if err != nil { + return nil, nil, fmt.Errorf("failed to pack calldata: %w", err) + } + + _, err = b.getAttestationQuote(calldata) if err != nil { return nil, nil, fmt.Errorf("failed to get attestation quote: %w", err) } @@ -1171,22 +1185,41 @@ func (b *BatchPoster) encodeAddBatch( return fullCalldata, kzgBlobs, nil } -func getAttestationQuote(userData []byte) ([]byte, error) { +func (b *BatchPoster) getAttestationQuote(userData []byte) ([]byte, error) { + if (b.config().UserDataAttestationFile == "") || (b.config().QuoteFile == "") { + return []byte{}, nil + } // keccak256 hash of userData userDataHash := crypto.Keccak256(userData) // Write the message to "/dev/attestation/user_report_data" - err := os.WriteFile("/dev/attestation/user_report_data", userDataHash, 0644) + file, err := os.Create(b.config().UserDataAttestationFile) if err != nil { return []byte{}, fmt.Errorf("failed to create user report data file: %w", err) } + defer file.Close() + + err = binary.Write(file, binary.LittleEndian, userDataHash) + if err != nil { + return []byte{}, fmt.Errorf("failed to write user report data file: %w", err) + } + // Read the quote from "/dev/attestation/quote" - attestationQuote, err := os.ReadFile("/dev/attestation/quote") + quoteFile, err := os.Open(b.config().QuoteFile) + if err != nil { + return []byte{}, fmt.Errorf("failed to read quote file: %w", err) + } + + defer quoteFile.Close() + + var attestationQuote []byte + + err = binary.Read(quoteFile, binary.LittleEndian, &attestationQuote) if err != nil { return []byte{}, fmt.Errorf("failed to read quote file: %w", err) } - log.Info("Attestation quote generated", "quote", attestationQuote) + log.Info("Attestation quote generated", "quote", hex.EncodeToString(attestationQuote)) return attestationQuote, nil } diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index b8ca932c6d..4673ff1b28 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -1498,7 +1498,7 @@ func (s *TransactionStreamer) SubmitEspressoTransactionPos(pos arbutil.MessageIn if err != nil && dbutil.IsErrNotFound(err) { // if the key doesn't exist, create a new array with the pos pendingTxnsPos = []*arbutil.MessageIndex{&pos} - } else if pendingTxnsPos[len(pendingTxnsPos)-1] == &pos { + } else if len(pendingTxnsPos) > 0 && pendingTxnsPos[len(pendingTxnsPos)-1] == &pos { return fmt.Errorf("Submitting the same position messages %v", pos) } else { pendingTxnsPos = append(pendingTxnsPos, &pos) From 688bf3f4055ce24f28e072767223460035f55297 Mon Sep 17 00:00:00 2001 From: Sneh Koul Date: Wed, 13 Nov 2024 18:39:22 -0500 Subject: [PATCH 4/7] use read file --- arbnode/batch_poster.go | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 8272ccc412..ea362566db 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -1185,36 +1185,34 @@ func (b *BatchPoster) encodeAddBatch( return fullCalldata, kzgBlobs, nil } +/** + * This function generates the attestation quote for the user data. + * The user data is hashed using keccak256 and then 32 bytes of padding is added to the hash. + * The hash is then written to a file specified in the config. (For SGX: /dev/attestation/user_report_data) + * The quote is then read from the file specified in the config. (For SGX: /dev/attestation/quote) + */ func (b *BatchPoster) getAttestationQuote(userData []byte) ([]byte, error) { if (b.config().UserDataAttestationFile == "") || (b.config().QuoteFile == "") { return []byte{}, nil } + // keccak256 hash of userData userDataHash := crypto.Keccak256(userData) - // Write the message to "/dev/attestation/user_report_data" - file, err := os.Create(b.config().UserDataAttestationFile) - if err != nil { - return []byte{}, fmt.Errorf("failed to create user report data file: %w", err) - } - - defer file.Close() - err = binary.Write(file, binary.LittleEndian, userDataHash) - if err != nil { - return []byte{}, fmt.Errorf("failed to write user report data file: %w", err) + // Add 32 bytes of padding to the user data hash + // because keccak256 hash is 32 bytes and sgx requires 64 bytes of user data + for i := 0; i < 32; i += 1 { + userDataHash = append(userData, 0) } - // Read the quote from "/dev/attestation/quote" - quoteFile, err := os.Open(b.config().QuoteFile) + // Write the message to "/dev/attestation/user_report_data" in SGX + err := os.WriteFile(b.config().UserDataAttestationFile, userDataHash, 0644) if err != nil { - return []byte{}, fmt.Errorf("failed to read quote file: %w", err) + return []byte{}, fmt.Errorf("failed to create user report data file: %w", err) } - defer quoteFile.Close() - - var attestationQuote []byte - - err = binary.Read(quoteFile, binary.LittleEndian, &attestationQuote) + // Read the quote from "/dev/attestation/quote" in SGX + attestationQuote, err := os.ReadFile(b.config().QuoteFile) if err != nil { return []byte{}, fmt.Errorf("failed to read quote file: %w", err) } From 0a9b0df1a59c6207ba1053931f04d8bc3c1dd510 Mon Sep 17 00:00:00 2001 From: Sneh Koul Date: Thu, 14 Nov 2024 15:10:23 -0500 Subject: [PATCH 5/7] update to userDataHash --- arbnode/batch_poster.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index ea362566db..ea7d1d2c72 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -1202,7 +1202,7 @@ func (b *BatchPoster) getAttestationQuote(userData []byte) ([]byte, error) { // Add 32 bytes of padding to the user data hash // because keccak256 hash is 32 bytes and sgx requires 64 bytes of user data for i := 0; i < 32; i += 1 { - userDataHash = append(userData, 0) + userDataHash = append(userDataHash, 0) } // Write the message to "/dev/attestation/user_report_data" in SGX From 950da985931940fd2b03e8e4e5f0dedbcaee1dcf Mon Sep 17 00:00:00 2001 From: Sneh Koul Date: Thu, 14 Nov 2024 16:50:21 -0500 Subject: [PATCH 6/7] reduce file permissions --- arbnode/batch_poster.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index ea7d1d2c72..c571edee7f 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -1206,7 +1206,7 @@ func (b *BatchPoster) getAttestationQuote(userData []byte) ([]byte, error) { } // Write the message to "/dev/attestation/user_report_data" in SGX - err := os.WriteFile(b.config().UserDataAttestationFile, userDataHash, 0644) + err := os.WriteFile(b.config().UserDataAttestationFile, userDataHash, 0600) if err != nil { return []byte{}, fmt.Errorf("failed to create user report data file: %w", err) } From 2a2b310127b479173171e39e31255fe740baa477 Mon Sep 17 00:00:00 2001 From: Sneh Koul Date: Fri, 15 Nov 2024 08:53:57 -0500 Subject: [PATCH 7/7] nit --- arbnode/batch_poster.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index c571edee7f..6038d92d11 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -185,13 +185,13 @@ type BatchPosterConfig struct { Dangerous BatchPosterDangerousConfig `koanf:"dangerous"` ReorgResistanceMargin time.Duration `koanf:"reorg-resistance-margin" reload:"hot"` CheckBatchCorrectness bool `koanf:"check-batch-correctness"` - UserDataAttestationFile string `koanf:"user-data-attestation-file"` - QuoteFile string `koanf:"quote-file"` gasRefunder common.Address l1BlockBound l1BlockBound // Espresso specific flags - LightClientAddress string `koanf:"light-client-address"` - HotShotUrl string `koanf:"hotshot-url"` + LightClientAddress string `koanf:"light-client-address"` + HotShotUrl string `koanf:"hotshot-url"` + UserDataAttestationFile string `koanf:"user-data-attestation-file"` + QuoteFile string `koanf:"quote-file"` } func (c *BatchPosterConfig) Validate() error {