diff --git a/core/auth/payment_metadata.go b/core/auth/payment_metadata.go deleted file mode 100644 index c43b8a3e53..0000000000 --- a/core/auth/payment_metadata.go +++ /dev/null @@ -1,168 +0,0 @@ -package auth - -import ( - "crypto/ecdsa" - "fmt" - "math/big" - - "github.com/Layr-Labs/eigenda/core" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/signer/core/apitypes" -) - -/* SUBJECT TO MODIFICATIONS */ - -// EIP712Signer handles EIP-712 domain specific signing operations over typed and structured data -type EIP712Signer struct { - domain apitypes.TypedDataDomain - types apitypes.Types -} - -// NewEIP712Signer creates a new EIP712Signer instance -func NewEIP712Signer(chainID *big.Int, verifyingContract common.Address) *EIP712Signer { - return &EIP712Signer{ - domain: apitypes.TypedDataDomain{ - Name: "EigenDA", - Version: "1", - ChainId: (*math.HexOrDecimal256)(chainID), - VerifyingContract: verifyingContract.Hex(), - }, - types: apitypes.Types{ - "EIP712Domain": []apitypes.Type{ - {Name: "name", Type: "string"}, - {Name: "version", Type: "string"}, - {Name: "chainId", Type: "uint256"}, - {Name: "verifyingContract", Type: "address"}, - }, - "PaymentMetadata": []apitypes.Type{ - {Name: "accountID", Type: "string"}, - {Name: "binIndex", Type: "uint32"}, - {Name: "cumulativePayment", Type: "uint64"}, - {Name: "dataLength", Type: "uint32"}, - {Name: "quorumNumbers", Type: "uint8[]"}, - }, - }, - } -} - -// SignPaymentMetadata signs a PaymentMetadata using EIP-712 -func (s *EIP712Signer) SignPaymentMetadata(header *core.PaymentMetadata, privateKey *ecdsa.PrivateKey) ([]byte, error) { - typedData := apitypes.TypedData{ - Types: s.types, - PrimaryType: "PaymentMetadata", - Domain: s.domain, - Message: apitypes.TypedDataMessage{ - "accountID": header.AccountID, - "binIndex": fmt.Sprintf("%d", header.BinIndex), - "cumulativePayment": fmt.Sprintf("%d", header.CumulativePayment), - "dataLength": fmt.Sprintf("%d", header.DataLength), - "quorumNumbers": convertUint8SliceToMap(header.QuorumNumbers), - }, - } - - signature, err := s.signTypedData(typedData, privateKey) - if err != nil { - return nil, fmt.Errorf("error signing payment metadata (header): %v", err) - } - - return signature, nil -} - -func convertUint8SliceToMap(params []uint8) []string { - result := make([]string, len(params)) - for i, param := range params { - result[i] = fmt.Sprintf("%d", param) // Converting uint32 to string - } - return result -} - -// RecoverSender recovers the sender's address from a signed PaymentMetadata -func (s *EIP712Signer) RecoverSender(header *core.PaymentMetadata) (common.Address, error) { - typedData := apitypes.TypedData{ - Types: s.types, - PrimaryType: "PaymentMetadata", - Domain: s.domain, - Message: apitypes.TypedDataMessage{ - "accountID": header.AccountID, - "binIndex": fmt.Sprintf("%d", header.BinIndex), - "cumulativePayment": fmt.Sprintf("%d", header.CumulativePayment), - "dataLength": fmt.Sprintf("%d", header.DataLength), - "quorumNumbers": convertUint8SliceToMap(header.QuorumNumbers), - }, - } - - return s.recoverTypedData(typedData, header.Signature) -} - -func (s *EIP712Signer) signTypedData(typedData apitypes.TypedData, privateKey *ecdsa.PrivateKey) ([]byte, error) { - domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) - if err != nil { - return nil, fmt.Errorf("error hashing EIP712Domain: %v", err) - } - - typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) - if err != nil { - return nil, fmt.Errorf("error hashing primary type: %v", err) - } - - rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) - digest := crypto.Keccak256(rawData) - - signature, err := crypto.Sign(digest, privateKey) - if err != nil { - return nil, fmt.Errorf("error signing digest: %v", err) - } - - return signature, nil -} - -func (s *EIP712Signer) recoverTypedData(typedData apitypes.TypedData, signature []byte) (common.Address, error) { - domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) - if err != nil { - return common.Address{}, fmt.Errorf("error hashing EIP712Domain: %v", err) - } - - typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) - if err != nil { - return common.Address{}, fmt.Errorf("error hashing primary type: %v", err) - } - - rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) - digest := crypto.Keccak256(rawData) - - pubKey, err := crypto.SigToPub(digest, signature) - if err != nil { - return common.Address{}, fmt.Errorf("error recovering public key: %v", err) - } - - return crypto.PubkeyToAddress(*pubKey), nil -} - -// ConstructPaymentMetadata creates a PaymentMetadata with a valid signature -func ConstructPaymentMetadata( - signer *EIP712Signer, - binIndex uint32, - cumulativePayment uint64, - dataLength uint32, - quorumNumbers []uint8, - privateKey *ecdsa.PrivateKey, -) (*core.PaymentMetadata, error) { - accountID := crypto.PubkeyToAddress(privateKey.PublicKey).Hex() - header := &core.PaymentMetadata{ - AccountID: accountID, - BinIndex: binIndex, - CumulativePayment: cumulativePayment, - QuorumNumbers: quorumNumbers, - DataLength: dataLength, - } - - signature, err := signer.SignPaymentMetadata(header, privateKey) - if err != nil { - return nil, fmt.Errorf("error signing payment metadata (header): %v", err) - } - - header.Signature = signature - return header, nil -} diff --git a/core/auth/payment_metadata_test.go b/core/auth/payment_metadata_test.go deleted file mode 100644 index e9de77024d..0000000000 --- a/core/auth/payment_metadata_test.go +++ /dev/null @@ -1,173 +0,0 @@ -package auth_test - -import ( - "math/big" - "testing" - - "github.com/Layr-Labs/eigenda/core" - "github.com/Layr-Labs/eigenda/core/auth" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestEIP712Signer(t *testing.T) { - chainID := big.NewInt(17000) - verifyingContract := common.HexToAddress("0x1234000000000000000000000000000000000000") - signer := auth.NewEIP712Signer(chainID, verifyingContract) - - privateKey, err := crypto.GenerateKey() - require.NoError(t, err) - - header := &core.PaymentMetadata{ - BinIndex: 0, - CumulativePayment: 1000, - DataLength: 1024, - QuorumNumbers: []uint8{1}, - } - - t.Run("SignPaymentMetadata", func(t *testing.T) { - signature, err := signer.SignPaymentMetadata(header, privateKey) - require.NoError(t, err) - assert.NotEmpty(t, signature) - }) - - t.Run("RecoverSender", func(t *testing.T) { - signature, err := signer.SignPaymentMetadata(header, privateKey) - require.NoError(t, err) - - header.Signature = signature - recoveredAddress, err := signer.RecoverSender(header) - require.NoError(t, err) - - expectedAddress := crypto.PubkeyToAddress(privateKey.PublicKey) - assert.Equal(t, expectedAddress, recoveredAddress) - }) -} - -func TestConstructPaymentMetadata(t *testing.T) { - chainID := big.NewInt(17000) - verifyingContract := common.HexToAddress("0x1234000000000000000000000000000000000000") - signer := auth.NewEIP712Signer(chainID, verifyingContract) - - privateKey, err := crypto.GenerateKey() - require.NoError(t, err) - - header, err := auth.ConstructPaymentMetadata( - signer, - 0, // binIndex - 1000, // cumulativePayment - 1024, // dataLength - []uint8{1}, - privateKey, - ) - - require.NoError(t, err) - assert.NotNil(t, header) - assert.NotEmpty(t, header.Signature) - - recoveredAddress, err := signer.RecoverSender(header) - require.NoError(t, err) - - expectedAddress := crypto.PubkeyToAddress(privateKey.PublicKey) - assert.Equal(t, expectedAddress, recoveredAddress) -} - -func TestEIP712SignerWithDifferentKeys(t *testing.T) { - chainID := big.NewInt(17000) - verifyingContract := common.HexToAddress("0x1234000000000000000000000000000000000000") - signer := auth.NewEIP712Signer(chainID, verifyingContract) - - privateKey1, err := crypto.GenerateKey() - require.NoError(t, err) - - privateKey2, err := crypto.GenerateKey() - require.NoError(t, err) - - header, err := auth.ConstructPaymentMetadata( - signer, - 0, - 1000, - 1024, - []uint8{1}, - privateKey1, - ) - - require.NoError(t, err) - assert.NotNil(t, header) - assert.NotEmpty(t, header.Signature) - - recoveredAddress, err := signer.RecoverSender(header) - require.NoError(t, err) - - expectedAddress1 := crypto.PubkeyToAddress(privateKey1.PublicKey) - expectedAddress2 := crypto.PubkeyToAddress(privateKey2.PublicKey) - - assert.Equal(t, expectedAddress1, recoveredAddress) - assert.NotEqual(t, expectedAddress2, recoveredAddress) -} - -func TestEIP712SignerWithModifiedHeader(t *testing.T) { - chainID := big.NewInt(17000) - verifyingContract := common.HexToAddress("0x1234000000000000000000000000000000000000") - signer := auth.NewEIP712Signer(chainID, verifyingContract) - - privateKey, err := crypto.GenerateKey() - require.NoError(t, err) - - header, err := auth.ConstructPaymentMetadata( - signer, - 0, - 1000, - 1024, - []uint8{1}, - privateKey, - ) - - require.NoError(t, err) - assert.NotNil(t, header) - assert.NotEmpty(t, header.Signature) - recoveredAddress, err := signer.RecoverSender(header) - require.NoError(t, err) - - expectedAddress := crypto.PubkeyToAddress(privateKey.PublicKey) - assert.Equal(t, expectedAddress, recoveredAddress, "Recovered address should match the address derived from the private key") - - header.AccountID = "modifiedAccount" - - addr, err := signer.RecoverSender(header) - require.NoError(t, err) - require.NotEqual(t, expectedAddress, addr) -} - -func TestEIP712SignerWithDifferentChainID(t *testing.T) { - chainID1 := big.NewInt(17000) - chainID2 := big.NewInt(17001) - verifyingContract := common.HexToAddress("0x1234000000000000000000000000000000000000") - signer1 := auth.NewEIP712Signer(chainID1, verifyingContract) - signer2 := auth.NewEIP712Signer(chainID2, verifyingContract) - - privateKey, err := crypto.GenerateKey() - require.NoError(t, err) - - header, err := auth.ConstructPaymentMetadata( - signer1, - 0, - 1000, - 1024, - []uint8{1}, - privateKey, - ) - - require.NoError(t, err) - assert.NotNil(t, header) - assert.NotEmpty(t, header.Signature) - - // Try to recover the sender using a signer with a different chain ID - sender, err := signer2.RecoverSender(header) - require.NoError(t, err) - expectedAddress := crypto.PubkeyToAddress(privateKey.PublicKey) - - require.NotEqual(t, expectedAddress, sender) -} diff --git a/core/data.go b/core/data.go index 5403282088..96593ffd18 100644 --- a/core/data.go +++ b/core/data.go @@ -8,6 +8,7 @@ import ( "github.com/Layr-Labs/eigenda/common" "github.com/Layr-Labs/eigenda/encoding" "github.com/consensys/gnark-crypto/ecc/bn254" + "github.com/ethereum/go-ethereum/crypto" ) type AccountID = string @@ -474,17 +475,31 @@ func (cb Bundles) FromEncodedBundles(eb EncodedBundles) (Bundles, error) { // PaymentMetadata represents the header information for a blob type PaymentMetadata struct { // Existing fields - DataLength uint32 // length in number of symbols - QuorumNumbers []uint8 - AccountID string + AccountID string // New fields BinIndex uint32 // TODO: we are thinking the contract can use uint128 for cumulative payment, // but the definition on v2 uses uint64. Double check with team. CumulativePayment uint64 +} + +// Hash returns the Keccak256 hash of the PaymentMetadata +func (pm *PaymentMetadata) Hash() []byte { + // Create a byte slice to hold the serialized data + data := make([]byte, 0, len(pm.AccountID)+12) + + data = append(data, []byte(pm.AccountID)...) + + binIndexBytes := make([]byte, 4) + binary.BigEndian.PutUint32(binIndexBytes, pm.BinIndex) + data = append(data, binIndexBytes...) + + paymentBytes := make([]byte, 8) + binary.BigEndian.PutUint64(paymentBytes, pm.CumulativePayment) + data = append(data, paymentBytes...) - Signature []byte + return crypto.Keccak256(data) } type TokenAmount uint64 // TODO: change to uint128 diff --git a/core/meterer/meterer_test.go b/core/meterer/meterer_test.go index 97e8e96aea..64ef0be08f 100644 --- a/core/meterer/meterer_test.go +++ b/core/meterer/meterer_test.go @@ -3,7 +3,6 @@ package meterer_test import ( "crypto/ecdsa" "fmt" - "math/big" "os" "testing" "time" @@ -11,11 +10,9 @@ import ( "github.com/Layr-Labs/eigenda/common" commonaws "github.com/Layr-Labs/eigenda/common/aws" commondynamodb "github.com/Layr-Labs/eigenda/common/aws/dynamodb" - "github.com/Layr-Labs/eigenda/core/auth" "github.com/Layr-Labs/eigenda/core/meterer" "github.com/Layr-Labs/eigenda/core/mock" "github.com/Layr-Labs/eigenda/inabox/deploy" - gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ory/dockertest/v3" @@ -29,7 +26,6 @@ var ( clientConfig commonaws.ClientConfig privateKey1 *ecdsa.PrivateKey privateKey2 *ecdsa.PrivateKey - signer *auth.EIP712Signer mt *meterer.Meterer deployLocalStack bool @@ -80,10 +76,6 @@ func setup(_ *testing.M) { panic("failed to create dynamodb client") } - chainID := big.NewInt(17000) - verifyingContract := gethcommon.HexToAddress("0x1234000000000000000000000000000000000000") - signer = auth.NewEIP712Signer(chainID, verifyingContract) - privateKey1, err = crypto.GenerateKey() if err != nil { teardown()