From 7e0bbdbe6e8a67848639f1940d4ca39f05fba813 Mon Sep 17 00:00:00 2001 From: Leo Date: Fri, 30 Jul 2021 22:40:01 +0200 Subject: [PATCH] node/pkg/db: store signed VAAs in database Bug: certusone/wormhole#282 Change-Id: Iecd4ff74a1e73655ac3240991a4dc36e572cdb15 --- bridge/cmd/guardiand/bridge.go | 14 +++++ bridge/go.mod | 1 + bridge/go.sum | 13 +++++ bridge/pkg/db/db.go | 85 +++++++++++++++++++++++++++++ bridge/pkg/processor/observation.go | 6 +- bridge/pkg/processor/processor.go | 5 ++ 6 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 bridge/pkg/db/db.go diff --git a/bridge/cmd/guardiand/bridge.go b/bridge/cmd/guardiand/bridge.go index fb59d6be..18951f58 100644 --- a/bridge/cmd/guardiand/bridge.go +++ b/bridge/cmd/guardiand/bridge.go @@ -3,11 +3,13 @@ package guardiand import ( "context" "fmt" + "github.com/certusone/wormhole/bridge/pkg/db" "github.com/gagliardetto/solana-go/rpc" "log" "net/http" _ "net/http/pprof" "os" + "path" "syscall" solana_types "github.com/gagliardetto/solana-go" @@ -322,6 +324,17 @@ func runBridge(cmd *cobra.Command, args []string) { } } + // Database + dbPath := path.Join(*dataDir, "db") + if err := os.MkdirAll(dbPath, 0700); err != nil { + logger.Fatal("failed to create database directory", zap.Error(err)) + } + db, err := db.Open(dbPath) + if err != nil { + logger.Fatal("failed to open database", zap.Error(err)) + } + defer db.Close() + // Guardian key gk, err := loadGuardianKey(*bridgeKeyPath) if err != nil { @@ -417,6 +430,7 @@ func runBridge(cmd *cobra.Command, args []string) { // TODO: this thing has way too many arguments at this point - make it an options struct p := processor.NewProcessor(ctx, + db, lockC, setC, sendC, diff --git a/bridge/go.mod b/bridge/go.mod index 41ca4d95..d31ad4c5 100644 --- a/bridge/go.mod +++ b/bridge/go.mod @@ -5,6 +5,7 @@ go 1.16 require ( github.com/cenkalti/backoff/v4 v4.1.1 github.com/davecgh/go-spew v1.1.1 + github.com/dgraph-io/badger/v3 v3.2103.1 github.com/ethereum/go-ethereum v1.10.6 github.com/gagliardetto/solana-go v0.3.5-0.20210727215348-0cf016734976 github.com/gorilla/mux v1.7.4 diff --git a/bridge/go.sum b/bridge/go.sum index f60e9e45..1039d963 100644 --- a/bridge/go.sum +++ b/bridge/go.sum @@ -65,6 +65,7 @@ github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo 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/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= @@ -228,17 +229,24 @@ github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70/go.mod h1:EoK/8RF 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= +github.com/dgraph-io/badger v1.6.1 h1:w9pSFNSdq/JPM1N12Fz/F/bzo993Is1W+Q7HjPzi7yg= github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU= +github.com/dgraph-io/badger/v3 v3.2103.1 h1:zaX53IRg7ycxVlkd5pYdCeFp1FynD6qBGQoQql3R3Hk= +github.com/dgraph-io/badger/v3 v3.2103.1/go.mod h1:dULbq6ehJ5K0cGW/1TQ9iSfUk0gbSiToDWmWmTsJ53E= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= +github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a h1:mq+R6XEM6lJX5VlLyZIrUSP8tSuJp82xTK89hvBwJbU= github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= @@ -320,6 +328,7 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -363,6 +372,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/flatbuffers v1.12.0 h1:/PtAHvnBY4Kqnx/xCQ3OIV9uYcSFGScBsWI3Oogeh6w= +github.com/google/flatbuffers v1.12.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -587,6 +598,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.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.1 h1:wXr2uRxZTJXHLly6qhJabee5JqIhTRoLBhDOA74hDEQ= github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5 h1:2U0HzY8BJ8hVwDKIzp7y4voR9CX/nvcfymLmg2UiOio= @@ -1173,6 +1185,7 @@ github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7A github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.1 h1:qgMbHoJbPbw579P+1zVY+6n4nIFuIchaIjzZ/I/Yq8M= diff --git a/bridge/pkg/db/db.go b/bridge/pkg/db/db.go new file mode 100644 index 00000000..eeda266b --- /dev/null +++ b/bridge/pkg/db/db.go @@ -0,0 +1,85 @@ +package db + +import ( + "errors" + "fmt" + "github.com/certusone/wormhole/bridge/pkg/vaa" + "github.com/dgraph-io/badger/v3" +) + +type Database struct { + db *badger.DB +} + +type VAAID struct { + EmitterChain vaa.ChainID + EmitterAddress vaa.Address + Sequence uint64 +} + +func vaaIDFromVAA(v *vaa.VAA) *VAAID { + return &VAAID{ + EmitterChain: v.EmitterChain, + EmitterAddress: v.EmitterAddress, + Sequence: v.Sequence, + } +} + +var ( + ErrVAANotFound = errors.New("requested VAA not found in store") +) + +func (i *VAAID) Bytes() []byte { + return []byte(fmt.Sprintf("signed/%d/%s/%d", i.EmitterChain, i.EmitterAddress, i.Sequence)) +} + +func Open(path string) (*Database, error) { + db, err := badger.Open(badger.DefaultOptions(path)) + if err != nil { + return nil, fmt.Errorf("failed to open database: %w", err) + } + return &Database{ + db: db, + }, nil +} + +func (d *Database) Close() error { + return d.db.Close() +} + +func (d *Database) StoreSignedVAA(v *vaa.VAA) error { + if len(v.Signatures) == 0 { + panic("StoreSignedVAA called for unsigned VAA") + } + + b, _ := v.Marshal() + + err := d.db.Update(func(txn *badger.Txn) error { + if err := txn.Set(vaaIDFromVAA(v).Bytes(), b); err != nil { + return err + } + return nil + }) + + if err != nil { + return fmt.Errorf("failed to commit tx: %w", err) + } + + return nil +} + +func (d *Database) GetSignedVAABytes(id VAAID) (b []byte, err error) { + if err := d.db.View(func(txn *badger.Txn) error { + item, err := txn.Get(id.Bytes()) + if err != nil { + return err + } + if _, err := item.ValueCopy(b); err != nil { + return err + } + return nil + }); err != nil { + return nil, err + } + return +} diff --git a/bridge/pkg/processor/observation.go b/bridge/pkg/processor/observation.go index 99cedd00..296da9a9 100644 --- a/bridge/pkg/processor/observation.go +++ b/bridge/pkg/processor/observation.go @@ -226,13 +226,15 @@ func (p *Processor) handleObservation(ctx context.Context, m *gossipv1.SignedObs panic(err) } - // Submit every VAA to Solana for data availability. + // Store signed VAA in database. p.logger.Info("signed VAA with quorum", zap.String("digest", hash), zap.Any("vaa", signed), zap.String("bytes", hex.EncodeToString(vaaBytes))) - // TODO: broadcast on p2p and persist + if err := p.db.StoreSignedVAA(signed); err != nil { + p.logger.Error("failed to store signed VAA", zap.Error(err)) + } p.state.vaaSignatures[hash].submitted = true } else { p.logger.Info("quorum not met or already submitted, doing nothing", diff --git a/bridge/pkg/processor/processor.go b/bridge/pkg/processor/processor.go index ecb298e3..181685b6 100644 --- a/bridge/pkg/processor/processor.go +++ b/bridge/pkg/processor/processor.go @@ -3,6 +3,7 @@ package processor import ( "context" "crypto/ecdsa" + "github.com/certusone/wormhole/bridge/pkg/db" "time" ethcommon "github.com/ethereum/go-ethereum/common" @@ -76,6 +77,8 @@ type Processor struct { logger *zap.Logger + db *db.Database + // Runtime state // gs is the currently valid guardian set @@ -90,6 +93,7 @@ type Processor struct { func NewProcessor( ctx context.Context, + db *db.Database, lockC chan *common.MessagePublication, setC chan *common.GuardianSet, sendC chan []byte, @@ -113,6 +117,7 @@ func NewProcessor( devnetMode: devnetMode, devnetNumGuardians: devnetNumGuardians, devnetEthRPC: devnetEthRPC, + db: db, terraLCD: terraLCD, terraChainID: terraChainID,