-
Notifications
You must be signed in to change notification settings - Fork 652
/
vaa_verifier.go
131 lines (108 loc) · 4.04 KB
/
vaa_verifier.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
129
130
131
package spy
import (
"context"
"fmt"
"sync"
"time"
"github.com/certusone/wormhole/node/pkg/common"
"github.com/wormhole-foundation/wormhole/sdk/vaa"
"go.uber.org/zap"
ethAbi "github.com/certusone/wormhole/node/pkg/watchers/evm/connectors/ethabi"
ethBind "github.com/ethereum/go-ethereum/accounts/abi/bind"
ethCommon "github.com/ethereum/go-ethereum/common"
ethClient "github.com/ethereum/go-ethereum/ethclient"
ethRpc "github.com/ethereum/go-ethereum/rpc"
)
// VaaVerifier is an object that can be used to validate VAA signatures.
// It reads the guardian set on chain whenever a new guardian set index is detected.
type VaaVerifier struct {
logger *zap.Logger
rpcUrl string
coreAddr ethCommon.Address
lock sync.Mutex
guardianSets map[uint32]*common.GuardianSet
}
// RpcTimeout is the context timeout on RPC calls.
const RpcTimeout = time.Second * 5
// NewVaaVerifier creates a VaaVerifier.
func NewVaaVerifier(logger *zap.Logger, rpcUrl string, coreAddr string) *VaaVerifier {
return &VaaVerifier{
logger: logger,
rpcUrl: rpcUrl,
coreAddr: ethCommon.HexToAddress(coreAddr),
guardianSets: make(map[uint32]*common.GuardianSet),
}
}
// GetInitialGuardianSet gets the current guardian set and adds it to the map. It is not necessary
// to call this function, but doing so will allow you to verify that the RPC endpoint works on start up,
// rather than having it fail the first VAA is received.
func (v *VaaVerifier) GetInitialGuardianSet() error {
timeout, cancel := context.WithTimeout(context.Background(), RpcTimeout)
defer cancel()
rawClient, err := ethRpc.DialContext(timeout, v.rpcUrl)
if err != nil {
return fmt.Errorf("failed to connect to ethereum: %w", err)
}
client := ethClient.NewClient(rawClient)
caller, err := ethAbi.NewAbiCaller(v.coreAddr, client)
if err != nil {
return fmt.Errorf("failed to create caller: %w", err)
}
gsIndex, err := caller.GetCurrentGuardianSetIndex(ðBind.CallOpts{Context: timeout})
if err != nil {
return fmt.Errorf("error requesting current guardian set index: %w", err)
}
result, err := caller.GetGuardianSet(ðBind.CallOpts{Context: timeout}, gsIndex)
if err != nil {
return fmt.Errorf("error requesting guardian set for index %d: %w", gsIndex, err)
}
gs := &common.GuardianSet{
Keys: result.Keys,
Index: gsIndex,
}
v.logger.Warn("read current guardian set", zap.Uint32("index", gsIndex), zap.Any("gs", *gs))
v.guardianSets[gsIndex] = gs
return nil
}
// VerifySignatures verifies that the signature on a VAA is valid, based on the guardian set contained in the VAA.
// If the guardian set is not currently in our map, it queries that guardian set and adds it.
func (v *VaaVerifier) VerifySignatures(vv *vaa.VAA) (bool, error) {
v.lock.Lock()
defer v.lock.Unlock()
gs, exists := v.guardianSets[vv.GuardianSetIndex]
if !exists {
var err error
gs, err = v.fetchGuardianSet(vv.GuardianSetIndex)
if err != nil {
return false, fmt.Errorf("failed to fetch guardian set for index %d: %w", vv.GuardianSetIndex, err)
}
v.logger.Warn("read guardian set", zap.Uint32("index", gs.Index), zap.Any("gs", *gs))
v.guardianSets[gs.Index] = gs
}
if err := vv.Verify(gs.Keys); err != nil {
return false, nil
}
return true, nil
}
// fetchGuardianSet reads the guardian set for the index passed in.
func (v *VaaVerifier) fetchGuardianSet(gsIndex uint32) (*common.GuardianSet, error) {
timeout, cancel := context.WithTimeout(context.Background(), RpcTimeout)
defer cancel()
rawClient, err := ethRpc.DialContext(timeout, v.rpcUrl)
if err != nil {
return nil, fmt.Errorf("failed to connect to ethereum: %w", err)
}
client := ethClient.NewClient(rawClient)
caller, err := ethAbi.NewAbiCaller(v.coreAddr, client)
if err != nil {
return nil, fmt.Errorf("failed to create caller: %w", err)
}
gs, err := caller.GetGuardianSet(ðBind.CallOpts{Context: timeout}, gsIndex)
if err != nil {
return nil, fmt.Errorf("error requesting current guardian set for index %d: %w", gsIndex, err)
}
return &common.GuardianSet{
Keys: gs.Keys,
Index: gsIndex,
}, nil
}