From 6e14746918c32b1d0e5da1b519933373e0d18d2f Mon Sep 17 00:00:00 2001 From: Ian Shim <100327837+ian-shim@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:44:15 -0800 Subject: [PATCH] [v2] Refactor object serialization (#870) --- api/grpc/common/v2/common.pb.go | 4 +- api/proto/common/v2/common.proto | 2 +- core/v2/serialization.go | 265 ++++++++++++++++++ .../{types_test.go => serialization_test.go} | 30 +- core/v2/types.go | 203 +------------- 5 files changed, 302 insertions(+), 202 deletions(-) create mode 100644 core/v2/serialization.go rename core/v2/{types_test.go => serialization_test.go} (70%) diff --git a/api/grpc/common/v2/common.pb.go b/api/grpc/common/v2/common.pb.go index 3e268fa23b..23c3f1dafa 100644 --- a/api/grpc/common/v2/common.pb.go +++ b/api/grpc/common/v2/common.pb.go @@ -109,7 +109,7 @@ type BlobCertificate struct { unknownFields protoimpl.UnknownFields BlobHeader *BlobHeader `protobuf:"bytes,1,opt,name=blob_header,json=blobHeader,proto3" json:"blob_header,omitempty"` - Relays []uint32 `protobuf:"varint,3,rep,packed,name=relays,proto3" json:"relays,omitempty"` + Relays []uint32 `protobuf:"varint,2,rep,packed,name=relays,proto3" json:"relays,omitempty"` } func (x *BlobCertificate) Reset() { @@ -300,7 +300,7 @@ var file_common_v2_common_proto_rawDesc = []byte{ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x22, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x22, 0x62, 0x0a, 0x0b, 0x42, 0x61, 0x74, 0x63, 0x68, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x34, 0x0a, diff --git a/api/proto/common/v2/common.proto b/api/proto/common/v2/common.proto index 7a2da669fc..3024eb9ed0 100644 --- a/api/proto/common/v2/common.proto +++ b/api/proto/common/v2/common.proto @@ -18,7 +18,7 @@ message BlobHeader { // BlobCertificate is what gets attested by the network message BlobCertificate { BlobHeader blob_header = 1; - repeated uint32 relays = 3; + repeated uint32 relays = 2; } // BatchHeader is the header of a batch of blobs diff --git a/core/v2/serialization.go b/core/v2/serialization.go new file mode 100644 index 0000000000..e9b94be813 --- /dev/null +++ b/core/v2/serialization.go @@ -0,0 +1,265 @@ +package v2 + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "golang.org/x/crypto/sha3" +) + +type abiG1Commit struct { + X *big.Int + Y *big.Int +} +type abiG2Commit struct { + X [2]*big.Int + Y [2]*big.Int +} +type abiBlobCommitments struct { + Commitment abiG1Commit + LengthCommitment abiG2Commit + LengthProof abiG2Commit + Length uint32 +} +type abiBlobHeader struct { + BlobVersion uint8 + BlobCommitments abiBlobCommitments + QuorumNumbers []byte + PaymentMetadataHash [32]byte +} + +func blobHeaderArgMarshaling() []abi.ArgumentMarshaling { + return []abi.ArgumentMarshaling{ + { + Name: "blobVersion", + Type: "uint8", + }, + { + Name: "blobCommitments", + Type: "tuple", + Components: []abi.ArgumentMarshaling{ + { + Name: "commitment", + Type: "tuple", + Components: []abi.ArgumentMarshaling{ + { + Name: "X", + Type: "uint256", + }, + { + Name: "Y", + Type: "uint256", + }, + }, + }, + { + Name: "lengthCommitment", + Type: "tuple", + Components: []abi.ArgumentMarshaling{ + { + Name: "X", + Type: "uint256[2]", + }, + { + Name: "Y", + Type: "uint256[2]", + }, + }, + }, + { + Name: "lengthProof", + Type: "tuple", + Components: []abi.ArgumentMarshaling{ + { + Name: "X", + Type: "uint256[2]", + }, + { + Name: "Y", + Type: "uint256[2]", + }, + }, + }, + { + Name: "length", + Type: "uint32", + }, + }, + }, + { + Name: "quorumNumbers", + Type: "bytes", + }, + { + Name: "paymentMetadataHash", + Type: "bytes32", + }, + } +} + +func (b *BlobHeader) toABIStruct() (abiBlobHeader, error) { + paymentHash, err := b.PaymentMetadata.Hash() + if err != nil { + return abiBlobHeader{}, err + } + return abiBlobHeader{ + BlobVersion: uint8(b.BlobVersion), + BlobCommitments: abiBlobCommitments{ + Commitment: abiG1Commit{ + X: b.BlobCommitments.Commitment.X.BigInt(new(big.Int)), + Y: b.BlobCommitments.Commitment.Y.BigInt(new(big.Int)), + }, + LengthCommitment: abiG2Commit{ + X: [2]*big.Int{ + b.BlobCommitments.LengthCommitment.X.A0.BigInt(new(big.Int)), + b.BlobCommitments.LengthCommitment.X.A1.BigInt(new(big.Int)), + }, + Y: [2]*big.Int{ + b.BlobCommitments.LengthCommitment.Y.A0.BigInt(new(big.Int)), + b.BlobCommitments.LengthCommitment.Y.A1.BigInt(new(big.Int)), + }, + }, + LengthProof: abiG2Commit{ + X: [2]*big.Int{ + b.BlobCommitments.LengthProof.X.A0.BigInt(new(big.Int)), + b.BlobCommitments.LengthProof.X.A1.BigInt(new(big.Int)), + }, + Y: [2]*big.Int{ + b.BlobCommitments.LengthProof.Y.A0.BigInt(new(big.Int)), + b.BlobCommitments.LengthProof.Y.A1.BigInt(new(big.Int)), + }, + }, + Length: uint32(b.BlobCommitments.Length), + }, + QuorumNumbers: b.QuorumNumbers, + PaymentMetadataHash: paymentHash, + }, nil +} + +func (b *BlobHeader) BlobKey() (BlobKey, error) { + blobHeaderType, err := abi.NewType("tuple", "", blobHeaderArgMarshaling()) + if err != nil { + return [32]byte{}, err + } + + arguments := abi.Arguments{ + { + Type: blobHeaderType, + }, + } + + s, err := b.toABIStruct() + if err != nil { + return [32]byte{}, err + } + + bytes, err := arguments.Pack(s) + if err != nil { + return [32]byte{}, err + } + + var headerHash [32]byte + hasher := sha3.NewLegacyKeccak256() + hasher.Write(bytes) + copy(headerHash[:], hasher.Sum(nil)[:32]) + + return headerHash, nil +} + +func (c *BlobCertificate) Hash() ([32]byte, error) { + if c.BlobHeader == nil { + return [32]byte{}, fmt.Errorf("blob header is nil") + } + + blobCertType, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{ + { + Name: "blobHeader", + Type: "tuple", + Components: blobHeaderArgMarshaling(), + }, + { + Name: "relayKeys", + Type: "uint16[]", + }, + }) + if err != nil { + return [32]byte{}, err + } + + arguments := abi.Arguments{ + { + Type: blobCertType, + }, + } + + bh, err := c.BlobHeader.toABIStruct() + if err != nil { + return [32]byte{}, err + } + s := struct { + BlobHeader abiBlobHeader + RelayKeys []RelayKey + }{ + BlobHeader: bh, + RelayKeys: c.RelayKeys, + } + + bytes, err := arguments.Pack(s) + if err != nil { + return [32]byte{}, err + } + + var blobCertHash [32]byte + hasher := sha3.NewLegacyKeccak256() + hasher.Write(bytes) + copy(blobCertHash[:], hasher.Sum(nil)[:32]) + + return blobCertHash, nil +} + +// GetBatchHeaderHash returns the hash of the batch header +func (h BatchHeader) Hash() ([32]byte, error) { + var headerHash [32]byte + + // The order here has to match the field ordering of ReducedBatchHeader defined in IEigenDAServiceManager.sol + // ref: https://github.com/Layr-Labs/eigenda/blob/master/contracts/src/interfaces/IEigenDAServiceManager.sol#L43 + batchHeaderType, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{ + { + Name: "blobHeadersRoot", + Type: "bytes32", + }, + { + Name: "referenceBlockNumber", + Type: "uint32", + }, + }) + if err != nil { + return headerHash, err + } + + arguments := abi.Arguments{ + { + Type: batchHeaderType, + }, + } + + s := struct { + BlobHeadersRoot [32]byte + ReferenceBlockNumber uint32 + }{ + BlobHeadersRoot: h.BatchRoot, + ReferenceBlockNumber: uint32(h.ReferenceBlockNumber), + } + + bytes, err := arguments.Pack(s) + if err != nil { + return headerHash, err + } + + hasher := sha3.NewLegacyKeccak256() + hasher.Write(bytes) + copy(headerHash[:], hasher.Sum(nil)[:32]) + + return headerHash, nil +} diff --git a/core/v2/types_test.go b/core/v2/serialization_test.go similarity index 70% rename from core/v2/types_test.go rename to core/v2/serialization_test.go index 74aab46b37..e9767de95e 100644 --- a/core/v2/types_test.go +++ b/core/v2/serialization_test.go @@ -56,7 +56,7 @@ func TestBlobKeyFromHeader(t *testing.T) { assert.Equal(t, "b19d368345990c79744fe571fe99f427f35787b9383c55089fb5bd6a5c171bbc", blobKey.Hex()) } -func TestBatchHeaderHAsh(t *testing.T) { +func TestBatchHeaderHash(t *testing.T) { batchRoot := [32]byte{} copy(batchRoot[:], []byte("1")) batchHeader := &v2.BatchHeader{ @@ -69,3 +69,31 @@ func TestBatchHeaderHAsh(t *testing.T) { // 0x891d0936da4627f445ef193aad63afb173409af9e775e292e4e35aff790a45e2 verified in solidity assert.Equal(t, "891d0936da4627f445ef193aad63afb173409af9e775e292e4e35aff790a45e2", hex.EncodeToString(hash[:])) } + +func TestBlobCertHash(t *testing.T) { + data := codec.ConvertByPaddingEmptyByte(GETTYSBURG_ADDRESS_BYTES) + commitments, err := p.GetCommitments(data) + if err != nil { + t.Fatal(err) + } + + blobCert := &v2.BlobCertificate{ + BlobHeader: &v2.BlobHeader{ + BlobVersion: 0, + BlobCommitments: commitments, + QuorumNumbers: []core.QuorumID{0, 1}, + PaymentMetadata: core.PaymentMetadata{ + AccountID: "0x123", + BinIndex: 5, + CumulativePayment: big.NewInt(100), + }, + Signature: []byte{1, 2, 3}, + }, + RelayKeys: []v2.RelayKey{4, 5, 6}, + } + + hash, err := blobCert.Hash() + assert.NoError(t, err) + // 0xc4512b8702f69cb837fff50a93d3d28aada535b1f151b64db45859c3f5bb096a verified in solidity + assert.Equal(t, "c4512b8702f69cb837fff50a93d3d28aada535b1f151b64db45859c3f5bb096a", hex.EncodeToString(hash[:])) +} diff --git a/core/v2/types.go b/core/v2/types.go index dde552cc5c..e2d2ac5813 100644 --- a/core/v2/types.go +++ b/core/v2/types.go @@ -12,9 +12,7 @@ import ( "github.com/Layr-Labs/eigenda/core" "github.com/Layr-Labs/eigenda/encoding" "github.com/consensys/gnark-crypto/ecc/bn254" - "github.com/ethereum/go-ethereum/accounts/abi" gethcommon "github.com/ethereum/go-ethereum/common" - "golang.org/x/crypto/sha3" ) var ( @@ -162,153 +160,6 @@ func (b *BlobHeader) GetEncodingParams() (encoding.EncodingParams, error) { }, nil } -func (b *BlobHeader) BlobKey() (BlobKey, error) { - blobHeaderType, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{ - { - Name: "blobVersion", - Type: "uint8", - }, - { - Name: "blobCommitments", - Type: "tuple", - Components: []abi.ArgumentMarshaling{ - { - Name: "commitment", - Type: "tuple", - Components: []abi.ArgumentMarshaling{ - { - Name: "X", - Type: "uint256", - }, - { - Name: "Y", - Type: "uint256", - }, - }, - }, - { - Name: "lengthCommitment", - Type: "tuple", - Components: []abi.ArgumentMarshaling{ - { - Name: "X", - Type: "uint256[2]", - }, - { - Name: "Y", - Type: "uint256[2]", - }, - }, - }, - { - Name: "lengthProof", - Type: "tuple", - Components: []abi.ArgumentMarshaling{ - { - Name: "X", - Type: "uint256[2]", - }, - { - Name: "Y", - Type: "uint256[2]", - }, - }, - }, - { - Name: "length", - Type: "uint32", - }, - }, - }, - { - Name: "quorumNumbers", - Type: "bytes", - }, - { - Name: "paymentMetadataHash", - Type: "bytes32", - }, - }) - if err != nil { - return [32]byte{}, err - } - - arguments := abi.Arguments{ - { - Type: blobHeaderType, - }, - } - - type g1Commit struct { - X *big.Int - Y *big.Int - } - type g2Commit struct { - X [2]*big.Int - Y [2]*big.Int - } - type blobCommitments struct { - Commitment g1Commit - LengthCommitment g2Commit - LengthProof g2Commit - Length uint32 - } - - paymentHash, err := b.PaymentMetadata.Hash() - if err != nil { - return [32]byte{}, err - } - s := struct { - BlobVersion uint8 - BlobCommitments blobCommitments - QuorumNumbers []byte - PaymentMetadataHash [32]byte - }{ - BlobVersion: uint8(b.BlobVersion), - BlobCommitments: blobCommitments{ - Commitment: g1Commit{ - X: b.BlobCommitments.Commitment.X.BigInt(new(big.Int)), - Y: b.BlobCommitments.Commitment.Y.BigInt(new(big.Int)), - }, - LengthCommitment: g2Commit{ - X: [2]*big.Int{ - b.BlobCommitments.LengthCommitment.X.A0.BigInt(new(big.Int)), - b.BlobCommitments.LengthCommitment.X.A1.BigInt(new(big.Int)), - }, - Y: [2]*big.Int{ - b.BlobCommitments.LengthCommitment.Y.A0.BigInt(new(big.Int)), - b.BlobCommitments.LengthCommitment.Y.A1.BigInt(new(big.Int)), - }, - }, - LengthProof: g2Commit{ - X: [2]*big.Int{ - b.BlobCommitments.LengthProof.X.A0.BigInt(new(big.Int)), - b.BlobCommitments.LengthProof.X.A1.BigInt(new(big.Int)), - }, - Y: [2]*big.Int{ - b.BlobCommitments.LengthProof.Y.A0.BigInt(new(big.Int)), - b.BlobCommitments.LengthProof.Y.A1.BigInt(new(big.Int)), - }, - }, - Length: uint32(b.BlobCommitments.Length), - }, - QuorumNumbers: b.QuorumNumbers, - PaymentMetadataHash: paymentHash, - } - - bytes, err := arguments.Pack(s) - if err != nil { - return [32]byte{}, err - } - - var headerHash [32]byte - hasher := sha3.NewLegacyKeccak256() - hasher.Write(bytes) - copy(headerHash[:], hasher.Sum(nil)[:32]) - - return headerHash, nil -} - type RelayKey uint16 type BlobCertificate struct { @@ -344,52 +195,6 @@ type BatchHeader struct { ReferenceBlockNumber uint64 } -// GetBatchHeaderHash returns the hash of the batch header -func (h BatchHeader) Hash() ([32]byte, error) { - var headerHash [32]byte - - // The order here has to match the field ordering of ReducedBatchHeader defined in IEigenDAServiceManager.sol - // ref: https://github.com/Layr-Labs/eigenda/blob/master/contracts/src/interfaces/IEigenDAServiceManager.sol#L43 - batchHeaderType, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{ - { - Name: "blobHeadersRoot", - Type: "bytes32", - }, - { - Name: "referenceBlockNumber", - Type: "uint32", - }, - }) - if err != nil { - return headerHash, err - } - - arguments := abi.Arguments{ - { - Type: batchHeaderType, - }, - } - - s := struct { - BlobHeadersRoot [32]byte - ReferenceBlockNumber uint32 - }{ - BlobHeadersRoot: h.BatchRoot, - ReferenceBlockNumber: uint32(h.ReferenceBlockNumber), - } - - bytes, err := arguments.Pack(s) - if err != nil { - return headerHash, err - } - - hasher := sha3.NewLegacyKeccak256() - hasher.Write(bytes) - copy(headerHash[:], hasher.Sum(nil)[:32]) - - return headerHash, nil -} - type Batch struct { BatchHeader *BatchHeader BlobCertificates []*BlobCertificate @@ -413,9 +218,11 @@ type Attestation struct { } type BlobVerificationInfo struct { - BlobCertificate *BlobCertificate - BlobIndex uint32 - InclusionProof []byte + *BatchHeader + + BlobKey + BlobIndex uint32 + InclusionProof []byte } type BlobVersionParameters struct {