main.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. package main
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "os"
  7. "strconv"
  8. abi "github.com/certusone/wormhole/node/pkg/watchers/evm/connectors/ethabi"
  9. "github.com/ethereum/go-ethereum/accounts/abi/bind"
  10. "github.com/ethereum/go-ethereum/common"
  11. "github.com/ethereum/go-ethereum/core/types"
  12. "github.com/ethereum/go-ethereum/crypto"
  13. "github.com/ethereum/go-ethereum/ethclient"
  14. "github.com/spf13/cobra"
  15. "github.com/wormhole-foundation/wormhole/sdk/vaa"
  16. )
  17. // rootCmd represents the base command when called without any subcommands
  18. var rootCmd = &cobra.Command{
  19. Use: "eth",
  20. Short: "Wormhole Ethereum Client",
  21. }
  22. var governanceVAACommand = &cobra.Command{
  23. Use: "execute_governance [VAA]",
  24. Short: "Execute a governance VAA",
  25. Run: executeGovernance,
  26. Args: cobra.ExactArgs(1),
  27. }
  28. var postMessageCommand = &cobra.Command{
  29. Use: "post_message [NONCE] [NUM_CONFIRMATIONS] [MESSAGE]",
  30. Short: "Post a message to wormhole",
  31. Run: postMessage,
  32. Args: cobra.ExactArgs(3),
  33. }
  34. var (
  35. contractAddress string
  36. rpcUrl string
  37. key string
  38. )
  39. func init() {
  40. rootCmd.PersistentFlags().StringVar(&contractAddress, "contract", "", "Address of the Wormhole contract")
  41. rootCmd.PersistentFlags().StringVar(&rpcUrl, "rpc", "", "Ethereum RPC address")
  42. rootCmd.PersistentFlags().StringVar(&key, "key", "", "Key to sign the transaction with (hex-encoded)")
  43. rootCmd.AddCommand(governanceVAACommand)
  44. rootCmd.AddCommand(postMessageCommand)
  45. }
  46. func postMessage(cmd *cobra.Command, args []string) {
  47. nonce, err := strconv.ParseUint(args[0], 10, 32)
  48. if err != nil {
  49. cmd.PrintErrln("Could not parse nonce", err)
  50. os.Exit(1)
  51. }
  52. consistencyLevel, err := strconv.ParseUint(args[1], 10, 8)
  53. if err != nil {
  54. cmd.PrintErrln("Could not parse confirmation number", err)
  55. os.Exit(1)
  56. }
  57. message := common.Hex2Bytes(args[2])
  58. ethC, err := getEthClient()
  59. if err != nil {
  60. cmd.PrintErrln(err)
  61. os.Exit(1)
  62. }
  63. signer, addr, err := getSigner(ethC)
  64. if err != nil {
  65. cmd.PrintErrln(err)
  66. os.Exit(1)
  67. }
  68. t, err := getTransactor(ethC)
  69. if err != nil {
  70. cmd.PrintErrln(err)
  71. os.Exit(1)
  72. }
  73. res, err := t.PublishMessage(&bind.TransactOpts{
  74. From: addr,
  75. Signer: signer,
  76. }, uint32(nonce), message, uint8(consistencyLevel))
  77. if err != nil {
  78. cmd.PrintErrln(err)
  79. os.Exit(1)
  80. }
  81. println("Posted tx. Hash:", res.Hash().String())
  82. }
  83. func executeGovernance(cmd *cobra.Command, args []string) {
  84. ethC, err := getEthClient()
  85. if err != nil {
  86. cmd.PrintErrln(err)
  87. os.Exit(1)
  88. }
  89. signer, addr, err := getSigner(ethC)
  90. if err != nil {
  91. cmd.PrintErrln(err)
  92. os.Exit(1)
  93. }
  94. t, err := getTransactor(ethC)
  95. if err != nil {
  96. cmd.PrintErrln(err)
  97. os.Exit(1)
  98. }
  99. vaaData := common.Hex2Bytes(args[0])
  100. parsedVaa, err := vaa.Unmarshal(vaaData)
  101. if err != nil {
  102. cmd.PrintErrln("Failed to parse VAA:", err)
  103. os.Exit(1)
  104. }
  105. governanceAction, err := getGovernanceVaaAction(parsedVaa.Payload)
  106. if err != nil {
  107. cmd.PrintErrln("Failed to parse governance payload:", err)
  108. os.Exit(1)
  109. }
  110. var contractFunction func(opts *bind.TransactOpts, _vm []byte) (*types.Transaction, error)
  111. switch governanceAction {
  112. case 1:
  113. println("Governance Action: ContractUpgrade")
  114. contractFunction = t.SubmitContractUpgrade
  115. case 2:
  116. println("Governance Action: NewGuardianSet")
  117. contractFunction = t.SubmitNewGuardianSet
  118. case 3:
  119. println("Governance Action: SetMessageFee")
  120. contractFunction = t.SubmitSetMessageFee
  121. case 4:
  122. println("Governance Action: TransferFees")
  123. contractFunction = t.SubmitTransferFees
  124. default:
  125. cmd.PrintErrln("Unknow governance action")
  126. os.Exit(1)
  127. }
  128. res, err := contractFunction(&bind.TransactOpts{
  129. From: addr,
  130. Signer: signer,
  131. }, vaaData)
  132. if err != nil {
  133. cmd.PrintErrln(err)
  134. os.Exit(1)
  135. }
  136. println("Posted tx. Hash:", res.Hash().String())
  137. }
  138. func getEthClient() (*ethclient.Client, error) {
  139. return ethclient.Dial(rpcUrl)
  140. }
  141. func getSigner(ethC *ethclient.Client) (func(address common.Address, transaction *types.Transaction) (*types.Transaction, error), common.Address, error) {
  142. cID, err := ethC.ChainID(context.Background())
  143. if err != nil {
  144. panic(err)
  145. }
  146. keyBytes := common.Hex2Bytes(key)
  147. key, err := crypto.ToECDSA(keyBytes)
  148. if err != nil {
  149. return nil, common.Address{}, err
  150. }
  151. return func(address common.Address, transaction *types.Transaction) (*types.Transaction, error) {
  152. return types.SignTx(transaction, types.NewEIP155Signer(cID), key)
  153. }, crypto.PubkeyToAddress(key.PublicKey), nil
  154. }
  155. func getTransactor(ethC *ethclient.Client) (*abi.AbiTransactor, error) {
  156. addr := common.HexToAddress(contractAddress)
  157. emptyAddr := common.Address{}
  158. if addr == emptyAddr {
  159. return nil, errors.New("invalid contract address")
  160. }
  161. t, err := abi.NewAbiTransactor(addr, ethC)
  162. if err != nil {
  163. return nil, err
  164. }
  165. return t, nil
  166. }
  167. func getGovernanceVaaAction(payload []byte) (uint8, error) {
  168. if len(payload) < 32+2+1 {
  169. return 0, errors.New("VAA payload does not contain a governance header")
  170. }
  171. return payload[32], nil
  172. }
  173. func main() {
  174. if err := rootCmd.Execute(); err != nil {
  175. fmt.Println(err)
  176. os.Exit(1)
  177. }
  178. }