adminserver_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. //nolint:unparam
  2. package adminrpc
  3. import (
  4. "bytes"
  5. "context"
  6. "testing"
  7. "time"
  8. wh_common "github.com/certusone/wormhole/node/pkg/common"
  9. "github.com/certusone/wormhole/node/pkg/db"
  10. "github.com/certusone/wormhole/node/pkg/governor"
  11. "github.com/certusone/wormhole/node/pkg/guardiansigner"
  12. nodev1 "github.com/certusone/wormhole/node/pkg/proto/node/v1"
  13. "github.com/certusone/wormhole/node/pkg/watchers/evm/connectors"
  14. "github.com/certusone/wormhole/node/pkg/watchers/evm/connectors/ethabi"
  15. ethereum "github.com/ethereum/go-ethereum"
  16. "github.com/ethereum/go-ethereum/common"
  17. "github.com/ethereum/go-ethereum/core/types"
  18. ethcrypto "github.com/ethereum/go-ethereum/crypto"
  19. "github.com/ethereum/go-ethereum/ethclient"
  20. "github.com/ethereum/go-ethereum/event"
  21. ethRpc "github.com/ethereum/go-ethereum/rpc"
  22. "github.com/stretchr/testify/assert"
  23. "github.com/stretchr/testify/require"
  24. "github.com/wormhole-foundation/wormhole/sdk/vaa"
  25. "go.uber.org/zap"
  26. "google.golang.org/protobuf/encoding/prototext"
  27. )
  28. type mockEVMConnector struct {
  29. guardianAddrs []common.Address
  30. guardianSetIndex uint32
  31. }
  32. func (m mockEVMConnector) GetCurrentGuardianSetIndex(ctx context.Context) (uint32, error) {
  33. return m.guardianSetIndex, nil
  34. }
  35. func (m mockEVMConnector) GetGuardianSet(ctx context.Context, index uint32) (ethabi.StructsGuardianSet, error) {
  36. return ethabi.StructsGuardianSet{
  37. Keys: m.guardianAddrs,
  38. ExpirationTime: 0,
  39. }, nil
  40. }
  41. func (m mockEVMConnector) NetworkName() string {
  42. panic("unimplemented")
  43. }
  44. func (m mockEVMConnector) ContractAddress() common.Address {
  45. panic("unimplemented")
  46. }
  47. func (m mockEVMConnector) WatchLogMessagePublished(ctx context.Context, errC chan error, sink chan<- *ethabi.AbiLogMessagePublished) (event.Subscription, error) {
  48. panic("unimplemented")
  49. }
  50. func (m mockEVMConnector) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
  51. panic("unimplemented")
  52. }
  53. func (m mockEVMConnector) TimeOfBlockByHash(ctx context.Context, hash common.Hash) (uint64, error) {
  54. panic("unimplemented")
  55. }
  56. func (m mockEVMConnector) ParseLogMessagePublished(log types.Log) (*ethabi.AbiLogMessagePublished, error) {
  57. panic("unimplemented")
  58. }
  59. func (m mockEVMConnector) SubscribeForBlocks(ctx context.Context, errC chan error, sink chan<- *connectors.NewBlock) (ethereum.Subscription, error) {
  60. panic("unimplemented")
  61. }
  62. func (e mockEVMConnector) GetLatest(ctx context.Context) (latest, finalized, safe uint64, err error) {
  63. panic("unimplemented")
  64. }
  65. func (m mockEVMConnector) RawCallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
  66. panic("unimplemented")
  67. }
  68. func (m mockEVMConnector) RawBatchCallContext(ctx context.Context, b []ethRpc.BatchElem) error {
  69. panic("unimplemented")
  70. }
  71. func (c mockEVMConnector) Client() *ethclient.Client {
  72. panic("unimplemented")
  73. }
  74. func (c mockEVMConnector) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
  75. panic("unimplemented")
  76. }
  77. func generateGuardianSigners(num int) (signers []guardiansigner.GuardianSigner, addrs []common.Address) {
  78. for i := 0; i < num; i++ {
  79. signer, err := guardiansigner.GenerateSignerWithPrivatekeyUnsafe(nil)
  80. if err != nil {
  81. panic(err)
  82. }
  83. signers = append(signers, signer)
  84. addrs = append(addrs, ethcrypto.PubkeyToAddress(signer.PublicKey(context.Background())))
  85. }
  86. return
  87. }
  88. func addrsToHexStrings(addrs []common.Address) (out []string) {
  89. for _, addr := range addrs {
  90. out = append(out, addr.String())
  91. }
  92. return
  93. }
  94. func generateMockVAA(gsIndex uint32, signers []guardiansigner.GuardianSigner, t *testing.T) []byte {
  95. t.Helper()
  96. v := &vaa.VAA{
  97. Version: 1,
  98. GuardianSetIndex: gsIndex,
  99. Signatures: nil,
  100. Timestamp: time.Now(),
  101. Nonce: 3,
  102. Sequence: 79,
  103. ConsistencyLevel: 1,
  104. EmitterChain: 1,
  105. EmitterAddress: vaa.Address{},
  106. Payload: []byte("test"),
  107. }
  108. for i, signer := range signers {
  109. sig, err := signer.Sign(context.Background(), v.SigningDigest().Bytes())
  110. if err != nil {
  111. require.NoError(t, err)
  112. }
  113. signature := [ecdsaSignatureLength]byte{}
  114. copy(signature[:], sig)
  115. v.Signatures = append(v.Signatures, &vaa.Signature{
  116. Index: uint8(i), // #nosec G115 -- This conversion is safe based on the constants used
  117. Signature: signature,
  118. })
  119. }
  120. vBytes, err := v.Marshal()
  121. if err != nil {
  122. panic(err)
  123. }
  124. return vBytes
  125. }
  126. func setupAdminServerForVAASigning(gsIndex uint32, gsAddrs []common.Address) *nodePrivilegedService {
  127. guardianSigner, err := guardiansigner.GenerateSignerWithPrivatekeyUnsafe(nil)
  128. if err != nil {
  129. panic(err)
  130. }
  131. connector := mockEVMConnector{
  132. guardianAddrs: gsAddrs,
  133. guardianSetIndex: gsIndex,
  134. }
  135. return &nodePrivilegedService{
  136. db: nil,
  137. injectC: nil,
  138. obsvReqSendC: nil,
  139. logger: zap.L(),
  140. signedInC: nil,
  141. governor: nil,
  142. evmConnector: connector,
  143. guardianSigner: guardianSigner,
  144. guardianAddress: ethcrypto.PubkeyToAddress(guardianSigner.PublicKey(context.Background())),
  145. }
  146. }
  147. func TestSignExistingVAA_NoVAA(t *testing.T) {
  148. s := setupAdminServerForVAASigning(0, []common.Address{})
  149. _, err := s.SignExistingVAA(context.Background(), &nodev1.SignExistingVAARequest{
  150. Vaa: nil,
  151. NewGuardianAddrs: nil,
  152. NewGuardianSetIndex: 0,
  153. })
  154. require.ErrorContains(t, err, "failed to unmarshal VAA")
  155. }
  156. func TestSignExistingVAA_NotGuardian(t *testing.T) {
  157. signers, gsAddrs := generateGuardianSigners(5)
  158. s := setupAdminServerForVAASigning(0, gsAddrs)
  159. v := generateMockVAA(0, signers, t)
  160. _, err := s.SignExistingVAA(context.Background(), &nodev1.SignExistingVAARequest{
  161. Vaa: v,
  162. NewGuardianAddrs: addrsToHexStrings(gsAddrs),
  163. NewGuardianSetIndex: 1,
  164. })
  165. require.ErrorContains(t, err, "local guardian is not a member of the new guardian set")
  166. }
  167. func TestSignExistingVAA_InvalidVAA(t *testing.T) {
  168. signers, gsAddrs := generateGuardianSigners(5)
  169. s := setupAdminServerForVAASigning(0, gsAddrs)
  170. v := generateMockVAA(0, signers[:2], t)
  171. gsAddrs = append(gsAddrs, s.guardianAddress)
  172. _, err := s.SignExistingVAA(context.Background(), &nodev1.SignExistingVAARequest{
  173. Vaa: v,
  174. NewGuardianAddrs: addrsToHexStrings(gsAddrs),
  175. NewGuardianSetIndex: 1,
  176. })
  177. require.ErrorContains(t, err, "failed to verify existing VAA")
  178. }
  179. func TestSignExistingVAA_DuplicateGuardian(t *testing.T) {
  180. signers, gsAddrs := generateGuardianSigners(5)
  181. s := setupAdminServerForVAASigning(0, gsAddrs)
  182. v := generateMockVAA(0, signers, t)
  183. gsAddrs = append(gsAddrs, s.guardianAddress)
  184. gsAddrs = append(gsAddrs, s.guardianAddress)
  185. _, err := s.SignExistingVAA(context.Background(), &nodev1.SignExistingVAARequest{
  186. Vaa: v,
  187. NewGuardianAddrs: addrsToHexStrings(gsAddrs),
  188. NewGuardianSetIndex: 1,
  189. })
  190. require.ErrorContains(t, err, "duplicate guardians in the guardian set")
  191. }
  192. func TestSignExistingVAA_AlreadyGuardian(t *testing.T) {
  193. signers, gsAddrs := generateGuardianSigners(5)
  194. s := setupAdminServerForVAASigning(0, gsAddrs)
  195. s.evmConnector = mockEVMConnector{
  196. guardianAddrs: append(gsAddrs, s.guardianAddress),
  197. guardianSetIndex: 0,
  198. }
  199. v := generateMockVAA(0, append(signers, s.guardianSigner), t)
  200. gsAddrs = append(gsAddrs, s.guardianAddress)
  201. _, err := s.SignExistingVAA(context.Background(), &nodev1.SignExistingVAARequest{
  202. Vaa: v,
  203. NewGuardianAddrs: addrsToHexStrings(gsAddrs),
  204. NewGuardianSetIndex: 1,
  205. })
  206. require.ErrorContains(t, err, "local guardian is already on the old set")
  207. }
  208. func TestSignExistingVAA_NotAFutureGuardian(t *testing.T) {
  209. signers, gsAddrs := generateGuardianSigners(5)
  210. s := setupAdminServerForVAASigning(0, gsAddrs)
  211. v := generateMockVAA(0, signers, t)
  212. _, err := s.SignExistingVAA(context.Background(), &nodev1.SignExistingVAARequest{
  213. Vaa: v,
  214. NewGuardianAddrs: addrsToHexStrings(gsAddrs),
  215. NewGuardianSetIndex: 1,
  216. })
  217. require.ErrorContains(t, err, "local guardian is not a member of the new guardian set")
  218. }
  219. func TestSignExistingVAA_CantReachQuorum(t *testing.T) {
  220. signers, gsAddrs := generateGuardianSigners(5)
  221. s := setupAdminServerForVAASigning(0, gsAddrs)
  222. v := generateMockVAA(0, signers, t)
  223. gsAddrs = append(gsAddrs, s.guardianAddress)
  224. _, err := s.SignExistingVAA(context.Background(), &nodev1.SignExistingVAARequest{
  225. Vaa: v,
  226. NewGuardianAddrs: addrsToHexStrings(append(gsAddrs, common.Address{0, 1}, common.Address{3, 1}, common.Address{8, 1})),
  227. NewGuardianSetIndex: 1,
  228. })
  229. require.ErrorContains(t, err, "cannot reach quorum on new guardian set with the local signature")
  230. }
  231. func TestSignExistingVAA_Valid(t *testing.T) {
  232. signers, gsAddrs := generateGuardianSigners(5)
  233. s := setupAdminServerForVAASigning(0, gsAddrs)
  234. v := generateMockVAA(0, signers, t)
  235. gsAddrs = append(gsAddrs, s.guardianAddress)
  236. res, err := s.SignExistingVAA(context.Background(), &nodev1.SignExistingVAARequest{
  237. Vaa: v,
  238. NewGuardianAddrs: addrsToHexStrings(gsAddrs),
  239. NewGuardianSetIndex: 1,
  240. })
  241. require.NoError(t, err)
  242. v2 := generateMockVAA(1, append(signers, s.guardianSigner), t)
  243. require.Equal(t, v2, res.Vaa)
  244. }
  245. const govGuardianSetIndex = uint32(4)
  246. var govTimestamp = time.Now()
  247. const govEmitterChain = vaa.ChainIDSolana
  248. var govEmitterAddr vaa.Address = [32]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4}
  249. // verifyGovernanceVAA verifies the VAA fields of a generated governance VAA. Note that it doesn't verify the payload because that is
  250. // already verified in `sdk/vaa/payload_test` and we don't want to duplicate all those arrays.
  251. func verifyGovernanceVAA(t *testing.T, v *vaa.VAA, expectedSeqNo uint64, expectedNonce uint32) {
  252. t.Helper()
  253. require.NotNil(t, v)
  254. assert.Equal(t, uint8(vaa.SupportedVAAVersion), v.Version)
  255. assert.Equal(t, govGuardianSetIndex, v.GuardianSetIndex)
  256. assert.Nil(t, v.Signatures)
  257. assert.Equal(t, govTimestamp, v.Timestamp)
  258. assert.Equal(t, expectedNonce, v.Nonce)
  259. assert.Equal(t, expectedSeqNo, v.Sequence)
  260. assert.Equal(t, uint8(32), v.ConsistencyLevel)
  261. assert.Equal(t, govEmitterChain, v.EmitterChain)
  262. assert.True(t, bytes.Equal(govEmitterAddr[:], v.EmitterAddress[:]))
  263. }
  264. // Test_adminCommands executes all of the tests in prototext_test.go, unmarshaling the prototext and feeding it into `GovMsgToVaa`.
  265. func Test_adminCommands(t *testing.T) {
  266. for _, tst := range adminCommandTest {
  267. t.Run(tst.label, func(t *testing.T) {
  268. var msg nodev1.InjectGovernanceVAARequest
  269. err := prototext.Unmarshal([]byte(tst.prototext), &msg)
  270. require.NoError(t, err)
  271. require.Equal(t, 1, len(msg.Messages))
  272. govMsg := msg.Messages[0]
  273. govVAA, err := GovMsgToVaa(govMsg, govGuardianSetIndex, govTimestamp)
  274. if tst.errText == "" {
  275. require.NoError(t, err)
  276. verifyGovernanceVAA(t, govVAA, govMsg.Sequence, govMsg.Nonce)
  277. } else {
  278. require.ErrorContains(t, err, tst.errText)
  279. }
  280. })
  281. }
  282. }
  283. func newNodePrivilegedServiceForGovernorTests() *nodePrivilegedService {
  284. gov := governor.NewChainGovernor(zap.NewNop(), &db.MockGovernorDB{}, wh_common.GoTest, false, "")
  285. return &nodePrivilegedService{
  286. db: nil,
  287. injectC: nil,
  288. obsvReqSendC: nil,
  289. logger: nil,
  290. signedInC: nil,
  291. governor: gov,
  292. evmConnector: nil,
  293. guardianSigner: nil,
  294. guardianAddress: common.Address{},
  295. }
  296. }
  297. func TestChainGovernorResetReleaseTimer(t *testing.T) {
  298. service := newNodePrivilegedServiceForGovernorTests()
  299. // governor has no VAAs enqueued, so if we receive this error we know the input validation passed
  300. success := `vaa not found in the pending list`
  301. boundsCheckFailure := `the specified number of days falls outside the range of 1 to 7`
  302. vaaIdLengthFailure := `the VAA id must be specified as "chainId/emitterAddress/seqNum"`
  303. tests := map[string]struct {
  304. vaaId string
  305. numDays uint32
  306. expectedResult string
  307. }{
  308. "EmptyVaaId": {
  309. vaaId: "",
  310. numDays: 1,
  311. expectedResult: vaaIdLengthFailure,
  312. },
  313. "NumDaysEqualsLowerBoundary": {
  314. vaaId: "valid",
  315. numDays: 1,
  316. expectedResult: success,
  317. },
  318. "NumDaysLowerThanLowerBoundary": {
  319. vaaId: "valid",
  320. numDays: 0,
  321. expectedResult: boundsCheckFailure,
  322. },
  323. "NumDaysEqualsUpperBoundary": {
  324. vaaId: "valid",
  325. numDays: maxResetReleaseTimerDays,
  326. expectedResult: success,
  327. },
  328. "NumDaysExceedsUpperBoundary": {
  329. vaaId: "valid",
  330. numDays: maxResetReleaseTimerDays + 1,
  331. expectedResult: boundsCheckFailure,
  332. },
  333. "EmptyVaaIdAndNumDaysExceedsUpperBoundary": {
  334. vaaId: "",
  335. numDays: maxResetReleaseTimerDays + 1,
  336. expectedResult: vaaIdLengthFailure,
  337. },
  338. "NumDaysSignificantlyExceedsUpperBoundary": {
  339. vaaId: "valid",
  340. numDays: maxResetReleaseTimerDays + 1000,
  341. expectedResult: boundsCheckFailure,
  342. },
  343. }
  344. ctx := context.Background()
  345. for name, test := range tests {
  346. t.Run(name, func(t *testing.T) {
  347. req := nodev1.ChainGovernorResetReleaseTimerRequest{
  348. VaaId: test.vaaId,
  349. NumDays: test.numDays,
  350. }
  351. _, err := service.ChainGovernorResetReleaseTimer(ctx, &req)
  352. assert.EqualError(t, err, test.expectedResult)
  353. })
  354. }
  355. }