vaa_verify.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. #!/usr/bin/python3
  2. """
  3. ================================================================================================
  4. The VAA Signature Verify Stateless Program
  5. Copyright 2022 Wormhole Project Contributors
  6. Licensed under the Apache License, Version 2.0 (the "License");
  7. you may not use this file except in compliance with the License.
  8. You may obtain a copy of the License at
  9. http://www.apache.org/licenses/LICENSE-2.0
  10. Unless required by applicable law or agreed to in writing, software
  11. distributed under the License is distributed on an "AS IS" BASIS,
  12. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. See the License for the specific language governing permissions and
  14. limitations under the License.
  15. ------------------------------------------------------------------------------------------------
  16. This program verifies a subset of the signatures in a VAA against the guardian set. This
  17. program works in tandem with the VAA Processor stateful program.
  18. The difference between this version and the Randlabs version is I removed most of the asserts
  19. since we are going to have to completely validate the arguments again in the
  20. TokenBridge contract.
  21. We also cannot retroactively see/verify what arguments were passed into this
  22. function unless all the arguments are in the Txn.application_args so
  23. everything has to get moved out of the lsig args and into the txn_args
  24. ================================================================================================
  25. """
  26. from pyteal.ast import *
  27. from pyteal.types import *
  28. from pyteal.compiler import *
  29. from pyteal.ir import *
  30. from globals import *
  31. from inlineasm import *
  32. import sys
  33. SLOTID_RECOVERED_PK_X = 240
  34. SLOTID_RECOVERED_PK_Y = 241
  35. @Subroutine(TealType.uint64)
  36. def sig_check(signatures, dhash, keys):
  37. """
  38. Verifies some signatures of a VAA. Due to computation budget limitations,
  39. this can't verify all signatures in one go. Instead, it just makes sure that
  40. whatever signatures it's given correspond to the given keys.
  41. In addition, none of the arguments are validated here beyond the fact that
  42. the signatures are valid given the keys and the message hash. In particular,
  43. the message hash is also not validated here. Thus, the proper way to use
  44. this function is by calling it (by the client) before the token bridge
  45. program. Then the token bridge program verify each input + that the right
  46. program was called. If it failed to verify any of these, then signature
  47. verification could be bypaseed.
  48. """
  49. si = ScratchVar(TealType.uint64) # signature index (zero-based)
  50. ki = ScratchVar(TealType.uint64) # key index
  51. slen = ScratchVar(TealType.uint64) # signature length
  52. rec_pk_x = ScratchVar(TealType.bytes, SLOTID_RECOVERED_PK_X)
  53. rec_pk_y = ScratchVar(TealType.bytes, SLOTID_RECOVERED_PK_Y)
  54. return Seq(
  55. [
  56. rec_pk_x.store(Bytes("")),
  57. rec_pk_y.store(Bytes("")),
  58. slen.store(Len(signatures)),
  59. For(Seq([
  60. si.store(Int(0)),
  61. ki.store(Int(0))
  62. ]),
  63. si.load() < slen.load(),
  64. Seq([
  65. si.store(si.load() + Int(66)),
  66. ki.store(ki.load() + Int(20))
  67. ])).Do(
  68. Seq([
  69. InlineAssembly(
  70. "ecdsa_pk_recover Secp256k1",
  71. dhash,
  72. Btoi(Extract(signatures, si.load() + Int(65), Int(1))),
  73. Extract(signatures, si.load() + Int(1), Int(32)), # R
  74. Extract(signatures, si.load() + Int(33), Int(32)), # S
  75. type=TealType.none),
  76. # returned values in stack, pass to scratch-vars
  77. InlineAssembly("store " + str(SLOTID_RECOVERED_PK_Y)),
  78. InlineAssembly("store " + str(SLOTID_RECOVERED_PK_X)),
  79. # Generate Ethereum-type public key, compare with guardian key.
  80. Assert(Extract(keys, ki.load(), Int(20)) == Substring(Keccak256(Concat(rec_pk_x.load(), rec_pk_y.load())), Int(12), Int(32)))
  81. ])
  82. ),
  83. Return(Int(1))
  84. ]
  85. )
  86. def vaa_verify_program():
  87. signatures = Txn.application_args[1]
  88. keys = Txn.application_args[2]
  89. dhash = Txn.application_args[3]
  90. return Seq([
  91. Assert(Txn.rekey_to() == Global.zero_address()),
  92. Assert(Txn.fee() == Int(0)),
  93. Assert(Txn.type_enum() == TxnType.ApplicationCall),
  94. Assert(sig_check(signatures, dhash, keys)),
  95. Approve()]
  96. )
  97. def get_vaa_verify(file_name = "teal/vaa_verify.teal"):
  98. teal = compileTeal(vaa_verify_program(), mode=Mode.Signature, version=6)
  99. with open(file_name, "w") as f:
  100. f.write(teal)
  101. return teal
  102. if __name__ == '__main__':
  103. if len(sys.argv) == 2:
  104. get_vaa_verify(sys.argv[1])
  105. else:
  106. get_vaa_verify()