diff --git a/bridge/cmd/guardiand/bridge.go b/bridge/cmd/guardiand/bridge.go index aae24e09..387fb212 100644 --- a/bridge/cmd/guardiand/bridge.go +++ b/bridge/cmd/guardiand/bridge.go @@ -3,6 +3,7 @@ package guardiand import ( "context" "fmt" + solana_types "github.com/dfuse-io/solana-go" "net/http" _ "net/http/pprof" "os" @@ -41,7 +42,8 @@ var ( adminSocketPath *string - bridgeKeyPath *string + bridgeKeyPath *string + solanaBridgeAddress *string ethRPC *string ethContract *string @@ -54,6 +56,9 @@ var ( terraContract *string terraKeyPath *string + solanaWsRPC *string + solanaRPC *string + agentRPC *string logLevel *string @@ -73,6 +78,7 @@ func init() { adminSocketPath = BridgeCmd.Flags().String("adminSocket", "", "Admin gRPC service UNIX domain socket path") bridgeKeyPath = BridgeCmd.Flags().String("bridgeKey", "", "Path to guardian key (required)") + solanaBridgeAddress = BridgeCmd.Flags().String("solanaBridgeAddress", "", "Address of the Solana Bridge Program (required)") ethRPC = BridgeCmd.Flags().String("ethRPC", "", "Ethereum RPC URL") ethContract = BridgeCmd.Flags().String("ethContract", "", "Ethereum bridge contract address") @@ -85,6 +91,9 @@ func init() { terraContract = BridgeCmd.Flags().String("terraContract", "", "Wormhole contract address on Terra blockhain") terraKeyPath = BridgeCmd.Flags().String("terraKey", "", "Path to mnemonic for account paying gas for submitting transactions to Terra") + solanaWsRPC = BridgeCmd.Flags().String("solanaWS", "", "Solana Websocket URL (required") + solanaRPC = BridgeCmd.Flags().String("solanaRPC", "", "Solana RPC URL (required") + agentRPC = BridgeCmd.Flags().String("agentRPC", "", "Solana agent sidecar gRPC socket path") logLevel = BridgeCmd.Flags().String("logLevel", "info", "Logging level (debug, info, warn, error, dpanic, panic, fatal)") @@ -233,6 +242,17 @@ func runBridge(cmd *cobra.Command, args []string) { if *nodeName == "" { logger.Fatal("Please specify --nodeName") } + + if *solanaBridgeAddress == "" { + logger.Fatal("Please specify --solanaBridgeAddress") + } + if *solanaWsRPC == "" { + logger.Fatal("Please specify --solanaWsUrl") + } + if *solanaRPC == "" { + logger.Fatal("Please specify --solanaUrl") + } + if *terraSupport { if !*unsafeDevMode { logger.Fatal("cannot enable terra support in production mode") @@ -256,6 +276,10 @@ func runBridge(cmd *cobra.Command, args []string) { } ethContractAddr := eth_common.HexToAddress(*ethContract) + solBridgeAddress, err := solana_types.PublicKeyFromBase58(*solanaBridgeAddress) + if err != nil { + logger.Fatal("invalid Solana bridge address", zap.Error(err)) + } // In devnet mode, we generate a deterministic guardian key and write it to disk. if *unsafeDevMode { @@ -354,8 +378,13 @@ func runBridge(cmd *cobra.Command, args []string) { } } + if err := supervisor.Run(ctx, "solvaa", + solana.NewSolanaVAASubmitter(*agentRPC, lockC, solanaVaaC).Run); err != nil { + return err + } + if err := supervisor.Run(ctx, "solwatch", - solana.NewSolanaBridgeWatcher(*agentRPC, lockC, solanaVaaC).Run); err != nil { + solana.NewSolanaWatcher(*solanaWsRPC, *solanaRPC, solBridgeAddress, lockC).Run); err != nil { return err } diff --git a/bridge/go.mod b/bridge/go.mod index ea1e4e9c..1ff5868b 100644 --- a/bridge/go.mod +++ b/bridge/go.mod @@ -10,6 +10,7 @@ require ( github.com/davecgh/go-spew v1.1.1 github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/deckarep/golang-set v1.7.1 // indirect + github.com/dfuse-io/solana-go v0.2.1-0.20210119190242-57bebed0dae0 github.com/ethereum/go-ethereum v1.9.23 github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect github.com/go-ole/go-ole v1.2.4 // indirect @@ -47,8 +48,8 @@ require ( github.com/peterh/liner v1.2.0 // indirect github.com/prometheus/tsdb v0.10.0 // indirect github.com/shirou/gopsutil v2.20.9+incompatible // indirect - github.com/spf13/cobra v1.0.0 - github.com/spf13/viper v1.6.3 + github.com/spf13/cobra v1.1.1 + github.com/spf13/viper v1.7.1 github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 github.com/stretchr/testify v1.6.1 github.com/tendermint/tendermint v0.33.8 diff --git a/bridge/go.sum b/bridge/go.sum index 4d9d4e41..2b740578 100644 --- a/bridge/go.sum +++ b/bridge/go.sum @@ -3,6 +3,7 @@ cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= @@ -10,8 +11,11 @@ cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +contrib.go.opencensus.io/exporter/stackdriver v0.12.6 h1:Y2FTyj0HgOhfjEW6D6ytZNoz1YcPDXmkKr1I478CWKs= +contrib.go.opencensus.io/exporter/stackdriver v0.12.6/go.mod h1:8x999/OcIPy5ivx/wDiV7Gx4D+VUPODf0mWRGRc5kSk= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= @@ -43,6 +47,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= +github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= @@ -65,6 +71,7 @@ github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9 github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -93,6 +100,7 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.22.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= @@ -106,6 +114,9 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= +github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= @@ -180,6 +191,7 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= github.com/danieljoos/wincred v1.0.2 h1:zf4bhty2iLuwgjgpraD2E9UbvO+fe54XXGJbOwe23fU= github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -195,6 +207,12 @@ github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vs github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/dfuse-io/binary v0.0.0-20210119182726-f245aa830ba8 h1:7/6ofivaY8iF8YWOxBG9ANq8mBgmEKKvmDjwpb9+7F0= +github.com/dfuse-io/binary v0.0.0-20210119182726-f245aa830ba8/go.mod h1:GDFX6qH3BQZPWTeYaA4ZW98T94zs2skRoG3oMz/0jw0= +github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79 h1:+HRtcJejUYA/2rnyTMbOaZ4g7f4aVuFduTV/03dbpLY= +github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79/go.mod h1:V+ED4kT/t/lKtH99JQmKIb0v9WL3VaYkJ36CfHlVECI= +github.com/dfuse-io/solana-go v0.2.1-0.20210119190242-57bebed0dae0 h1:/X37RfIKD1lUy41rzC2lZ1M87swMA/okjqRmwd6S7Do= +github.com/dfuse-io/solana-go v0.2.1-0.20210119190242-57bebed0dae0/go.mod h1:uRYzVm9Kq+2Bx+baLgjqIByOvSpqD/bZgFmqtCPiVQU= github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= @@ -243,6 +261,7 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqL github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc h1:jtW8jbpkO4YirRSyepBOH8E+2HEw6/hKkBvFPwhUN8c= github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= @@ -270,6 +289,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -353,6 +373,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= @@ -388,6 +409,8 @@ github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/ github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/rpc v1.2.0 h1:WvvdC2lNeT1SP32zrIce5l0ECBfbAlmrmSBsuc57wfk= +github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -411,7 +434,9 @@ github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uM github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -553,6 +578,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= @@ -778,6 +804,7 @@ github.com/libp2p/go-yamux v1.4.0 h1:7nqe0T95T2CWh40IdJ/tp8RMor4ubc9/wYZpB2a/Hx0 github.com/libp2p/go-yamux v1.4.0/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lucas-clemente/quic-go v0.18.1 h1:DMR7guC0NtVS8zNZR3IO7NARZvZygkSC56GGtC6cyys= github.com/lucas-clemente/quic-go v0.18.1/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= @@ -843,6 +870,8 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -927,6 +956,7 @@ github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= @@ -1122,6 +1152,8 @@ github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= @@ -1137,6 +1169,9 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs= github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= +github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= @@ -1187,7 +1222,8 @@ github.com/tendermint/tendermint v0.33.8 h1:Xxu4QhpqcomSE0iQDw1MqLgfsa8fqtPtWFJK github.com/tendermint/tendermint v0.33.8/go.mod h1:0yUs9eIuuDq07nQql9BmI30FtYGcEC60Tu5JzB5IezM= github.com/tendermint/tm-db v0.5.1 h1:H9HDq8UEA7Eeg13kdYckkgwwkQLBnJGgX4PgLJRhieY= github.com/tendermint/tm-db v0.5.1/go.mod h1:g92zWjHpCYlEvQXvy9M168Su8V1IBEeawpXVVBaK4f4= -github.com/terra-project/terra.go v1.0.0 h1:TR2b3x8yrljXhrs9a3KORRCQ6BGr+bCTp0ZwTrG/i3c= +github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA= +github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= github.com/terra-project/terra.go v1.0.1-0.20201113170042-b3bffdc6fd06 h1:TAhaL+7VYJe44qBEKqjlj3wD0CRjJN1JZfz8p+L6FGY= github.com/terra-project/terra.go v1.0.1-0.20201113170042-b3bffdc6fd06/go.mod h1:elzj1F6B9Sel3c4QFNeR3yR4E9tu+c1xBP+ZZYPlSq8= github.com/tidwall/gjson v1.6.3 h1:aHoiiem0dr7GHkW001T1SMTJ7X5PvyekH5WX0whWGnI= @@ -1211,6 +1247,8 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= @@ -1233,6 +1271,9 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1: github.com/xtaci/kcp-go v5.4.5+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= +github.com/ybbus/jsonrpc v2.1.2+incompatible h1:V4mkE9qhbDQ92/MLMIhlhMSbz8jNXdagC3xBR5NDwaQ= +github.com/ybbus/jsonrpc v2.1.2+incompatible/go.mod h1:XJrh1eMSzdIYFbM08flv0wp5G35eRniyeGut1z+LSiE= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8= github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -1270,6 +1311,7 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEa go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= @@ -1317,6 +1359,7 @@ golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1339,6 +1382,8 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd h1:ePuNC7PZ6O5BzgPn9bZayERXBdfZjUYoXEf5BTfDfh8= golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1364,6 +1409,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190912160710-24e19bdeb0f2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1371,6 +1417,7 @@ golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -1388,6 +1435,8 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1428,6 +1477,7 @@ golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1492,6 +1542,7 @@ golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1501,6 +1552,8 @@ golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69 h1:yBHHx+XZqXJBm6Exke3N7V9 golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd h1:hHkvGJK23seRCflePJnVa9IMv8fsuavSCWKd11kDQFs= golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200601175630-2caf76543d99 h1:deddXmhOJb/bvD/4M/j2AUMrhHeh6GkqykJSCWyTNVk= +golang.org/x/tools v0.0.0-20200601175630-2caf76543d99/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= @@ -1515,6 +1568,7 @@ google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEt google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1535,9 +1589,11 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200218151345-dad8c97a84f5/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= @@ -1635,7 +1691,6 @@ k8s.io/api v0.19.4 h1:I+1I4cgJYuCDgiLNjKx7SLmIbwgj9w7N7Zr5vSIdwpo= k8s.io/api v0.19.4/go.mod h1:SbtJ2aHCItirzdJ36YslycFNzWADYH3tgOhvBEFtZAk= k8s.io/apimachinery v0.19.4 h1:+ZoddM7nbzrDCp0T3SWnyxqf8cbWPT2fkZImoyvHUG0= k8s.io/apimachinery v0.19.4/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= -k8s.io/apimachinery v0.20.1 h1:LAhz8pKbgR8tUwn7boK+b2HZdt7MiTu2mkYtFMUjTRQ= k8s.io/client-go v0.19.4 h1:85D3mDNoLF+xqpyE9Dh/OtrJDyJrSRKkHmDXIbEzer8= k8s.io/client-go v0.19.4/go.mod h1:ZrEy7+wj9PjH5VMBCuu/BDlvtUAku0oVFk4MmnW9mWA= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= diff --git a/bridge/pkg/solana/client.go b/bridge/pkg/solana/client.go new file mode 100644 index 00000000..66543520 --- /dev/null +++ b/bridge/pkg/solana/client.go @@ -0,0 +1,214 @@ +package ethereum + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "github.com/certusone/wormhole/bridge/pkg/common" + "github.com/certusone/wormhole/bridge/pkg/supervisor" + "github.com/certusone/wormhole/bridge/pkg/vaa" + "github.com/dfuse-io/solana-go" + "github.com/dfuse-io/solana-go/rpc" + eth_common "github.com/ethereum/go-ethereum/common" + "go.uber.org/zap" + "math/big" + "time" +) + +type SolanaWatcher struct { + bridge solana.PublicKey + wsUrl string + rpcUrl string + lockEvent chan *common.ChainLock +} + +func NewSolanaWatcher(wsUrl, rpcUrl string, bridgeAddress solana.PublicKey, lockEvents chan *common.ChainLock) *SolanaWatcher { + return &SolanaWatcher{bridge: bridgeAddress, wsUrl: wsUrl, rpcUrl: rpcUrl, lockEvent: lockEvents} +} + +func (s *SolanaWatcher) Run(ctx context.Context) error { + rpcClient := rpc.NewClient(s.rpcUrl) + logger := supervisor.Logger(ctx) + errC := make(chan error) + + go func() { + timer := time.NewTicker(time.Second * 5) + defer timer.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-timer.C: + func() { + rCtx, cancel := context.WithTimeout(ctx, time.Second*5) + defer cancel() + + start := time.Now() + + // Find TransferOutProposal accounts without a VAA + accounts, err := rpcClient.GetProgramAccounts(rCtx, s.bridge, &rpc.GetProgramAccountsOpts{ + Commitment: rpc.CommitmentMax, + Filters: []rpc.RPCFilter{ + { + DataSize: 1184, // Search for TransferOutProposal accounts + }, + { + Memcmp: &rpc.RPCFilterMemcmp{ + Offset: 1140, // Offset of VaaTime + Bytes: solana.Base58{0, 0, 0, 0}, // VAA time is 0 when no VAA is present + }, + }, + }, + }) + if err != nil { + errC <- err + return + } + + logger.Debug("fetched transfer proposals without VAA", + zap.Int("n", len(accounts)), + zap.Duration("took", time.Since(start)), + ) + + for _, acc := range accounts { + proposal, err := ParseTransferOutProposal(acc.Account.Data) + if err != nil { + logger.Warn( + "failed to parse transfer proposal", + zap.Stringer("account", acc.Pubkey), + zap.Error(err), + ) + continue + } + + // VAA submitted + if proposal.VaaTime.Unix() != 0 { + continue + } + + var txHash eth_common.Hash + copy(txHash[:], acc.Pubkey[:]) + + lock := &common.ChainLock{ + TxHash: txHash, + Timestamp: proposal.LockupTime, + Nonce: proposal.Nonce, + SourceAddress: proposal.SourceAddress, + TargetAddress: proposal.ForeignAddress, + SourceChain: vaa.ChainIDSolana, + TargetChain: proposal.ToChainID, + TokenChain: proposal.Asset.Chain, + TokenAddress: proposal.Asset.Address, + TokenDecimals: proposal.Asset.Decimals, + Amount: proposal.Amount, + } + logger.Info("found lockup without VAA", zap.Stringer("lockup_address", acc.Pubkey)) + s.lockEvent <- lock + } + }() + } + } + }() + + select { + case <-ctx.Done(): + return ctx.Err() + case err := <-errC: + return err + } +} + +type ( + TransferOutProposal struct { + Amount *big.Int + ToChainID vaa.ChainID + SourceAddress vaa.Address + ForeignAddress vaa.Address + Asset vaa.AssetMeta + Nonce uint32 + VAA [1001]byte + VaaTime time.Time + LockupTime time.Time + PokeCounter uint8 + SignatureAccount solana.PublicKey + } +) + +func ParseTransferOutProposal(data []byte) (*TransferOutProposal, error) { + prop := &TransferOutProposal{} + r := bytes.NewBuffer(data) + + var amountBytes [32]byte + if n, err := r.Read(amountBytes[:]); err != nil || n != 32 { + return nil, fmt.Errorf("failed to read amount: %w", err) + } + // Reverse (little endian -> big endian) + for i := 0; i < len(amountBytes)/2; i++ { + amountBytes[i], amountBytes[len(amountBytes)-i-1] = amountBytes[len(amountBytes)-i-1], amountBytes[i] + } + prop.Amount = new(big.Int).SetBytes(amountBytes[:]) + + if err := binary.Read(r, binary.LittleEndian, &prop.ToChainID); err != nil { + return nil, fmt.Errorf("failed to read to chain id: %w", err) + } + + if n, err := r.Read(prop.SourceAddress[:]); err != nil || n != 32 { + return nil, fmt.Errorf("failed to read source address: %w", err) + } + + if n, err := r.Read(prop.ForeignAddress[:]); err != nil || n != 32 { + return nil, fmt.Errorf("failed to read source address: %w", err) + } + + assetMeta := vaa.AssetMeta{} + if n, err := r.Read(assetMeta.Address[:]); err != nil || n != 32 { + return nil, fmt.Errorf("failed to read asset meta address: %w", err) + } + + if err := binary.Read(r, binary.LittleEndian, &assetMeta.Chain); err != nil { + return nil, fmt.Errorf("failed to read asset meta chain: %w", err) + } + + if err := binary.Read(r, binary.LittleEndian, &assetMeta.Decimals); err != nil { + return nil, fmt.Errorf("failed to read asset meta decimals: %w", err) + } + prop.Asset = assetMeta + + // Skip alignment byte + r.Next(1) + + if err := binary.Read(r, binary.LittleEndian, &prop.Nonce); err != nil { + return nil, fmt.Errorf("failed to read nonce: %w", err) + } + + if n, err := r.Read(prop.VAA[:]); err != nil || n != 1001 { + return nil, fmt.Errorf("failed to read vaa: %w", err) + } + + // Skip alignment bytes + r.Next(3) + + var vaaTime uint32 + if err := binary.Read(r, binary.LittleEndian, &vaaTime); err != nil { + return nil, fmt.Errorf("failed to read vaa time: %w", err) + } + prop.VaaTime = time.Unix(int64(vaaTime), 0) + + var lockupTime uint32 + if err := binary.Read(r, binary.LittleEndian, &lockupTime); err != nil { + return nil, fmt.Errorf("failed to read lockup time: %w", err) + } + prop.LockupTime = time.Unix(int64(lockupTime), 0) + + if err := binary.Read(r, binary.LittleEndian, &prop.PokeCounter); err != nil { + return nil, fmt.Errorf("failed to read poke counter: %w", err) + } + + if n, err := r.Read(prop.SignatureAccount[:]); err != nil || n != 32 { + return nil, fmt.Errorf("failed to read signature account: %w", err) + } + + return prop, nil +} diff --git a/bridge/pkg/solana/client_test.go b/bridge/pkg/solana/client_test.go new file mode 100644 index 00000000..f34cd0c1 --- /dev/null +++ b/bridge/pkg/solana/client_test.go @@ -0,0 +1,20 @@ +package ethereum + +import ( + "encoding/hex" + "encoding/json" + "github.com/stretchr/testify/require" + "testing" +) + +func TestParseTransferOutProposal(t *testing.T) { + data, err := hex.DecodeString("809698000000000000000000000000000000000000000000000000000000000002bd84f96dc4955d6c7f876de115738476ddd343fe1019d139534addc907018cfb0000000000000000000000008d689476eb446a1fb0065bffac32398ed7f89165000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48020600263a000001000000000060075fe01000003a260102bd84f96dc4955d6c7f876de115738476ddd343fe1019d139534addc907018cfb0000000000000000000000008d689476eb446a1fb0065bffac32398ed7f8916502000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48060000000000000000000000000000000000000000000000000000000000989680ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e05f0760e05f076003a4f2e022fec85b8bcbfec192d71a5d38e482f1328da6bf4d14a92f7755fc2ecc010000") + require.NoError(t, err) + + proposal, err := ParseTransferOutProposal(data) + require.NoError(t, err) + + s, err := json.Marshal(proposal) + require.NoError(t, err) + require.Equal(t, "{\"Amount\":10000000,\"ToChainID\":2,\"SourceAddress\":[189,132,249,109,196,149,93,108,127,135,109,225,21,115,132,118,221,211,67,254,16,25,209,57,83,74,221,201,7,1,140,251],\"ForeignAddress\":[0,0,0,0,0,0,0,0,0,0,0,0,141,104,148,118,235,68,106,31,176,6,91,255,172,50,57,142,215,248,145,101],\"Asset\":{\"Chain\":2,\"Address\":[0,0,0,0,0,0,0,0,0,0,0,0,160,184,105,145,198,33,139,54,193,209,157,74,46,158,176,206,54,6,235,72],\"Decimals\":6},\"Nonce\":14886,\"VAA\":[1,0,0,0,0,0,96,7,95,224,16,0,0,58,38,1,2,189,132,249,109,196,149,93,108,127,135,109,225,21,115,132,118,221,211,67,254,16,25,209,57,83,74,221,201,7,1,140,251,0,0,0,0,0,0,0,0,0,0,0,0,141,104,148,118,235,68,106,31,176,6,91,255,172,50,57,142,215,248,145,101,2,0,0,0,0,0,0,0,0,0,0,0,0,160,184,105,145,198,33,139,54,193,209,157,74,46,158,176,206,54,6,235,72,6,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,152,150,128,255,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"VaaTime\":\"2021-01-19T23:40:32+01:00\",\"LockupTime\":\"2021-01-19T23:40:32+01:00\",\"PokeCounter\":3,\"SignatureAccount\":\"C6tfScZr4ntvH4HUGGpk23TQxk73jLW1MeoduSgUEpDZ\"}", string(s)) +} diff --git a/bridge/pkg/solana/watcher.go b/bridge/pkg/solana/watcher.go index 5cf749e4..603a6d6d 100644 --- a/bridge/pkg/solana/watcher.go +++ b/bridge/pkg/solana/watcher.go @@ -4,11 +4,9 @@ import ( "context" "encoding/hex" "fmt" - "math/big" "strings" "time" - eth_common "github.com/ethereum/go-ethereum/common" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -24,7 +22,7 @@ import ( ) type ( - SolanaBridgeWatcher struct { + SolanaVAASubmitter struct { url string lockChan chan *common.ChainLock @@ -32,11 +30,11 @@ type ( } ) -func NewSolanaBridgeWatcher(url string, lockEvents chan *common.ChainLock, vaaQueue chan *vaa.VAA) *SolanaBridgeWatcher { - return &SolanaBridgeWatcher{url: url, lockChan: lockEvents, vaaChan: vaaQueue} +func NewSolanaVAASubmitter(url string, lockEvents chan *common.ChainLock, vaaQueue chan *vaa.VAA) *SolanaVAASubmitter { + return &SolanaVAASubmitter{url: url, lockChan: lockEvents, vaaChan: vaaQueue} } -func (e *SolanaBridgeWatcher) Run(ctx context.Context) error { +func (e *SolanaVAASubmitter) Run(ctx context.Context) error { // We only support UNIX sockets since we rely on Unix filesystem permissions for access control. path := fmt.Sprintf("unix://%s", e.url) @@ -53,12 +51,6 @@ func (e *SolanaBridgeWatcher) Run(ctx context.Context) error { errC := make(chan error) logger := supervisor.Logger(ctx) - // Subscribe to new token lockups - tokensLockedSub, err := c.WatchLockups(ctx, &agentv1.WatchLockupsRequest{}) - if err != nil { - return fmt.Errorf("failed to subscribe to token lockup events: %w", err) - } - // Check whether agent is up by doing a GetBalance call. This is a bit hacky, but otherwise, a broken agent won't // fail until Recv(). Readiness is best-effort and if this succeeds, it's fair to assume that the watch does too. balance, err := c.GetBalance(timeout, &agentv1.GetBalanceRequest{}) @@ -68,41 +60,6 @@ func (e *SolanaBridgeWatcher) Run(ctx context.Context) error { readiness.SetReady(common.ReadinessSolanaSyncing) logger.Info("account balance", zap.Uint64("lamports", balance.Balance)) - go func() { - logger.Info("watching for on-chain events") - - for { - ev, err := tokensLockedSub.Recv() - if err != nil { - errC <- fmt.Errorf("failed to receive message from agent: %w", err) - return - } - - switch event := ev.Event.(type) { - case *agentv1.LockupEvent_New: - logger.Debug("received lockup event", - zap.Any("event", ev)) - - lock := &common.ChainLock{ - TxHash: eth_common.HexToHash(ev.LockupAddress), - Timestamp: time.Unix(int64(ev.Time), 0), - Nonce: event.New.Nonce, - SourceChain: vaa.ChainIDSolana, - TargetChain: vaa.ChainID(event.New.TargetChain), - TokenChain: vaa.ChainID(event.New.TokenChain), - TokenDecimals: uint8(event.New.TokenDecimals), - Amount: new(big.Int).SetBytes(event.New.Amount), - } - copy(lock.TokenAddress[:], event.New.TokenAddress) - copy(lock.SourceAddress[:], event.New.SourceAddress) - copy(lock.TargetAddress[:], event.New.TargetAddress) - - e.lockChan <- lock - logger.Info("found new lockup transaction", zap.String("lockup_address", ev.LockupAddress)) - } - } - }() - go func() { for { select { diff --git a/proto/agent/v1/service.proto b/proto/agent/v1/service.proto index 057f055d..3e2e46f4 100644 --- a/proto/agent/v1/service.proto +++ b/proto/agent/v1/service.proto @@ -8,7 +8,6 @@ option go_package = "proto/agent/v1;agentv1"; service Agent { rpc SubmitVAA (SubmitVAARequest) returns (SubmitVAAResponse); - rpc WatchLockups (WatchLockupsRequest) returns (stream LockupEvent); rpc GetBalance (GetBalanceRequest) returns (GetBalanceResponse); } @@ -23,50 +22,6 @@ message SubmitVAAResponse { string signature = 1; } -message WatchLockupsRequest { - -} - -message LockupEvent { - uint64 slot = 1; - string lockupAddress = 2; // TODO: why is this a string? - uint64 time = 3; - oneof event { - LockupEventNew new = 4; - LockupEventVAAPosted vaaPosted = 5; - Empty empty = 6; - Empty slotEvent = 7; - } -} - -// Token on Solana was locked or burned. -message LockupEventNew { - uint32 nonce = 1; - uint32 sourceChain = 2; - uint32 targetChain = 3; - bytes sourceAddress = 4; - bytes targetAddress = 5; - uint32 tokenChain = 6; - bytes tokenAddress = 7; - uint32 tokenDecimals = 8; - bytes amount = 9; -} - -// A VAA was posted to Solana for data availability. -message LockupEventVAAPosted { - uint32 nonce = 1; - uint32 sourceChain = 2; - uint32 targetChain = 3; - bytes sourceAddress = 4; - bytes targetAddress = 5; - uint32 tokenChain = 6; - bytes tokenAddress = 7; - uint32 tokenDecimals = 8; - bytes amount = 9; - - bytes vaa = 10; -} - message GetBalanceRequest{ } diff --git a/solana/agent/src/main.rs b/solana/agent/src/main.rs index 9c984684..aa6fdb7b 100644 --- a/solana/agent/src/main.rs +++ b/solana/agent/src/main.rs @@ -22,10 +22,8 @@ use tonic::{transport::Server, Code, Request, Response, Status}; use service::{ agent_server::{Agent, AgentServer}, - lockup_event::Event, - Empty, LockupEvent, LockupEventNew, LockupEventVaaPosted, SubmitVaaRequest, SubmitVaaResponse, + Empty,SubmitVaaRequest, SubmitVaaResponse, GetBalanceResponse, GetBalanceRequest, - WatchLockupsRequest, }; use spl_bridge::{ instruction::{post_vaa, verify_signatures, VerifySigPayload, CHAIN_ID_SOLANA}, @@ -33,9 +31,6 @@ use spl_bridge::{ vaa::VAA, }; -use crate::monitor::PubsubClient; - -mod monitor; mod socket; pub mod service { @@ -153,153 +148,6 @@ impl Agent for AgentImpl { .join() .unwrap() } - - type WatchLockupsStream = mpsc::Receiver>; - - async fn watch_lockups( - &self, - _req: Request, - ) -> Result, Status> { - let (mut tx, rx) = mpsc::channel(1); - let mut tx2 = tx.clone(); - let url = self.url.clone(); - let url2 = self.url.clone(); - let bridge = self.bridge.clone(); - let rpc_url = self.rpc_url.clone(); - let rpc_url2 = self.rpc_url.clone(); - - tokio::spawn(async move { - let _rpc = RpcClient::new(rpc_url.to_string()); - let sub = PubsubClient::program_subscribe(&url, &bridge).unwrap(); - // looping and sending our response using stream - loop { - let item = sub.1.recv(); - match item { - Ok(v) => { - // We only want to track lockups - if v.value.account.data.len() != size_of::() { - continue; - } - - println!("lockup changed in slot: {}", v.context.slot); - - let b = match Bridge::unpack_immutable::( - v.value.account.data.as_slice(), - ) { - Ok(v) => v, - Err(e) => { - println!("failed to deserialize lockup: {}", e); - continue; - } - }; - - let mut amount_b: [u8; 32] = [0; 32]; - b.amount.to_big_endian(&mut amount_b); - - let event = if b.vaa_time == 0 { - // The Lockup was created - LockupEvent { - slot: v.context.slot, - lockup_address: v.value.pubkey.to_string(), - time: b.lockup_time as u64, - event: Some(Event::New(LockupEventNew { - nonce: b.nonce, - source_chain: CHAIN_ID_SOLANA as u32, - target_chain: b.to_chain_id as u32, - source_address: b.source_address.to_vec(), - target_address: b.foreign_address.to_vec(), - token_chain: b.asset.chain as u32, - token_address: b.asset.address.to_vec(), - token_decimals: b.asset.decimals as u32, - amount: amount_b.to_vec(), - })), - } - } else { - // The VAA was submitted - LockupEvent { - slot: v.context.slot, - lockup_address: v.value.pubkey.to_string(), - time: b.lockup_time as u64, - event: Some(Event::VaaPosted(LockupEventVaaPosted { - nonce: b.nonce, - source_chain: CHAIN_ID_SOLANA as u32, - target_chain: b.to_chain_id as u32, - source_address: b.source_address.to_vec(), - target_address: b.foreign_address.to_vec(), - token_chain: b.asset.chain as u32, - token_address: b.asset.address.to_vec(), - token_decimals: b.asset.decimals as u32, - amount: amount_b.to_vec(), - vaa: b.vaa.to_vec(), - })), - } - }; - - let mut amount_b: [u8; 32] = [0; 32]; - b.amount.to_big_endian(&mut amount_b); - - if let Err(e) = tx.send(Ok(event)).await { - println!("sending event failed: {}", e); - return; - }; - // We need to push a second message to flush the channel - // https://github.com/hyperium/tonic/issues/378 - if let Err(e) = tx - .send(Ok(LockupEvent { - slot: 0, - time: 0, - lockup_address: String::from(""), - event: Some(Event::Empty(Empty {})), - })) - .await - { - println!("sending event failed: {}", e); - return; - }; - } - Err(e) => { - println!("watcher died: {}", e); - tx.send(Err(Status::new(Code::Aborted, "watcher died"))) - .await; - return; - } - }; - } - }); - - tokio::spawn(async move { - let _rpc = RpcClient::new(rpc_url2.to_string()); - let sub = solana_client::pubsub_client::PubsubClient::slot_subscribe(&url2).unwrap(); - // looping and sending our response using stream - loop { - let item = sub.1.recv(); - match item { - Ok(v) => { - if let Err(e) = tx2 - .send(Ok(LockupEvent { - slot: v.slot, - time: 0, - lockup_address: String::from(""), - event: Some(Event::SlotEvent(Empty {})), - })) - .await - { - println!("sending event failed: {}", e); - return; - }; - } - Err(e) => { - println!("watcher died: {}", e); - tx2.send(Err(Status::new(Code::Aborted, "watcher died"))) - .await; - return; - } - }; - } - }); - - Ok(Response::new(rx)) - } } fn pack_sig_verification_txs<'a>( diff --git a/solana/agent/src/monitor.rs b/solana/agent/src/monitor.rs deleted file mode 100644 index 33b7962b..00000000 --- a/solana/agent/src/monitor.rs +++ /dev/null @@ -1,274 +0,0 @@ -use std::{ - marker::PhantomData, - str::FromStr, - sync::{ - atomic::{AtomicBool, Ordering}, - mpsc::{channel, Receiver}, - Arc, RwLock, - }, - thread::JoinHandle, -}; - -use bs58; -use log::*; -use serde::{ - de::{DeserializeOwned, Error}, - Deserialize, Deserializer, Serialize, -}; -use serde_json::{ - json, - value::Value::{Number, Object}, - Map, Value, -}; - -use solana_sdk::pubkey::Pubkey; -use thiserror::Error; -use tungstenite::{client::AutoStream, connect, Message, WebSocket}; -use url::{ParseError, Url}; - -#[derive(Debug, Error)] -pub enum PubsubClientError { - #[error("url parse error")] - UrlParseError(#[from] ParseError), - - #[error("unable to connect to server")] - ConnectionError(#[from] tungstenite::Error), - - #[error("json parse error")] - JsonParseError(#[from] serde_json::error::Error), - - #[error("unexpected message format")] - UnexpectedMessageError, -} - -#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)] -pub struct ProgramUpdate { - #[serde(deserialize_with = "from_bs58")] - pub pubkey: Pubkey, - pub account: ProgramAccount, -} - -#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)] -#[serde(rename_all = "camelCase")] -pub struct ProgramAccount { - /// lamports in the account - pub lamports: u64, - /// data held in this account - #[serde(deserialize_with = "bytes_from_bs58")] - pub data: Vec, - /// the program that owns this account. If executable, the program that loads this account. - #[serde(deserialize_with = "from_bs58")] - pub owner: Pubkey, - /// this account's data contains a loaded program (and is now read-only) - pub executable: bool, - /// the epoch at which this account will next owe rent - pub rent_epoch: u64, -} - -#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)] -pub struct ProgramUpdateContext { - pub slot: u64, -} - -#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)] -pub struct ProgramNotificationMessage { - pub value: ProgramUpdate, - pub context: ProgramUpdateContext, -} - -pub struct PubsubClientSubscription -where - T: DeserializeOwned, -{ - message_type: PhantomData, - operation: &'static str, - socket: Arc>>, - subscription_id: u64, - t_cleanup: Option>, - exit: Arc, -} - -impl Drop for PubsubClientSubscription -where - T: DeserializeOwned, -{ - fn drop(&mut self) { - self.send_unsubscribe() - .unwrap_or_else(|_| warn!("unable to unsubscribe from websocket")); - self.socket - .write() - .unwrap() - .close(None) - .unwrap_or_else(|_| warn!("unable to close websocket")); - } -} - -impl PubsubClientSubscription -where - T: DeserializeOwned, -{ - fn send_subscribe( - writable_socket: &Arc>>, - operation: &str, - program: &Pubkey, - ) -> Result { - let method = format!("{}Subscribe", operation); - writable_socket - .write() - .unwrap() - .write_message(Message::Text( - json!({ - "jsonrpc":"2.0","id":1,"method":method,"params":[program.to_string(),{"encoding":"binary", "commitment": "max"}] - }) - .to_string(), - ))?; - let message = writable_socket.write().unwrap().read_message()?; - Self::extract_subscription_id(message) - } - - fn extract_subscription_id(message: Message) -> Result { - let message_text = &message.into_text()?; - let json_msg: Map = serde_json::from_str(message_text)?; - - if let Some(Number(x)) = json_msg.get("result") { - if let Some(x) = x.as_u64() { - return Ok(x); - } - } - - Err(PubsubClientError::UnexpectedMessageError) - } - - pub fn send_unsubscribe(&self) -> Result<(), PubsubClientError> { - let method = format!("{}Unubscribe", self.operation); - self.socket - .write() - .unwrap() - .write_message(Message::Text( - json!({ - "jsonrpc":"2.0","id":1,"method":method,"params":[self.subscription_id] - }) - .to_string(), - )) - .map_err(|err| err.into()) - } - - fn read_message( - writable_socket: &Arc>>, - ) -> Result { - let message = writable_socket.write().unwrap().read_message()?; - let message_text = &message.into_text().unwrap(); - let json_msg: Map = serde_json::from_str(message_text)?; - - if let Some(Object(value_1)) = json_msg.get("params") { - if let Some(value_2) = value_1.get("result") { - let x: T = serde_json::from_value::(value_2.clone()).unwrap(); - return Ok(x); - } - } - - Err(PubsubClientError::UnexpectedMessageError) - } - - pub fn shutdown(&mut self) -> std::thread::Result<()> { - if self.t_cleanup.is_some() { - info!("websocket thread - shutting down"); - self.exit.store(true, Ordering::Relaxed); - let x = self.t_cleanup.take().unwrap().join(); - info!("websocket thread - shut down."); - x - } else { - warn!("websocket thread - already shut down."); - Ok(()) - } - } -} - -const SLOT_OPERATION: &str = "program"; - -pub struct PubsubClient {} - -impl PubsubClient { - pub fn program_subscribe( - url: &str, - program: &Pubkey, - ) -> Result< - ( - PubsubClientSubscription, - Receiver, - ), - PubsubClientError, - > { - let url = Url::parse(url)?; - let (socket, _response) = connect(url)?; - let (sender, receiver) = channel::(); - - let socket = Arc::new(RwLock::new(socket)); - let socket_clone = socket.clone(); - let exit = Arc::new(AtomicBool::new(false)); - let exit_clone = exit.clone(); - let subscription_id = - PubsubClientSubscription::::send_subscribe( - &socket_clone, - SLOT_OPERATION, - program, - ) - .unwrap(); - - let t_cleanup = std::thread::spawn(move || { - loop { - if exit_clone.load(Ordering::Relaxed) { - break; - } - - let message: Result = - PubsubClientSubscription::read_message(&socket_clone); - - if let Ok(msg) = message { - match sender.send(msg.clone()) { - Ok(_) => (), - Err(err) => { - info!("receive error: {:?}", err); - break; - } - } - } else { - info!("receive error: {:?}", message); - break; - } - } - - info!("websocket - exited receive loop"); - }); - - let result: PubsubClientSubscription = - PubsubClientSubscription { - message_type: PhantomData, - operation: SLOT_OPERATION, - socket, - subscription_id, - t_cleanup: Some(t_cleanup), - exit, - }; - - Ok((result, receiver)) - } -} - -fn from_bs58<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let s: String = Deserialize::deserialize(deserializer)?; - Pubkey::from_str(s.as_str()).map_err(D::Error::custom) -} - -fn bytes_from_bs58<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - let s: String = Deserialize::deserialize(deserializer)?; - bs58::decode(s.as_str()) - .into_vec() - .map_err(D::Error::custom) -}