-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathvalidate.go
128 lines (111 loc) · 4.99 KB
/
validate.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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package ethauth
import (
"context"
"fmt"
"math/big"
"github.com/0xsequence/ethkit/ethcoder"
"github.com/0xsequence/ethkit/ethrpc"
"github.com/0xsequence/ethkit/ethwallet"
"github.com/0xsequence/ethkit/go-ethereum"
"github.com/0xsequence/ethkit/go-ethereum/common"
"github.com/0xsequence/ethkit/go-ethereum/common/hexutil"
)
type ValidatorFunc func(ctx context.Context, provider *ethrpc.Provider, chainID *big.Int, proof *Proof) (bool, string, error)
// ValidateEOAProof verifies the account proof, testing if the proof claims have been signed with an
// EOA (externally owned account) and will return success/failture, the account address as a string, and any errors.
func ValidateEOAProof(ctx context.Context, provider *ethrpc.Provider, chainID *big.Int, proof *Proof) (bool, string, error) {
// Compute eip712 encoded message from the proof claims
message, err := proof.Message()
if err != nil {
return false, "", fmt.Errorf("ValidateEOAProof failed. Unable to compute ethauth message digest, because %w", err)
}
isValid, err := ValidateEOASignature(proof.Address, message, proof.Signature)
if err != nil {
return false, "", err
}
if !isValid {
return false, "", fmt.Errorf("ValidateEOAProof failed. invalid EOA signature")
}
return true, proof.Address, nil
}
// ValidateContractAccountProof verifies the account proof, testing if the
// proof claims have been signed with a smart-contract based account by calling the EIP-1271
// method of the remote contract. This method will return success/failure, the
// account address as a string, and any errors. The wallet contract must be deployed in
// order for this call to be successful. In order test an undeployed smart-wallet, you
// will have to implement your own custom validator method.
func ValidateContractAccountProof(ctx context.Context, provider *ethrpc.Provider, chainID *big.Int, proof *Proof) (bool, string, error) {
if provider == nil {
return false, "", fmt.Errorf("ValidateContractAccountProof failed. provider is nil")
}
if chainID == nil {
return false, "", fmt.Errorf("ValidateContractAccountProof failed. chainID is nil")
}
// Compute eip712 message digest from the proof claims
messageDigest, err := proof.MessageDigest()
if err != nil {
return false, "", fmt.Errorf("ValidateContractAccountProof failed. Unable to compute ethauth message digest, because %w", err)
}
// Early check to ensure the contract wallet has been deployed
walletCode, err := provider.CodeAt(ctx, common.HexToAddress(proof.Address), nil)
if err != nil {
return false, "", fmt.Errorf("ValidateContractAccountProof failed. unable to fetch wallet contract code - %w", err)
}
if len(walletCode) == 0 {
return false, "", fmt.Errorf("ValidateContractAccountProof failed. unable to fetch wallet contract code, likely wallet has not been deployed")
}
// Call EIP-1271 IsValidSignature(bytes32, bytes) method on the deployed wallet. Note: for undeployed
// wallets, you will need to implement your own ValidatorFunc with the additional context.
signature, err := ethcoder.HexDecode(proof.Signature)
if err != nil {
return false, "", fmt.Errorf("ValidateContractAccountProof failed. HexDecode of proof.signature failed - %w", err)
}
input, err := ethcoder.ABIEncodeMethodCalldata("isValidSignature(bytes32,bytes)", []interface{}{
ethcoder.BytesToBytes32(messageDigest),
signature,
})
if err != nil {
return false, "", fmt.Errorf("ValidateContractAccountProof failed. EncodeMethodCalldata error")
}
toAddress := common.HexToAddress(proof.Address)
txMsg := ethereum.CallMsg{
To: &toAddress,
Data: input,
}
output, err := provider.CallContract(context.Background(), txMsg, nil)
if err != nil {
return false, "", fmt.Errorf("ValidateContractAccountProof failed. Provider CallContract failed - %w", err)
}
isValid := len(output) >= 4 && IsValidSignatureBytes32MagicValue == ethcoder.HexEncode(output[:4])
if !isValid {
return false, "", fmt.Errorf("ValidateContractAccountProof failed. invalid signature")
}
return true, proof.Address, nil
}
const (
// IsValidSignatureBytes32 is the EIP-1271 magic value we test
IsValidSignatureBytes32MagicValue = "0x1626ba7e"
)
// Validate the public key address of an Ethereum signed message
func ValidateEOASignature(address string, message []byte, signatureHex string) (bool, error) {
if !common.IsHexAddress(address) {
return false, fmt.Errorf("ValidateEOASignature, address is not a valid Ethereum address")
}
if len(message) < 1 || len(signatureHex) < 1 {
return false, fmt.Errorf("ValidateEOASignature, message and signature must not be empty")
}
sig, err := hexutil.Decode(signatureHex)
if err != nil {
return false, fmt.Errorf("ValidateEOASignature, signature is an invalid hex string")
}
isValid, err := ethwallet.IsValid191Signature(common.HexToAddress(address), message, sig)
if err != nil {
fmt.Println("NO1")
return false, fmt.Errorf("ValidateEOASignature, invalid signature")
}
if !isValid {
fmt.Println("NO2")
return false, fmt.Errorf("ValidateEOASignature, invalid signature")
}
return true, nil
}