bridge/pkg/solana: use polling GetProgramAccounts watcher (#156)
This mitigates https://github.com/solana-labs/solana/issues/9909 by polling GetProgramAccounts with a server-side filter. It also removes the agent dependency for the lockup observation logic - the agent is now used for transaction construction only.
This commit is contained in:
parent
108f050c0e
commit
fd6c54de83
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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{
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Result<LockupEvent, Status>>;
|
||||
|
||||
async fn watch_lockups(
|
||||
&self,
|
||||
_req: Request<WatchLockupsRequest>,
|
||||
) -> Result<Response<Self::WatchLockupsStream>, 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::<TransferOutProposal>() {
|
||||
continue;
|
||||
}
|
||||
|
||||
println!("lockup changed in slot: {}", v.context.slot);
|
||||
|
||||
let b = match Bridge::unpack_immutable::<TransferOutProposal>(
|
||||
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>(
|
||||
|
|
|
@ -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<u8>,
|
||||
/// 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<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
message_type: PhantomData<T>,
|
||||
operation: &'static str,
|
||||
socket: Arc<RwLock<WebSocket<AutoStream>>>,
|
||||
subscription_id: u64,
|
||||
t_cleanup: Option<JoinHandle<()>>,
|
||||
exit: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl<T> Drop for PubsubClientSubscription<T>
|
||||
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<T> PubsubClientSubscription<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
fn send_subscribe(
|
||||
writable_socket: &Arc<RwLock<WebSocket<AutoStream>>>,
|
||||
operation: &str,
|
||||
program: &Pubkey,
|
||||
) -> Result<u64, PubsubClientError> {
|
||||
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<u64, PubsubClientError> {
|
||||
let message_text = &message.into_text()?;
|
||||
let json_msg: Map<String, Value> = 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<RwLock<WebSocket<AutoStream>>>,
|
||||
) -> Result<T, PubsubClientError> {
|
||||
let message = writable_socket.write().unwrap().read_message()?;
|
||||
let message_text = &message.into_text().unwrap();
|
||||
let json_msg: Map<String, Value> = 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::<T>(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<ProgramNotificationMessage>,
|
||||
Receiver<ProgramNotificationMessage>,
|
||||
),
|
||||
PubsubClientError,
|
||||
> {
|
||||
let url = Url::parse(url)?;
|
||||
let (socket, _response) = connect(url)?;
|
||||
let (sender, receiver) = channel::<ProgramNotificationMessage>();
|
||||
|
||||
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::<ProgramNotificationMessage>::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<ProgramNotificationMessage, PubsubClientError> =
|
||||
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<ProgramNotificationMessage> =
|
||||
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<Pubkey, D::Error>
|
||||
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<Vec<u8>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s: String = Deserialize::deserialize(deserializer)?;
|
||||
bs58::decode(s.as_str())
|
||||
.into_vec()
|
||||
.map_err(D::Error::custom)
|
||||
}
|
Loading…
Reference in New Issue