This repository has been archived by the owner on Nov 28, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
sign_typed_data.go
69 lines (57 loc) · 2.83 KB
/
sign_typed_data.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package dydx
import (
"crypto/ecdsa"
"fmt"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/signer/core/apitypes"
)
const eip712DomainName = "EIP712Domain"
// SignTypedData is an interface to sign apitypes.TypedData on ethereum network.
// For a reference implementation, see:
// https://pkg.go.dev/github.com/ethereum/go-ethereum@v1.10.18/signer/core#SignerAPI.SignTypedData
type SignTypedData interface {
EthSignTypedData(typedData apitypes.TypedData) ([]byte, error)
}
// ecdsaPrivateKeySigner implements the SignTypedData interface for ecdsa.PrivateKey
type ecdsaPrivateKeySigner ecdsa.PrivateKey
// NewEcdsaPrivateKeySigner converts an *ecdsa.PrivateKey into a signer to EthSignTypedData
func NewEcdsaPrivateKeySigner(key *ecdsa.PrivateKey) *ecdsaPrivateKeySigner {
return (*ecdsaPrivateKeySigner)(key)
}
// prepareTypedDataForSign does part of the process in geth's SignerApi.SignTypedData.
// https://pkg.go.dev/github.com/ethereum/go-ethereum@v1.10.18/signer/core#SignerAPI.SignTypedData
// See the source code of tha method for the details.
// The function returns the raw bytes ([]byte) that can be fed into Wallet.SignData
func prepareTypedDataForSign(typedData apitypes.TypedData) ([]byte, error) {
domainSeparator, err := typedData.HashStruct(eip712DomainName, typedData.Domain.Map())
if err != nil {
return nil, fmt.Errorf("failed to generate domain separator: %w", err)
}
typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
if err != nil {
return nil, fmt.Errorf("failed to generate typed data hash: %w", err)
}
rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
return rawData, nil
}
// SignTypedData's process is coming from SignerApi.SignTypedData.
// https://pkg.go.dev/github.com/ethereum/go-ethereum@v1.10.18/signer/core#SignerAPI.SignTypedData
// See the source code of tha method for the details.
// The function returns the signed signature of the typedData.
// After the process of `PrepareTypedDataForSign`, the generated []byte can be signed by Wallet.SignData method.
// Wallet is defined here: https://pkg.go.dev/github.com/ethereum/go-ethereum@v1.10.18/accounts#Wallet.
// However, we only need SignData method without even the mimeType or account.
// SignData method in the keystoreWallet simply gets the hash and then sign the hash with the private key.
func (key *ecdsaPrivateKeySigner) EthSignTypedData(typedData apitypes.TypedData) ([]byte, error) {
rawData, err := prepareTypedDataForSign(typedData)
if err != nil {
return nil, fmt.Errorf("failed to convert typed data into raw bytes: %w", err)
}
sig, err := crypto.Sign(crypto.Keccak256(rawData), (*ecdsa.PrivateKey)(key))
if err != nil {
return nil, fmt.Errorf("failed to sign the raw bytes for typed data: %w", err)
}
// Legacy Signing
sig[64] += 27
return sig, nil
}