wormhole/node/cmd/guardiand/adminserver_test.go

258 lines
7.4 KiB
Go

package guardiand
import (
"context"
"crypto/ecdsa"
"testing"
"time"
nodev1 "github.com/certusone/wormhole/node/pkg/proto/node/v1"
"github.com/certusone/wormhole/node/pkg/watchers/evm/connectors"
"github.com/certusone/wormhole/node/pkg/watchers/evm/connectors/ethabi"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event"
"github.com/stretchr/testify/require"
"github.com/wormhole-foundation/wormhole/sdk/vaa"
"go.uber.org/zap"
)
type mockEVMConnector struct {
guardianAddrs []common.Address
guardianSetIndex uint32
}
func (m mockEVMConnector) GetCurrentGuardianSetIndex(ctx context.Context) (uint32, error) {
return m.guardianSetIndex, nil
}
func (m mockEVMConnector) GetGuardianSet(ctx context.Context, index uint32) (ethabi.StructsGuardianSet, error) {
return ethabi.StructsGuardianSet{
Keys: m.guardianAddrs,
ExpirationTime: 0,
}, nil
}
func (m mockEVMConnector) NetworkName() string {
panic("unimplemented")
}
func (m mockEVMConnector) ContractAddress() common.Address {
panic("unimplemented")
}
func (m mockEVMConnector) WatchLogMessagePublished(ctx context.Context, sink chan<- *ethabi.AbiLogMessagePublished) (event.Subscription, error) {
panic("unimplemented")
}
func (m mockEVMConnector) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
panic("unimplemented")
}
func (m mockEVMConnector) TimeOfBlockByHash(ctx context.Context, hash common.Hash) (uint64, error) {
panic("unimplemented")
}
func (m mockEVMConnector) ParseLogMessagePublished(log types.Log) (*ethabi.AbiLogMessagePublished, error) {
panic("unimplemented")
}
func (m mockEVMConnector) SubscribeForBlocks(ctx context.Context, sink chan<- *connectors.NewBlock) (ethereum.Subscription, error) {
panic("unimplemented")
}
func (m mockEVMConnector) RawCallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
panic("unimplemented")
}
func generateGS(num int) (keys []*ecdsa.PrivateKey, addrs []common.Address) {
for i := 0; i < num; i++ {
key, err := ethcrypto.GenerateKey()
if err != nil {
panic(err)
}
keys = append(keys, key)
addrs = append(addrs, ethcrypto.PubkeyToAddress(key.PublicKey))
}
return
}
func addrsToHexStrings(addrs []common.Address) (out []string) {
for _, addr := range addrs {
out = append(out, addr.String())
}
return
}
func generateMockVAA(gsIndex uint32, gsKeys []*ecdsa.PrivateKey) []byte {
v := &vaa.VAA{
Version: 1,
GuardianSetIndex: gsIndex,
Signatures: nil,
Timestamp: time.Now(),
Nonce: 3,
Sequence: 79,
ConsistencyLevel: 1,
EmitterChain: 1,
EmitterAddress: vaa.Address{},
Payload: []byte("test"),
}
for i, key := range gsKeys {
v.AddSignature(key, uint8(i))
}
vBytes, err := v.Marshal()
if err != nil {
panic(err)
}
return vBytes
}
func setupAdminServerForVAASigning(gsIndex uint32, gsAddrs []common.Address) *nodePrivilegedService {
gk, err := ethcrypto.GenerateKey()
if err != nil {
panic(err)
}
connector := mockEVMConnector{
guardianAddrs: gsAddrs,
guardianSetIndex: gsIndex,
}
return &nodePrivilegedService{
db: nil,
injectC: nil,
obsvReqSendC: nil,
logger: zap.L(),
signedInC: nil,
governor: nil,
evmConnector: connector,
gk: gk,
guardianAddress: ethcrypto.PubkeyToAddress(gk.PublicKey),
}
}
func TestSignExistingVAA_NoVAA(t *testing.T) {
s := setupAdminServerForVAASigning(0, []common.Address{})
_, err := s.SignExistingVAA(context.Background(), &nodev1.SignExistingVAARequest{
Vaa: nil,
NewGuardianAddrs: nil,
NewGuardianSetIndex: 0,
})
require.ErrorContains(t, err, "failed to unmarshal VAA")
}
func TestSignExistingVAA_NotGuardian(t *testing.T) {
gsKeys, gsAddrs := generateGS(5)
s := setupAdminServerForVAASigning(0, gsAddrs)
v := generateMockVAA(0, gsKeys)
_, err := s.SignExistingVAA(context.Background(), &nodev1.SignExistingVAARequest{
Vaa: v,
NewGuardianAddrs: addrsToHexStrings(gsAddrs),
NewGuardianSetIndex: 1,
})
require.ErrorContains(t, err, "local guardian is not a member of the new guardian set")
}
func TestSignExistingVAA_InvalidVAA(t *testing.T) {
gsKeys, gsAddrs := generateGS(5)
s := setupAdminServerForVAASigning(0, gsAddrs)
v := generateMockVAA(0, gsKeys[:2])
gsAddrs = append(gsAddrs, s.guardianAddress)
_, err := s.SignExistingVAA(context.Background(), &nodev1.SignExistingVAARequest{
Vaa: v,
NewGuardianAddrs: addrsToHexStrings(gsAddrs),
NewGuardianSetIndex: 1,
})
require.ErrorContains(t, err, "failed to verify existing VAA")
}
func TestSignExistingVAA_DuplicateGuardian(t *testing.T) {
gsKeys, gsAddrs := generateGS(5)
s := setupAdminServerForVAASigning(0, gsAddrs)
v := generateMockVAA(0, gsKeys)
gsAddrs = append(gsAddrs, s.guardianAddress)
gsAddrs = append(gsAddrs, s.guardianAddress)
_, err := s.SignExistingVAA(context.Background(), &nodev1.SignExistingVAARequest{
Vaa: v,
NewGuardianAddrs: addrsToHexStrings(gsAddrs),
NewGuardianSetIndex: 1,
})
require.ErrorContains(t, err, "duplicate guardians in the guardian set")
}
func TestSignExistingVAA_AlreadyGuardian(t *testing.T) {
gsKeys, gsAddrs := generateGS(5)
s := setupAdminServerForVAASigning(0, gsAddrs)
s.evmConnector = mockEVMConnector{
guardianAddrs: append(gsAddrs, s.guardianAddress),
guardianSetIndex: 0,
}
v := generateMockVAA(0, append(gsKeys, s.gk))
gsAddrs = append(gsAddrs, s.guardianAddress)
_, err := s.SignExistingVAA(context.Background(), &nodev1.SignExistingVAARequest{
Vaa: v,
NewGuardianAddrs: addrsToHexStrings(gsAddrs),
NewGuardianSetIndex: 1,
})
require.ErrorContains(t, err, "local guardian is already on the old set")
}
func TestSignExistingVAA_NotAFutureGuardian(t *testing.T) {
gsKeys, gsAddrs := generateGS(5)
s := setupAdminServerForVAASigning(0, gsAddrs)
v := generateMockVAA(0, gsKeys)
_, err := s.SignExistingVAA(context.Background(), &nodev1.SignExistingVAARequest{
Vaa: v,
NewGuardianAddrs: addrsToHexStrings(gsAddrs),
NewGuardianSetIndex: 1,
})
require.ErrorContains(t, err, "local guardian is not a member of the new guardian set")
}
func TestSignExistingVAA_CantReachQuorum(t *testing.T) {
gsKeys, gsAddrs := generateGS(5)
s := setupAdminServerForVAASigning(0, gsAddrs)
v := generateMockVAA(0, gsKeys)
gsAddrs = append(gsAddrs, s.guardianAddress)
_, err := s.SignExistingVAA(context.Background(), &nodev1.SignExistingVAARequest{
Vaa: v,
NewGuardianAddrs: addrsToHexStrings(append(gsAddrs, common.Address{0, 1}, common.Address{3, 1}, common.Address{8, 1})),
NewGuardianSetIndex: 1,
})
require.ErrorContains(t, err, "cannot reach quorum on new guardian set with the local signature")
}
func TestSignExistingVAA_Valid(t *testing.T) {
gsKeys, gsAddrs := generateGS(5)
s := setupAdminServerForVAASigning(0, gsAddrs)
v := generateMockVAA(0, gsKeys)
gsAddrs = append(gsAddrs, s.guardianAddress)
res, err := s.SignExistingVAA(context.Background(), &nodev1.SignExistingVAARequest{
Vaa: v,
NewGuardianAddrs: addrsToHexStrings(gsAddrs),
NewGuardianSetIndex: 1,
})
require.NoError(t, err)
v2 := generateMockVAA(1, append(gsKeys, s.gk))
require.Equal(t, v2, res.Vaa)
}