diff --git a/node/cmd/guardiand/node.go b/node/cmd/guardiand/node.go index 03dab1cb35..fc3a359139 100644 --- a/node/cmd/guardiand/node.go +++ b/node/cmd/guardiand/node.go @@ -15,9 +15,9 @@ import ( "time" "github.com/certusone/wormhole/node/pkg/guardiansigner" - "github.com/certusone/wormhole/node/pkg/tss" "github.com/certusone/wormhole/node/pkg/watchers" "github.com/certusone/wormhole/node/pkg/watchers/ibc" + ethcommon "github.com/ethereum/go-ethereum/common" ethcrypto "github.com/ethereum/go-ethereum/crypto" "github.com/certusone/wormhole/node/pkg/watchers/cosmwasm" @@ -73,7 +73,11 @@ var ( guardianKeyPath *string guardianSignerUri *string - tssSecretsPath *string + tssTLSCertPath *string + tssTLSKeyPath *string + tssSignerAddr *string + tssLeaderAddr *string + tssConfigurationsPath *string ethRPC *string ethContract *string @@ -324,7 +328,11 @@ func init() { fogoContract = NodeCmd.Flags().String("fogoContract", "", "Address of the Fogo program (required if fogoRpc is specified)") fogoShimContract = NodeCmd.Flags().String("fogoShimContract", "", "Address of the Fogo shim program") - tssSecretsPath = NodeCmd.Flags().String("tssSecret", "", "Path to guardian tss secrets (required)") + tssTLSCertPath = NodeCmd.Flags().String("tssTLSCert", "", "Path to guardian tss TLS certificate (required for secure connections)") + tssTLSKeyPath = NodeCmd.Flags().String("tssTLSKey", "", "Path to guardian tss TLS key (required for secure connections)") + tssSignerAddr = NodeCmd.Flags().String("tssSignerAddress", "127.0.0.1:9973", "Path to guardian tss socket (address:port for TCP connections)") + tssLeaderAddr = NodeCmd.Flags().String("tssLeaderAddress", "", "ethereum address (as hex) of the guardian that is the TSS leader") + tssConfigurationsPath = NodeCmd.Flags().String("tssConfigurations", "", "Path to guardian tss configurations JSON file (optional)") ethRPC = node.RegisterFlagWithValidationOrFail(NodeCmd, "ethRPC", "Ethereum RPC URL", "ws://eth-devnet:8545", []string{"ws", "wss"}) ethContract = NodeCmd.Flags().String("ethContract", "", "Ethereum contract address") @@ -763,8 +771,10 @@ func runNode(cmd *cobra.Command, args []string) { logger.Fatal("failed to create a new guardian signer", zap.Error(err)) } - logger.Info("Created the guardian signer", zap.String( - "address", ethcrypto.PubkeyToAddress(guardianSigner.PublicKey(rootCtx)).String())) + guardianSignerAddress := ethcrypto.PubkeyToAddress(guardianSigner.PublicKey(rootCtx)) + logger.Info("Created the guardian signer", + zap.String("address", guardianSignerAddress.String()), + ) // Load p2p private key var p2pKey libp2p_crypto.PrivKey @@ -1883,21 +1893,11 @@ func runNode(cmd *cobra.Command, args []string) { } } - tssGuardianStorage, err := tss.NewGuardianStorageFromFile(*tssSecretsPath) - if err != nil { - logger.Fatal("failed to load the guardian's threshold signature scheme's secrets", zap.Error(err)) - } logger.Info("Loaded the guardian's threshold signature scheme's storage") - reliableTss, err := tss.NewReliableTSS(tssGuardianStorage) - if err != nil { - logger.Fatal("failed to start tss engine", zap.Error(err)) - } - guardianNode := node.NewGuardianNode( env, guardianSigner, - reliableTss, ) var guardianAddrAsBytes []byte @@ -1906,6 +1906,7 @@ func runNode(cmd *cobra.Command, args []string) { } guardianOptions := []*node.GuardianOption{ + node.GuardianOptionTSS(guardianSignerAddress, ethcommon.HexToAddress(*tssLeaderAddr), *tssConfigurationsPath, *tssSignerAddr, *tssTLSCertPath, *tssTLSKeyPath), node.GuardianOptionDatabase(db), node.GuardianOptionWatchers(watcherConfigs, ibcWatcherConfig), node.GuardianOptionAccountant(*accountantWS, *accountantContract, *accountantCheckEnabled, accountantWormchainConn, *accountantNttContract, accountantNttWormchainConn), @@ -1917,7 +1918,6 @@ func runNode(cmd *cobra.Command, args []string) { node.GuardianOptionStatusServer(*statusAddr), node.GuardianOptionAlternatePublisher(guardianAddrAsBytes, *additionalPublishers), node.GuardianOptionProcessor(*p2pNetworkID), - node.GuardianOptionTSSNetwork(), // Keep this last so that all of its dependencies are met. node.GuardianOptionP2P( diff --git a/node/go.mod b/node/go.mod index 9d750c9239..97c9c56daa 100644 --- a/node/go.mod +++ b/node/go.mod @@ -1,6 +1,8 @@ module github.com/certusone/wormhole/node -go 1.23.3 +go 1.24.0 + +toolchain go1.24.5 require ( github.com/cenkalti/backoff/v4 v4.2.0 @@ -27,17 +29,17 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.14.0 github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 // indirect - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.11.1 github.com/tendermint/tendermint v0.34.24 github.com/tidwall/gjson v1.15.0 go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.28.0 - golang.org/x/sys v0.26.0 + golang.org/x/crypto v0.41.0 + golang.org/x/sys v0.35.0 golang.org/x/time v0.5.0 google.golang.org/api v0.126.0 google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect - google.golang.org/grpc v1.57.1 - google.golang.org/protobuf v1.36.10 + google.golang.org/grpc v1.76.0 + google.golang.org/protobuf v1.36.11 ) require ( @@ -61,15 +63,15 @@ require ( github.com/prometheus/common v0.60.0 github.com/wormhole-foundation/wormchain v0.0.0-00010101000000-000000000000 github.com/wormhole-foundation/wormhole/sdk v0.0.0-20220926172624-4b38dc650bb0 - github.com/xlabs/multi-party-sig v0.0.2-0.20251008145440-ef00c6484b73 - github.com/xlabs/tss-common v0.0.0-20251006064114-b1fcd9c2ce8e + github.com/xlabs/multi-party-sig v0.0.2-0.20260123131947-80650451a297 + github.com/xlabs/tss-common v0.0.0-20260123064940-13b8c984d60d golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e + google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b nhooyr.io/websocket v1.8.7 ) require ( - cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/compute/metadata v0.7.0 // indirect contrib.go.opencensus.io/exporter/stackdriver v0.13.11 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect @@ -140,12 +142,12 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect - github.com/fxamacker/cbor/v2 v2.8.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/gagliardetto/binary v0.7.7 // indirect github.com/gagliardetto/treeout v0.1.4 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/analysis v0.21.4 // indirect @@ -164,7 +166,7 @@ require ( github.com/gogo/gateway v1.1.0 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.3 // indirect - github.com/golang/glog v1.1.0 // indirect + github.com/golang/glog v1.2.5 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.2 // indirect @@ -349,10 +351,11 @@ require ( go.etcd.io/etcd/client/v3 v3.5.5 // indirect go.mongodb.org/mongo-driver v1.14.0 // indirect go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect - go.opentelemetry.io/otel v1.21.0 // indirect - go.opentelemetry.io/otel/metric v1.21.0 // indirect - go.opentelemetry.io/otel/trace v1.21.0 // indirect + go.opentelemetry.io/otel v1.37.0 // indirect + go.opentelemetry.io/otel/metric v1.37.0 // indirect + go.opentelemetry.io/otel/trace v1.37.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/dig v1.18.0 // indirect go.uber.org/fx v1.23.0 // indirect @@ -360,16 +363,16 @@ require ( go.uber.org/mock v0.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect - golang.org/x/mod v0.21.0 // indirect - golang.org/x/net v0.30.0 // indirect - golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/term v0.25.0 // indirect - golang.org/x/text v0.19.0 // indirect - golang.org/x/tools v0.26.0 // indirect - gonum.org/v1/gonum v0.13.0 // indirect + golang.org/x/mod v0.26.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/term v0.34.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/tools v0.35.0 // indirect + gonum.org/v1/gonum v0.16.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect @@ -389,12 +392,7 @@ replace github.com/CosmWasm/wasmd v0.30.0 => github.com/wormhole-foundation/wasm replace github.com/cosmos/cosmos-sdk => github.com/wormhole-foundation/cosmos-sdk v0.45.9-wormhole -require ( - github.com/gogo/status v1.1.1 - github.com/test-go/testify v1.1.4 - // github.com/xlabs/tss-lib/v2 v2.0.0-20241224083831-c218006b15e3 - github.com/xlabs/tss-lib/v2 v2.0.0-20251008162257-d741b8e3b429 -) +require github.com/gogo/status v1.1.1 replace github.com/agl/ed25519 => github.com/binance-chain/edwards25519 v0.0.0-20200305024217-f36fc4b53d43 // done in the TSS-LIB. diff --git a/node/go.sum b/node/go.sum index df59325aeb..e831ddf92f 100644 --- a/node/go.sum +++ b/node/go.sum @@ -135,8 +135,8 @@ cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZ cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU= +cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= @@ -849,8 +849,8 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= @@ -1193,15 +1193,16 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= -github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f h1:7T++XKzy4xg7PKy+bM+Sa9/oe1OC88yz2hXQUISoXfA= -github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= +github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= +github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= +github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= -github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0= github.com/ethereum/go-ethereum v1.9.25/go.mod h1:vMkFiYLHI4tgPw4k2j4MHKoovchFE8plZ0M9VMk4/oM= github.com/ethereum/go-ethereum v1.10.21 h1:5lqsEx92ZaZzRyOqBEXux4/UR06m296RGzN3ol3teJY= @@ -1265,8 +1266,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM= -github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= -github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/fzipp/gocyclo v0.5.1/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/gagliardetto/binary v0.7.7 h1:QZpT38+sgoPg+TIQjH94sLbl/vX+nlIRA37pEyOsjfY= @@ -1320,8 +1321,8 @@ github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= @@ -1481,8 +1482,8 @@ github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I= +github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= 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= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -2578,6 +2579,8 @@ github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdL github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= @@ -2717,8 +2720,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= @@ -2937,8 +2940,9 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= github.com/subosito/gotenv v1.4.0/go.mod h1:mZd6rFysKEcUhUHXJk0C/08wAgyDBFuwEYL7vWWGaGo= @@ -3133,12 +3137,10 @@ github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlabs/multi-party-sig v0.0.2-0.20251008145440-ef00c6484b73 h1:MP649hemhNYpdOA/l6hXfqd8bwyBB8FPdZzCGlyo1SU= -github.com/xlabs/multi-party-sig v0.0.2-0.20251008145440-ef00c6484b73/go.mod h1:NpqmP2W7AAEany+2usJbi0eadzytXVSoucwK1SQ89cs= -github.com/xlabs/tss-common v0.0.0-20251006064114-b1fcd9c2ce8e h1:5PRcFPXMXmvPL4IBEA+h7Evvd2HziSMdcFKuXtiqugg= -github.com/xlabs/tss-common v0.0.0-20251006064114-b1fcd9c2ce8e/go.mod h1:EBXV5kup1GsnVpEknaxXXpid87uN6Mh7NboAZhV+HNM= -github.com/xlabs/tss-lib/v2 v2.0.0-20251008162257-d741b8e3b429 h1:zPlr1aiXP/dExYCLMasJy3IU+POVwOUQjQjMMXjN2uM= -github.com/xlabs/tss-lib/v2 v2.0.0-20251008162257-d741b8e3b429/go.mod h1:P6bh6juLJXH/zm/lVAeDiF1g6gbrtBGHreo5PFZdDZM= +github.com/xlabs/multi-party-sig v0.0.2-0.20260123131947-80650451a297 h1:eS6dFzAxUbRwWFB+8MDwF593Iq9k9WirvVdZm8fZudk= +github.com/xlabs/multi-party-sig v0.0.2-0.20260123131947-80650451a297/go.mod h1:Ay/VW23Nj586fRacOhLkWULwaK3MSi5INuIcx3SZovA= +github.com/xlabs/tss-common v0.0.0-20260123064940-13b8c984d60d h1:z57FZNB7w/AJpxmx8ZN+29Edho8sccrn4oeewelH0mo= +github.com/xlabs/tss-common v0.0.0-20260123064940-13b8c984d60d/go.mod h1:fZmQ4NRkV/rXf68cDLgZoi/4LF89Nn8Rh2KGdxDB+ZM= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= @@ -3231,6 +3233,8 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w= @@ -3246,8 +3250,8 @@ go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVj go.opentelemetry.io/otel v1.4.0/go.mod h1:jeAqMFKy2uLIxCtKxoFj0FAL5zAPKQagc3+GtBWakzk= go.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4= go.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM= -go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= -go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= go.opentelemetry.io/otel/exporters/jaeger v1.4.1/go.mod h1:ZW7vkOu9nC1CxsD8bHNHCia5JUbwP39vxgd1q4Z5rCI= go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= @@ -3261,21 +3265,25 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.4.1/go.mod h1 go.opentelemetry.io/otel/internal/metric v0.27.0/go.mod h1:n1CVxRqKqYZtqyTh9U/onvKapPGv7y/rpyOTI+LFNzw= go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= go.opentelemetry.io/otel/metric v0.27.0/go.mod h1:raXDJ7uP2/Jc0nVZWQjJtzoyssOYWu/+pjZqRzfvZ7g= -go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= -go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= go.opentelemetry.io/otel/sdk v1.4.1/go.mod h1:NBwHDgDIBYjwK2WNu1OPgsIc2IJzmBXNnvIJxJc8BpE= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= go.opentelemetry.io/otel/trace v1.4.0/go.mod h1:uc3eRsqDfWs9R7b92xbQbU42/eTNz4N+gLP8qJCi4aE= go.opentelemetry.io/otel/trace v1.4.1/go.mod h1:iYEVbroFCNut9QkwEczV9vMRPHNKSSwYZjulEtsmhFc= go.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4= -go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= -go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= go.opentelemetry.io/proto/otlp v0.12.0/go.mod h1:TsIjwGWIx5VFYv9KGVlOpxoBl5Dy+63SUguV7GGvlSQ= @@ -3384,8 +3392,8 @@ golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -3443,8 +3451,8 @@ golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= +golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -3549,8 +3557,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.0.0-20180724155351-3d292e4d0cdc/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -3584,8 +3592,8 @@ golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= 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= @@ -3604,8 +3612,8 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -3790,8 +3798,8 @@ golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -3808,8 +3816,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -3827,8 +3835,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -3978,8 +3986,8 @@ golang.org/x/tools v0.1.12-0.20220628192153-7743d1d949f1/go.mod h1:SgwaegtQh8clI golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= 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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -3991,8 +3999,8 @@ golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNq gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.13.0 h1:a0T3bh+7fhRyqeNbiC3qVHYmkiQgit3wnNan/2c0HMM= -gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= @@ -4227,10 +4235,10 @@ google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614G google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= -google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e h1:z3vDksarJxsAKM5dmEGv0GHwE2hKJ096wZra71Vs4sw= -google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 h1:wukfNtZmZUurLN/atp2hiIeTKn7QJWIQdHzqmsOnAOk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b h1:ULiyYQ0FdsJhwwZUwbaXpZF5yUE3h+RA+gxvBu37ucc= +google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:oDOGiMSXHL4sDTJvFvIB9nRQCGdLP1o/iVaqQK8zB+M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b h1:zPKJod4w6F1+nRGDI9ubnXYhU9NSWoFAijkHkUXeTK8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -4285,8 +4293,8 @@ google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCD google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= -google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= +google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -4304,8 +4312,8 @@ google.golang.org/protobuf v1.27.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/node/pkg/node/node.go b/node/pkg/node/node.go index 51f95b74a7..008bf4cc71 100644 --- a/node/pkg/node/node.go +++ b/node/pkg/node/node.go @@ -89,7 +89,7 @@ type G struct { // guardianSigner is the abstracted GuardianSigner that signs VAAs, or any other guardian-related information guardianSigner guardiansigner.GuardianSigner - tssEngine tss.ReliableTSS + tssEngine tss.SignerConnection // components db *db.Database @@ -137,14 +137,13 @@ type G struct { func NewGuardianNode( env common.Environment, guardianSigner guardiansigner.GuardianSigner, - tssEngine tss.ReliableTSS, ) *G { g := G{ env: env, - tssEngine: tssEngine, guardianSigner: guardianSigner, } + return &g } @@ -230,13 +229,6 @@ func (g *G) Run(rootCtxCancel context.CancelFunc, options ...*GuardianOption) su if g.tssEngine != nil { logger.Info("Starting TSS engine") - if err := g.tssEngine.SetGuardianSetState(g.gst); err != nil { - logger.Fatal("failed to set guardian set state", zap.Error(err)) - } - - if err := g.tssEngine.Start(ctx); err != nil { - logger.Fatal("failed to start TSS engine", zap.Error(err)) - } } // TODO there is an opportunity to refactor the startup of the accountant and governor: diff --git a/node/pkg/node/node_test.go b/node/pkg/node/node_test.go index 0dd400e922..ffe382cc76 100644 --- a/node/pkg/node/node_test.go +++ b/node/pkg/node/node_test.go @@ -26,13 +26,13 @@ import ( guardianDB "github.com/certusone/wormhole/node/pkg/db" "github.com/certusone/wormhole/node/pkg/devnet" "github.com/certusone/wormhole/node/pkg/guardiansigner" - "github.com/certusone/wormhole/node/pkg/internal/testutils" "github.com/certusone/wormhole/node/pkg/processor" gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1" publicrpcv1 "github.com/certusone/wormhole/node/pkg/proto/publicrpc/v1" "github.com/certusone/wormhole/node/pkg/readiness" "github.com/certusone/wormhole/node/pkg/supervisor" "github.com/certusone/wormhole/node/pkg/tss" + tssmock "github.com/certusone/wormhole/node/pkg/tss/mock" "github.com/certusone/wormhole/node/pkg/watchers" "github.com/certusone/wormhole/node/pkg/watchers/mock" eth_crypto "github.com/ethereum/go-ethereum/crypto" @@ -87,7 +87,7 @@ type mockGuardian struct { config *guardianConfig db *guardianDB.Database - tssEngine tss.ReliableTSS + tssEngine tss.SignerConnection } type guardianConfig struct { @@ -124,21 +124,6 @@ func newMockGuardianSet(t testing.TB, testId uint, n int) []*mockGuardian { panic(err) } - tssStoragePath, err := testutils.GetMockGuardianTssStorage(i) - if err != nil { - panic(err) - } - - tssStorage, err := tss.NewGuardianStorageFromFile(tssStoragePath) - if err != nil { - panic(err) - } - - reliableTss, err := tss.NewReliableTSS(tssStorage) - if err != nil { - panic(err) - } - gs[i] = &mockGuardian{ p2pKey: devnet.DeterministicP2PPrivKeyByIndex(int64(i)), MockObservationC: make(chan *common.MessagePublication), @@ -147,11 +132,10 @@ func newMockGuardianSet(t testing.TB, testId uint, n int) []*mockGuardian { guardianAddr: eth_crypto.PubkeyToAddress(guardianSigner.PublicKey(context.Background())), config: createGuardianConfig(t, testId, uint(i)), // #nosec G115 -- Guardian set will never be that large - tssEngine: reliableTss, + tssEngine: tssmock.NewMockSignerConnection(), } } - setTSSEngineAccordingToMockSet(gs) return gs } @@ -164,45 +148,26 @@ func mockGuardianSetToGuardianAddrList(t testing.TB, gs []*mockGuardian) []eth_c return result } -func setTSSEngineAccordingToMockSet(gs []*mockGuardian) { - idToPort := map[string]int{} - idToAddress := map[string]eth_common.Address{} - engines := make([]*tss.Engine, len(gs)) - - for i, g := range gs { - cnfg := g.config - en := g.tssEngine.(*tss.Engine) - - idToPort[en.GuardianStorage.Self.Pid.GetID()] = int(cnfg.tssNetworkPort) - idToAddress[en.GuardianStorage.Self.Pid.GetID()] = g.guardianAddr - - en.GuardianStorage.Self.Port = int(cnfg.tssNetworkPort) - en.GuardianStorage.Self.Hostname = "localhost" - - tmp := eth_common.Address{} - copy(tmp[:], g.guardianAddr[:]) - en.GuardianStorage.Self.VAAv1PubKey = &tmp - - engines[i] = en - } - - // go to each guardian and change the ID of everyone including self. - // for each member, grab its port, then change it in the peer of the guardian. - for _, en := range engines { - for _, id := range en.GuardianStorage.Identities { - id.Hostname = "localhost" - id.Port = idToPort[id.Pid.GetID()] - - tmp := eth_common.Address{} - addr := idToAddress[id.Pid.GetID()] - copy(tmp[:], addr[:]) - - id.VAAv1PubKey = &tmp - } - } - - for _, e := range engines { - e.GuardianStorage.SetInnerFields() +func guardianOptionMockTSS() *GuardianOption { + serviceName := "tss" + return &GuardianOption{ + name: serviceName, + dependencies: nil, // doesn't depend on anything. + f: func(_ context.Context, logger *zap.Logger, g *G) error { + g.tssEngine = tssmock.NewMockSignerConnection() + g.runnables[serviceName] = func(ctx context.Context) error { + supervisor.Signal(ctx, supervisor.SignalHealthy) + for { + select { + case <-ctx.Done(): + return nil + default: + } + time.Sleep(time.Millisecond * 100) + } + } + return nil + }, } } @@ -252,6 +217,7 @@ func mockGuardianRunnable(t testing.TB, gs []*mockGuardian, mockGuardianIndex ui // assemble all the options guardianOptions := []*GuardianOption{ + guardianOptionMockTSS(), GuardianOptionDatabase(db), GuardianOptionWatchers(watcherConfigs, nil), GuardianOptionNoAccountant(), // disable accountant @@ -267,8 +233,6 @@ func mockGuardianRunnable(t testing.TB, gs []*mockGuardian, mockGuardianIndex ui GuardianOptionAlternatePublisher([]byte{}, []string{}), // disable alternate publisher GuardianOptionProcessor(networkID), - GuardianOptionTSSNetwork(), - // Keep this last so that all of its dependencies are met. GuardianOptionP2P(gs[mockGuardianIndex].p2pKey, networkID, bootstrapPeers, nodeName, informOnNewVAAs, false, cfg.p2pPort, "", 0, "", "", false, []string{}, []string{}, []string{}), } @@ -276,7 +240,6 @@ func mockGuardianRunnable(t testing.TB, gs []*mockGuardian, mockGuardianIndex ui guardianNode := NewGuardianNode( env, gs[mockGuardianIndex].guardianSigner, - gs[mockGuardianIndex].tssEngine, ) if err = supervisor.Run(ctx, "g", guardianNode.Run(ctxCancel, guardianOptions...)); err != nil { @@ -942,7 +905,8 @@ func pollApiAndInspectVaa(t *testing.T, ctx context.Context, msg *common.Message if !testCase.prePopulateVAA { // if the VAA is pre-populated with a dummy, then this is expected to fail var verificationPublic vaa.PublicKeys = gsAddrList if testCase.tssVaaVersionChecks { - verificationPublic, err = gs[0].tssEngine.GetPublicKey() + // verificationPublic, err = gs[0].tssEngine.GetPublicKey() + t.FailNow() // TODO: implement TSS public key retrieval in tests assert.NoError(t, err) } @@ -1062,13 +1026,7 @@ func runGuardianConfigTests(t *testing.T, testCases []testCaseGuardianConfig) { ctx, ctxCancel := context.WithCancel(ctx) defer ctxCancel() - guardianTssStorage, err := tss.NewGuardianStorageFromFile(testutils.MustGetMockGuardianTssStorage()) - require.NoError(t, err) - - reliableTss, err := tss.NewReliableTSS(guardianTssStorage) - require.NoError(t, err) - - if err := supervisor.Run(ctx, tc.name, NewGuardianNode(common.GoTest, nil, reliableTss).Run(ctxCancel, tc.opts...)); err != nil { + if err := supervisor.Run(ctx, tc.name, NewGuardianNode(common.GoTest, nil).Run(ctxCancel, tc.opts...)); err != nil { panic(err) } @@ -1412,6 +1370,7 @@ func runConsensusBenchmark(t *testing.B, name string, numGuardians int, numMessa } func TestTssCorrectRun(t *testing.T) { + t.Skip("Need a real TSS signer to be connected to each guardian. This isn't the case yet.") processor.FirstRetryMinWait = time.Second * 3 processor.CleanupInterval = time.Second * 30 diff --git a/node/pkg/node/options.go b/node/pkg/node/options.go index 6d908f4eb9..06d469fd6e 100644 --- a/node/pkg/node/options.go +++ b/node/pkg/node/options.go @@ -2,9 +2,12 @@ package node import ( "context" + "crypto/tls" + "crypto/x509" "errors" "fmt" "net/http" + "os" "time" "github.com/certusone/wormhole/node/pkg/accountant" @@ -20,16 +23,20 @@ import ( "github.com/certusone/wormhole/node/pkg/query" "github.com/certusone/wormhole/node/pkg/readiness" "github.com/certusone/wormhole/node/pkg/supervisor" - tsscomm "github.com/certusone/wormhole/node/pkg/tss/comm" + "github.com/certusone/wormhole/node/pkg/tss" "github.com/certusone/wormhole/node/pkg/watchers" "github.com/certusone/wormhole/node/pkg/watchers/ibc" "github.com/certusone/wormhole/node/pkg/wormconn" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/gorilla/mux" libp2p_crypto "github.com/libp2p/go-libp2p/core/crypto" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/wormhole-foundation/wormhole/sdk/vaa" "go.uber.org/zap" "go.uber.org/zap/zapcore" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" ) type GuardianOption struct { @@ -59,7 +66,7 @@ func GuardianOptionP2P( ) *GuardianOption { return &GuardianOption{ name: "p2p", - dependencies: []string{"accountant", "alternate-publisher", "gateway-relayer", "governor", "query"}, + dependencies: []string{"accountant", "alternate-publisher", "gateway-relayer", "governor", "query", "tss"}, f: func(ctx context.Context, logger *zap.Logger, g *G) error { components := p2p.DefaultComponents() components.Port = port @@ -118,6 +125,7 @@ func GuardianOptionP2P( ccqProtectedPeers, featureFlags, featureFlagFuncs, + g.tssEngine, // as a tss.Gossiper ), ) if err != nil { @@ -633,7 +641,7 @@ func GuardianOptionProcessor(networkId string) *GuardianOption { return &GuardianOption{ name: "processor", // governor, accountant, and notary may be set to nil, but that choice needs to be made before the processor is configured - dependencies: []string{"accountant", "alternate-publisher", "db", "gateway-relayer", "governor", "notary"}, + dependencies: []string{"accountant", "alternate-publisher", "db", "gateway-relayer", "governor", "notary", "tss"}, f: func(ctx context.Context, logger *zap.Logger, g *G) error { @@ -662,18 +670,69 @@ func GuardianOptionProcessor(networkId string) *GuardianOption { }} } -func GuardianOptionTSSNetwork() *GuardianOption { - serviceName := "tsscomm" +func GuardianOptionTSS(selfAddr, leaderAddr ethcommon.Address, configurationsPath, address, x509path, tlsKeyPath string) *GuardianOption { + serviceName := "tss" return &GuardianOption{ name: serviceName, - dependencies: []string{"processor"}, // TODO: I think it is dependant on it, since the TSS passes its signatures to the processor. + dependencies: nil, // doesn't depend on anything. f: func(_ context.Context, logger *zap.Logger, g *G) error { - srvr, err := tsscomm.NewServer(logger.Named(serviceName), g.tssEngine) + + var dialOpts []grpc.DialOption + + if x509path != "" { + if tlsKeyPath == "" { + return fmt.Errorf("tss tls key path must be provided when tls cert path is provided (using mTLS)") + } + certBytes, err := os.ReadFile(x509path) + if err != nil { + return fmt.Errorf("failed to read tss tls certificate: %w", err) + } + + pool := x509.NewCertPool() // pool of accepted server certificates + if ok := pool.AppendCertsFromPEM(certBytes); !ok { + return fmt.Errorf("failed to parse tss tls certificate") + } + + var creds credentials.TransportCredentials + cert, err := tls.LoadX509KeyPair(x509path, tlsKeyPath) + if err != nil { + return fmt.Errorf("failed to load tss tls key pair: %w", err) + } + creds = credentials.NewTLS(&tls.Config{ + Certificates: []tls.Certificate{cert}, // present client certificate to the server. + RootCAs: pool, + MinVersion: tls.VersionTLS13, + }) + logger.Info("loaded tss tls certificate and key", zap.String("cert", x509path), zap.String("key", tlsKeyPath)) + dialOpts = append(dialOpts, grpc.WithTransportCredentials(creds)) + } else { + logger.Warn("no tss tls certificate configured, connecting insecurely") + dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) + } + + var configurations tss.Configurations + if configurationsPath != "" { + if err := configurations.LoadFromFile(configurationsPath); err != nil { + return fmt.Errorf("failed to load tss configurations: %w", err) + } + } + + engine, err := tss.NewSigner(tss.Parameters{ + SocketPath: address, + DialOpts: dialOpts, + LeaderAddress: leaderAddr, + Self: selfAddr, + GST: g.gst, + GuardianSigner: g.guardianSigner, + Configurations: configurations, + }) + if err != nil { - return fmt.Errorf("failed to create tsscomm server: %w", err) + return err } - g.runnables[serviceName] = srvr.Run + g.tssEngine = engine + g.runnables["tss"] = engine.Connect return nil }} diff --git a/node/pkg/node/options_test.go b/node/pkg/node/options_test.go new file mode 100644 index 0000000000..07e1d43613 --- /dev/null +++ b/node/pkg/node/options_test.go @@ -0,0 +1,196 @@ +package node + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "os" + "testing" + "time" + + "github.com/certusone/wormhole/node/pkg/common" + "github.com/certusone/wormhole/node/pkg/guardiansigner" + "github.com/certusone/wormhole/node/pkg/supervisor" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" +) + +func TestGuardianOptionTSS(t *testing.T) { + logger := zap.NewNop() + ctx := context.Background() + + // Create a dummy guardian signer + signer, err := guardiansigner.GenerateSignerWithPrivatekeyUnsafe(nil) + require.NoError(t, err) + + selfAddr := ethcommon.HexToAddress("0x1") + leaderAddr := ethcommon.HexToAddress("0x2") + address := "localhost:1234" + + t.Run("Insecure", func(t *testing.T) { + g := &G{ + gst: common.NewGuardianSetState(nil), + guardianSigner: signer, + runnables: make(map[string]supervisor.Runnable), + } + opt := GuardianOptionTSS(selfAddr, leaderAddr, "", address, "", "") + err := opt.f(ctx, logger, g) + assert.NoError(t, err) + assert.NotNil(t, g.tssEngine) + assert.NotNil(t, g.runnables["tss"]) + }) + + t.Run("MissingKeyPath", func(t *testing.T) { + g := &G{ + gst: common.NewGuardianSetState(nil), + guardianSigner: signer, + runnables: make(map[string]supervisor.Runnable), + } + opt := GuardianOptionTSS(selfAddr, leaderAddr, "", address, "/tmp/cert.pem", "") + err := opt.f(ctx, logger, g) + assert.Error(t, err) + assert.Contains(t, err.Error(), "tss tls key path must be provided") + }) + + t.Run("InvalidCertPath", func(t *testing.T) { + g := &G{ + gst: common.NewGuardianSetState(nil), + guardianSigner: signer, + runnables: make(map[string]supervisor.Runnable), + } + opt := GuardianOptionTSS(selfAddr, leaderAddr, "", address, "/nonexistent/cert.pem", "/nonexistent/key.pem") + err := opt.f(ctx, logger, g) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to read tss tls certificate") + }) + + t.Run("ValidMTLS", func(t *testing.T) { + certPath, keyPath, cleanup := generateTempCertKey(t) + defer cleanup() + + g := &G{ + gst: common.NewGuardianSetState(nil), + guardianSigner: signer, + runnables: make(map[string]supervisor.Runnable), + } + opt := GuardianOptionTSS(selfAddr, leaderAddr, "", address, certPath, keyPath) + err := opt.f(ctx, logger, g) + assert.NoError(t, err) + assert.NotNil(t, g.tssEngine) + assert.NotNil(t, g.runnables["tss"]) + }) + + t.Run("InvalidCertContent", func(t *testing.T) { + f, err := os.CreateTemp("", "garbage-cert") + require.NoError(t, err) + defer os.Remove(f.Name()) + _, err = f.WriteString("garbage") + require.NoError(t, err) + f.Close() + + g := &G{ + gst: common.NewGuardianSetState(nil), + guardianSigner: signer, + runnables: make(map[string]supervisor.Runnable), + } + opt := GuardianOptionTSS(selfAddr, leaderAddr, "", address, f.Name(), "somekey") + err = opt.f(ctx, logger, g) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to parse tss tls certificate") + }) + + t.Run("ValidConfig", func(t *testing.T) { + f, err := os.CreateTemp("", "tss-config-*.json") + require.NoError(t, err) + defer os.Remove(f.Name()) + _, err = f.WriteString(`{"threshold_size": 2}`) + require.NoError(t, err) + f.Close() + + g := &G{ + gst: common.NewGuardianSetState(nil), + guardianSigner: signer, + runnables: make(map[string]supervisor.Runnable), + } + opt := GuardianOptionTSS(selfAddr, leaderAddr, f.Name(), address, "", "") + err = opt.f(ctx, logger, g) + assert.NoError(t, err) + assert.NotNil(t, g.tssEngine) + }) + + t.Run("InvalidConfigPath", func(t *testing.T) { + g := &G{ + gst: common.NewGuardianSetState(nil), + guardianSigner: signer, + runnables: make(map[string]supervisor.Runnable), + } + opt := GuardianOptionTSS(selfAddr, leaderAddr, "/nonexistent/config.json", address, "", "") + err := opt.f(ctx, logger, g) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to load tss configurations") + }) + + t.Run("InvalidConfigContent", func(t *testing.T) { + f, err := os.CreateTemp("", "tss-config-*.json") + require.NoError(t, err) + defer os.Remove(f.Name()) + _, err = f.WriteString(`invalid-json`) + require.NoError(t, err) + f.Close() + + g := &G{ + gst: common.NewGuardianSetState(nil), + guardianSigner: signer, + runnables: make(map[string]supervisor.Runnable), + } + opt := GuardianOptionTSS(selfAddr, leaderAddr, f.Name(), address, "", "") + err = opt.f(ctx, logger, g) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to load tss configurations") + }) +} + +func generateTempCertKey(t *testing.T) (string, string, func()) { + priv, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + Organization: []string{"Test Org"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().Add(time.Hour), + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + BasicConstraintsValid: true, + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + require.NoError(t, err) + + certOut, err := os.CreateTemp("", "cert-*.pem") + require.NoError(t, err) + pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + certOut.Close() + + keyOut, err := os.CreateTemp("", "key-*.pem") + require.NoError(t, err) + privBytes := x509.MarshalPKCS1PrivateKey(priv) + pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: privBytes}) + keyOut.Close() + + cleanup := func() { + os.Remove(certOut.Name()) + os.Remove(keyOut.Name()) + } + + return certOut.Name(), keyOut.Name(), cleanup +} diff --git a/node/pkg/p2p/p2p.go b/node/pkg/p2p/p2p.go index 3a30473480..1357a853a0 100644 --- a/node/pkg/p2p/p2p.go +++ b/node/pkg/p2p/p2p.go @@ -312,6 +312,7 @@ func Run(params *RunParams) func(ctx context.Context) error { p2pMessagesSent.WithLabelValues("control").Add(0) p2pMessagesSent.WithLabelValues("attestation").Add(0) p2pMessagesSent.WithLabelValues("vaa").Add(0) + p2pMessagesSent.WithLabelValues("tss").Add(0) p2pReceiveChannelOverflow.WithLabelValues("observation").Add(0) p2pReceiveChannelOverflow.WithLabelValues("batch_observation").Add(0) p2pReceiveChannelOverflow.WithLabelValues("signed_vaa_with_quorum").Add(0) @@ -378,8 +379,8 @@ func Run(params *RunParams) func(ctx context.Context) error { } // These will only be non-nil if the application plans to listen for or publish to that topic. - var controlPubsubTopic, attestationPubsubTopic, vaaPubsubTopic *pubsub.Topic - var controlSubscription, attestationSubscription, vaaSubscription *pubsub.Subscription + var controlPubsubTopic, attestationPubsubTopic, vaaPubsubTopic, tssPubsubTopic *pubsub.Topic + var controlSubscription, attestationSubscription, vaaSubscription, tssSubscription *pubsub.Subscription // Set up the control channel. //////////////////////////////////////////////////////////////////// if params.nodeName != "" || params.gossipControlSendC != nil || params.obsvReqSendC != nil || params.obsvReqRecvC != nil || params.signedGovCfgRecvC != nil || params.signedGovStatusRecvC != nil || params.gst.IsSubscribedToHeartbeats() { @@ -456,6 +457,30 @@ func Run(params *RunParams) func(ctx context.Context) error { } } + // Set up the TSS networking topics. //////////////////////////////////////////////////////////////////// + if params.tssGossiper != nil { + tssTopic := fmt.Sprintf("%s/%s", params.networkID, "tss") + logger.Info("joining the tss topic", zap.String("topic", tssTopic)) + + tssPubsubTopic, err = ps.Join(tssTopic) + if err != nil { + return fmt.Errorf("failed to join the tss topic: %w", err) + } + + defer func() { + if err := tssPubsubTopic.Close(); err != nil && !errors.Is(err, context.Canceled) { + logger.Error("Error closing the tss topic", zap.Error(err)) + } + }() + + logger.Info("subscribing to the tss topic", zap.String("topic", tssTopic)) + tssSubscription, err = tssPubsubTopic.Subscribe(pubsub.WithBufferSize(P2P_SUBSCRIPTION_BUFFER_SIZE)) + if err != nil { + return fmt.Errorf("failed to subscribe to the tss topic: %w", err) + } + defer tssSubscription.Cancel() + } + // Make sure we connect to at least 1 bootstrap node (this is particularly important in a local devnet and CI // as peer discovery can take a long time). @@ -606,6 +631,11 @@ func Run(params *RunParams) func(ctx context.Context) error { // This routine processes messages received from the internal channels and publishes them to gossip. /////////////////// // NOTE: The go specification says that it is safe to receive on a nil channel, it just blocks forever. go func() { + var tssOutbound <-chan *gossipv1.TSSGossipMessage + if params.tssGossiper != nil { + tssOutbound = params.tssGossiper.Outbound() + } + for { select { case <-ctx.Done(): @@ -680,6 +710,24 @@ func Run(params *RunParams) func(ctx context.Context) error { } else { logger.Info("published signed observation request", zap.Any("signed_observation_request", sReq)) } + case msg := <-tssOutbound: + if tssPubsubTopic == nil { + continue // no control topic: drop TSS gossip messages. + } + + envelope := &gossipv1.GossipMessage{ + Message: &gossipv1.GossipMessage_TssGossipMessage{TssGossipMessage: msg}, + } + + b, err := proto.Marshal(envelope) + if err != nil { + logger.Error("failed to marshal tss gossip message", zap.Error(err)) + } + + p2pMessagesSent.WithLabelValues("tss").Inc() + if err := tssPubsubTopic.Publish(ctx, b); err != nil { + logger.Error("failed to publish tss gossip message", zap.Error(err)) + } } } }() @@ -965,6 +1013,10 @@ func Run(params *RunParams) func(ctx context.Context) error { }() } + if params.tssGossiper != nil && tssSubscription != nil { + tssGossipListener(ctx, tssSubscription, errC, logger, h, params) + } + // Wait for either a shutdown or a fatal error from a pubsub subscription. select { case <-ctx.Done(): @@ -975,6 +1027,54 @@ func Run(params *RunParams) func(ctx context.Context) error { } } +// This method listens for TSS gossip messages and passes them to the TSS gossiper. +func tssGossipListener(ctx context.Context, tssSubscription *pubsub.Subscription, errC chan error, logger *zap.Logger, h host.Host, params *RunParams) { + go func() { + for { + envelope, err := tssSubscription.Next(ctx) // Note: sub.Next(ctx) will return an error once ctx is canceled + if err != nil { + errC <- fmt.Errorf("failed to receive pubsub message on tss topic: %w", err) //nolint:channelcheck // The runnable will exit anyway + return + } + + var msg gossipv1.GossipMessage + err = proto.Unmarshal(envelope.Data, &msg) + if err != nil { + logger.Info("received invalid message on tss topic", + zap.Binary("data", envelope.Data), + zap.String("from", envelope.GetFrom().String()), + ) + p2pMessagesReceived.WithLabelValues("invalid").Inc() + + continue + } + + if envelope.GetFrom() == h.ID() { + p2pMessagesReceived.WithLabelValues("loopback").Inc() + + continue // ignoring loopback messages + } + + m, ok := msg.Message.(*gossipv1.GossipMessage_TssGossipMessage) + if !ok { + p2pMessagesReceived.WithLabelValues("unknown").Inc() + logger.Warn("received unknown message type on tss topic (running outdated software?)", + zap.Any("payload", msg.Message), + zap.Binary("raw", envelope.Data), + zap.String("from", envelope.GetFrom().String()), + ) + + continue + } + + p2pMessagesReceived.WithLabelValues("tss").Inc() + if err := params.tssGossiper.Inform(m.TssGossipMessage); err != nil { + logger.Error("failed to inform tss gossiper", zap.Error(err)) + } + } + }() +} + func createSignedHeartbeat(ctx context.Context, guardianSigner guardiansigner.GuardianSigner, heartbeat *gossipv1.Heartbeat) *gossipv1.SignedHeartbeat { ourAddr := ethcrypto.PubkeyToAddress(guardianSigner.PublicKey(ctx)) diff --git a/node/pkg/p2p/p2p_tss_gossip_test.go b/node/pkg/p2p/p2p_tss_gossip_test.go new file mode 100644 index 0000000000..fb5b5812e4 --- /dev/null +++ b/node/pkg/p2p/p2p_tss_gossip_test.go @@ -0,0 +1,176 @@ +package p2p + +import ( + "context" + "fmt" + "sync" + "testing" + "time" + + "github.com/certusone/wormhole/node/pkg/common" + gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1" + "github.com/ethereum/go-ethereum/crypto" + pubsub "github.com/libp2p/go-libp2p-pubsub" + p2pcrypto "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" + + p2ppeer "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/p2p/net/connmgr" + "github.com/multiformats/go-multiaddr" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "google.golang.org/protobuf/proto" +) + +// Test helpers from watermark_test.go +const LOCAL_P2P_PORTRANGE_START_TSS = 15000 + +type mockTssGossiper struct { + messages chan *gossipv1.TSSGossipMessage + mu sync.Mutex +} + +func newMockTssGossiper() *mockTssGossiper { + return &mockTssGossiper{ + messages: make(chan *gossipv1.TSSGossipMessage, 10), + } +} + +func (m *mockTssGossiper) Inform(msg *gossipv1.TSSGossipMessage) error { + m.mu.Lock() + defer m.mu.Unlock() + m.messages <- msg + return nil +} + +func (m *mockTssGossiper) Outbound() <-chan *gossipv1.TSSGossipMessage { + return nil +} + +func (m *mockTssGossiper) getMessage(t *testing.T) *gossipv1.TSSGossipMessage { + select { + case msg := <-m.messages: + return msg + case <-time.After(5 * time.Second): + t.Fatal("timeout waiting for tss message") + return nil + } +} + +func (m *mockTssGossiper) assertNoMessage(t *testing.T) { + select { + case msg := <-m.messages: + t.Fatalf("received unexpected tss message: %v", msg) + case <-time.After(2 * time.Second): + // OK + } +} + +func TestTssGossipListener(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + // Create a bootstrap node (g0) and a listener node (g1) + var guardianset = &common.GuardianSet{} + var gs [2]*G + for i := range gs { + + gs[i] = NewG(t, fmt.Sprintf("n%d", i)) + gs[i].components.Port = uint(LOCAL_P2P_PORTRANGE_START_TSS + i) // #nosec G115 -- This is safe as the inputs are constants + gs[i].networkID = "/wormhole/localdev" + guardianset.Keys = append(guardianset.Keys, crypto.PubkeyToAddress(gs[i].guardianSigner.PublicKey(ctx))) + // id, err := p2ppeer.IDFromPublicKey(gs[i].priv.GetPublic()) + // require.NoError(t, err) + // protectedPeers = append(protectedPeers, id.String()) // Protect all nodes + + // Set bootstrap to the first node + + gs[i].components.ConnMgr, _ = connmgr.NewConnManager(2, 3, connmgr.WithGracePeriod(2*time.Second)) + + } + + id0, err := p2ppeer.IDFromPublicKey(gs[0].priv.GetPublic()) + require.NoError(t, err) + bootstrapPeer := fmt.Sprintf("/ip4/127.0.0.1/udp/%d/quic-v1/p2p/%s", LOCAL_P2P_PORTRANGE_START_TSS, id0.String()) + + for i := range gs { + gs[i].bootstrapPeers = bootstrapPeer + gs[i].gst.Set(guardianset) + } + + // g0 is just a bootstrap node, no tss gossiper + startGuardian(t, ctx, gs[0], []string{}) + + // g1 is the listener with a mock tss gossiper + mockGossiper := newMockTssGossiper() + gs[1].tssGossiper = mockGossiper + startGuardian(t, ctx, gs[1], []string{}) + + // Wait for nodes to connect + time.Sleep(5 * time.Second) + + // Create a publisher host + p2ppriv, _, err := p2pcrypto.GenerateKeyPair(p2pcrypto.Ed25519, -1) + require.NoError(t, err) + publisher, err := NewHost(zap.NewNop(), ctx, gs[0].networkID, bootstrapPeer, DefaultComponents(), p2ppriv) + require.NoError(t, err) + defer publisher.Close() + + // Connect publisher to bootstrap + bootstrapMultiAddr, err := multiaddr.NewMultiaddr(bootstrapPeer) + require.NoError(t, err) + bootstrapAddrInfo, err := peer.AddrInfoFromP2pAddr(bootstrapMultiAddr) + require.NoError(t, err) + err = publisher.Connect(ctx, *bootstrapAddrInfo) + require.NoError(t, err) + + ps, err := pubsub.NewGossipSub(ctx, publisher) + require.NoError(t, err) + + tssTopicName := fmt.Sprintf("%s/%s", gs[0].networkID, "tss") + topic, err := ps.Join(tssTopicName) + require.NoError(t, err) + // Give pubsub time to propagate subscriptions + time.Sleep(2 * time.Second) + + t.Run("ValidMessage", func(t *testing.T) { + tssMsg := &gossipv1.TSSGossipMessage{ + Message: []byte("hello"), + Signature: []byte("sig"), + } + gossipMsg := &gossipv1.GossipMessage{ + Message: &gossipv1.GossipMessage_TssGossipMessage{ + TssGossipMessage: tssMsg, + }, + } + msgBytes, err := proto.Marshal(gossipMsg) + require.NoError(t, err) + + err = topic.Publish(ctx, msgBytes) + require.NoError(t, err) + + received := mockGossiper.getMessage(t) + assert.True(t, proto.Equal(tssMsg, received)) + }) + + t.Run("InvalidMessage", func(t *testing.T) { + err = topic.Publish(ctx, []byte("this is not a protobuf")) + require.NoError(t, err) + mockGossiper.assertNoMessage(t) + }) + + t.Run("UnknownMessageType", func(t *testing.T) { + gossipMsg := &gossipv1.GossipMessage{ + Message: &gossipv1.GossipMessage_SignedHeartbeat{ + SignedHeartbeat: &gossipv1.SignedHeartbeat{}, + }, + } + msgBytes, err := proto.Marshal(gossipMsg) + require.NoError(t, err) + + err = topic.Publish(ctx, msgBytes) + require.NoError(t, err) + mockGossiper.assertNoMessage(t) + }) +} diff --git a/node/pkg/p2p/run_params.go b/node/pkg/p2p/run_params.go index e2a81c1ded..bb0e8e8ce7 100644 --- a/node/pkg/p2p/run_params.go +++ b/node/pkg/p2p/run_params.go @@ -10,6 +10,7 @@ import ( "github.com/certusone/wormhole/node/pkg/guardiansigner" gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1" "github.com/certusone/wormhole/node/pkg/query" + "github.com/certusone/wormhole/node/pkg/tss" "github.com/libp2p/go-libp2p/core/crypto" ) @@ -61,6 +62,8 @@ type ( ccqProtectedPeers []string featureFlags []string featureFlagFuncs []func() string + + tssGossiper tss.Gossiper } // RunOpt is used to specify optional parameters. @@ -196,6 +199,7 @@ func WithGuardianOptions( ccqProtectedPeers []string, featureFlags []string, featureFlagFuncs []func() string, + tssGossiper tss.Gossiper, ) RunOpt { return func(p *RunParams) error { p.nodeName = nodeName @@ -221,6 +225,7 @@ func WithGuardianOptions( p.ccqProtectedPeers = ccqProtectedPeers p.featureFlags = featureFlags p.featureFlagFuncs = featureFlagFuncs + p.tssGossiper = tssGossiper return nil } } diff --git a/node/pkg/p2p/run_params_test.go b/node/pkg/p2p/run_params_test.go index cd9f6df835..28d30ee15d 100644 --- a/node/pkg/p2p/run_params_test.go +++ b/node/pkg/p2p/run_params_test.go @@ -242,6 +242,7 @@ func TestRunParamsWithGuardianOptions(t *testing.T) { ccqProtectedPeers, []string{}, // featureFlags []func() string{}, // featureFlagFuncs + nil, // tssGossiper ), ) diff --git a/node/pkg/p2p/watermark_test.go b/node/pkg/p2p/watermark_test.go index 39e5434aaf..3e8290b842 100644 --- a/node/pkg/p2p/watermark_test.go +++ b/node/pkg/p2p/watermark_test.go @@ -15,6 +15,7 @@ import ( "github.com/certusone/wormhole/node/pkg/guardiansigner" gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1" "github.com/certusone/wormhole/node/pkg/supervisor" + "github.com/certusone/wormhole/node/pkg/tss" "github.com/ethereum/go-ethereum/crypto" p2pcrypto "github.com/libp2p/go-libp2p/core/crypto" p2ppeer "github.com/libp2p/go-libp2p/core/peer" @@ -46,6 +47,7 @@ type G struct { signedGovCfg chan *gossipv1.SignedChainGovernorConfig signedGovSt chan *gossipv1.SignedChainGovernorStatus components *Components + tssGossiper tss.Gossiper } func NewG(t *testing.T, nodeName string) *G { @@ -292,6 +294,7 @@ func startGuardian(t *testing.T, ctx context.Context, g *G, protectedPeers []str []string{}, // ccq protected peers []string{}, // featureFlags []func() string{}, // featureFlagFuncs + g.tssGossiper, // tssGossiper )) require.NoError(t, err) diff --git a/node/pkg/processor/benchmark_test.go b/node/pkg/processor/benchmark_test.go index a985f3f1af..eed8a1359a 100644 --- a/node/pkg/processor/benchmark_test.go +++ b/node/pkg/processor/benchmark_test.go @@ -58,7 +58,7 @@ func BenchmarkHandleObservation(b *testing.B) { for guardianIdx := 1; guardianIdx < 19; guardianIdx++ { start := time.Now() - p.handleSingleObservation(pd.guardianAddrs[guardianIdx], pd.createObservation(b, guardianIdx, k)) + p.handleSingleObservation(ctx, pd.guardianAddrs[guardianIdx], pd.createObservation(b, guardianIdx, k)) duration := time.Since(start) totalCount++ totalTime += duration @@ -111,7 +111,7 @@ func BenchmarkProfileHandleObservation(b *testing.B) { p.handleMessage(ctx, k) for guardianIdx := 1; guardianIdx < 19; guardianIdx++ { - p.handleSingleObservation(pd.guardianAddrs[guardianIdx], pd.createObservation(b, guardianIdx, k)) + p.handleSingleObservation(ctx, pd.guardianAddrs[guardianIdx], pd.createObservation(b, guardianIdx, k)) } } require.Equal(b, NumObservations, len(pd.gossipVaaSendC)) diff --git a/node/pkg/processor/cleanup.go b/node/pkg/processor/cleanup.go index 2005ce51cc..7dbc3b3c9f 100644 --- a/node/pkg/processor/cleanup.go +++ b/node/pkg/processor/cleanup.go @@ -276,7 +276,7 @@ func (p *Processor) handleCleanup(ctx context.Context) { // Clean up tss signatures that are waited upon. now := time.Now() - maxSigWaitTime := p.thresholdSigner.MaxTTL() + maxSigWaitTime := time.Minute * 10 // TODO: make configurable // TODO: We can increase efficiency by using a heap / queue and removing from the map the top elements. for vaaDigest, waitingSig := range p.tssWaiters { diff --git a/node/pkg/processor/message.go b/node/pkg/processor/message.go index 1e28197e1f..27f5454f00 100644 --- a/node/pkg/processor/message.go +++ b/node/pkg/processor/message.go @@ -6,6 +6,7 @@ import ( "time" "github.com/mr-tron/base58" + "github.com/xlabs/tss-common/service/signer" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -65,9 +66,8 @@ func (p *Processor) handleMessage(ctx context.Context, k *common.MessagePublicat Reobservation: k.IsReobservation, } - vCpy := *v - p.tssSetup(&vCpy) - // Generate digest of the unsigned VAA. + vShallowCopy := *v + p.tssSign(&vShallowCopy) digest := v.SigningDigest() hash := hex.EncodeToString(digest.Bytes()) @@ -127,12 +127,12 @@ func (p *Processor) handleMessage(ctx context.Context, k *common.MessagePublicat // Fast path for our own signature. if !s.submitted { start := time.Now() - p.checkForQuorum(ourObs, s, s.gs, hash) + p.checkForQuorum(ctx, ourObs, s, s.gs, hash) timeToHandleObservation.Observe(float64(time.Since(start).Microseconds())) } } -func (p *Processor) tssSetup(v *VAA) { +func (p *Processor) tssSign(v *VAA) { v.Version = vaa.TSSVaaVersion digest := v.SigningDigest() hash := hex.EncodeToString(digest.Bytes()) @@ -142,5 +142,15 @@ func (p *Processor) tssSetup(v *VAA) { startTime: time.Now(), } - p.thresholdSigner.BeginAsyncThresholdSigningProtocol(digest.Bytes(), v.EmitterChain, v.ConsistencyLevel) + err := p.thresholdSigner.AsyncSign(&signer.SignRequest{ + Digest: v.SigningDigest().Bytes(), + Protocol: p.thresholdSigner.GetProtocol(int(v.EmitterChain)).ToString(), + }) + + if err != nil { + p.logger.Error("failed to request TSS signature", + zap.String("hash", hash), + zap.Error(err), + ) + } } diff --git a/node/pkg/processor/observation.go b/node/pkg/processor/observation.go index f7ba1174cc..9bc9521afb 100644 --- a/node/pkg/processor/observation.go +++ b/node/pkg/processor/observation.go @@ -1,6 +1,7 @@ package processor import ( + "context" "encoding/hex" "fmt" "math" @@ -73,15 +74,15 @@ func signaturesToVaaFormat(signatures map[common.Address][]byte, gsKeys []common } // handleBatchObservation processes a batch of remote VAA observations. -func (p *Processor) handleBatchObservation(m *node_common.MsgWithTimeStamp[gossipv1.SignedObservationBatch]) { +func (p *Processor) handleBatchObservation(ctx context.Context, m *node_common.MsgWithTimeStamp[gossipv1.SignedObservationBatch]) { for _, obs := range m.Msg.Observations { - p.handleSingleObservation(m.Msg.Addr, obs) + p.handleSingleObservation(ctx, m.Msg.Addr, obs) } batchObservationTotalDelay.Observe(float64(time.Since(m.Timestamp).Microseconds())) } // handleObservation processes a remote VAA observation, verifies it, checks whether the VAA has met quorum, and assembles and submits a valid VAA if possible. -func (p *Processor) handleSingleObservation(addr []byte, m *gossipv1.Observation) { +func (p *Processor) handleSingleObservation(ctx context.Context, addr []byte, m *gossipv1.Observation) { // SECURITY: at this point, observations received from the p2p network are fully untrusted (all fields!) // // Note that observations are never tied to the (verified) p2p identity key - the p2p network @@ -222,7 +223,7 @@ func (p *Processor) handleSingleObservation(addr []byte, m *gossipv1.Observation s.signatures[their_addr] = m.Signature if s.ourObservation != nil { - p.checkForQuorum(m, s, gs, hash) + p.checkForQuorum(ctx, m, s, gs, hash) } else { if p.logger.Level().Enabled(zapcore.DebugLevel) { p.logger.Debug("we have not yet seen this observation yet", @@ -238,7 +239,7 @@ func (p *Processor) handleSingleObservation(addr []byte, m *gossipv1.Observation // checkForQuorum checks for quorum after a valid signature has been added to the observation state. If quorum is met, it broadcasts the signed VAA. This function // is called both for local and external observations. It assumes we that we have made the observation ourselves but have not already submitted the VAA. -func (p *Processor) checkForQuorum(m *gossipv1.Observation, s *state, gs *node_common.GuardianSet, hash string) { +func (p *Processor) checkForQuorum(ctx context.Context, m *gossipv1.Observation, s *state, gs *node_common.GuardianSet, hash string) { // Check if we have more signatures than required for quorum. // s.signatures may contain signatures from multiple guardian sets during guardian set updates // Hence, if len(s.signatures) < quorum, then there is definitely no quorum and we can return early to save additional computation, @@ -283,13 +284,13 @@ func (p *Processor) checkForQuorum(m *gossipv1.Observation, s *state, gs *node_c // We have reached quorum *with the active guardian set*. start := time.Now() - s.ourObservation.HandleQuorum(sigsVaaFormat, hash, p) + s.ourObservation.HandleQuorum(ctx, sigsVaaFormat, hash, p) s.submitted = true timeToHandleQuorum.Observe(float64(time.Since(start).Microseconds())) } // handleInboundSignedVAAWithQuorum takes a VAA received from the network. If we have not already seen it and it is valid, we store it in the database. -func (p *Processor) handleInboundSignedVAAWithQuorum(m *gossipv1.SignedVAAWithQuorum) { +func (p *Processor) handleInboundSignedVAAWithQuorum(ctx context.Context, m *gossipv1.SignedVAAWithQuorum) { v, err := vaa.Unmarshal(m.Vaa) if err != nil { p.logger.Warn("received invalid VAA in SignedVAAWithQuorum message", @@ -303,7 +304,10 @@ func (p *Processor) handleInboundSignedVAAWithQuorum(m *gossipv1.SignedVAAWithQu // // in case the leader has a VaaV2, it doesn't need to start a new signing process. if !p.haveSignedVaav2(*db.VaaIDFromVAA(v)) { - p.thresholdSigner.WitnessNewVaa(v) + if err := p.thresholdSigner.WitnessNewVaaV1(ctx, v); err != nil { + p.logger.Warn("witnessing new VAA v1 for TSS signing failed", + zap.Error(err), zap.Any("message", m)) + } } } @@ -340,7 +344,8 @@ func (p *Processor) handleInboundSignedVAAWithQuorum(m *gossipv1.SignedVAAWithQu var verificationPublic vaa.PublicKeys = keys if v.Version == vaa.TSSVaaVersion { - verificationPublic, err = p.thresholdSigner.GetPublicKey() + protocol := p.thresholdSigner.GetProtocol(int(v.EmitterChain)) + pb, err := p.thresholdSigner.GetPublicKey(ctx, protocol) if err != nil { p.logger.Warn("dropping SignedVAAWithQuorum message since we failed to get public key for TSS VAA", zap.String("message_id", v.MessageID()), @@ -349,6 +354,8 @@ func (p *Processor) handleInboundSignedVAAWithQuorum(m *gossipv1.SignedVAAWithQu ) return } + + verificationPublic = pb } if err := v.Verify(verificationPublic); err != nil { diff --git a/node/pkg/processor/observation_test.go b/node/pkg/processor/observation_test.go index 16b320f099..931bfb2760 100644 --- a/node/pkg/processor/observation_test.go +++ b/node/pkg/processor/observation_test.go @@ -1,6 +1,7 @@ package processor import ( + "context" "crypto/ecdsa" "crypto/rand" "testing" @@ -8,7 +9,7 @@ import ( "github.com/certusone/wormhole/node/pkg/common" gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1" - "github.com/certusone/wormhole/node/pkg/tss" + tssmock "github.com/certusone/wormhole/node/pkg/tss/mock" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/assert" @@ -44,10 +45,10 @@ func TestHandleInboundSignedVAAWithQuorum_NilGuardianSet(t *testing.T) { observedLogger := zap.New(observedZapCore) signedVAAWithQuorum := &gossipv1.SignedVAAWithQuorum{Vaa: marshalVAA} - processor := Processor{thresholdSigner: &tss.Engine{}} // added a thresholdSigner to avoid nil pointer + processor := Processor{thresholdSigner: tssmock.NewMockSignerConnection()} // added a thresholdSigner to avoid nil pointer processor.logger = observedLogger - processor.handleInboundSignedVAAWithQuorum(signedVAAWithQuorum) + processor.handleInboundSignedVAAWithQuorum(context.Background(), signedVAAWithQuorum) // Check to see if we got an error, which we should have, // because a `gs` is not defined on processor @@ -106,11 +107,11 @@ func TestHandleInboundSignedVAAWithQuorum(t *testing.T) { observedLogger := zap.New(observedZapCore) signedVAAWithQuorum := &gossipv1.SignedVAAWithQuorum{Vaa: marshalVAA} - processor := Processor{thresholdSigner: &tss.Engine{}} // added a thresholdSigner to avoid nil pointer + processor := Processor{thresholdSigner: tssmock.NewMockSignerConnection()} // added a thresholdSigner to avoid nil pointer processor.gs = &guardianSet processor.logger = observedLogger - processor.handleInboundSignedVAAWithQuorum(signedVAAWithQuorum) + processor.handleInboundSignedVAAWithQuorum(context.Background(), signedVAAWithQuorum) // Check to see if we got an error, which we should have assert.Equal(t, 1, observedLogs.Len()) diff --git a/node/pkg/processor/processor.go b/node/pkg/processor/processor.go index a123d2408c..36275765e6 100644 --- a/node/pkg/processor/processor.go +++ b/node/pkg/processor/processor.go @@ -15,6 +15,7 @@ import ( guardianNotary "github.com/certusone/wormhole/node/pkg/notary" "github.com/certusone/wormhole/node/pkg/p2p" "github.com/certusone/wormhole/node/pkg/tss" + "google.golang.org/grpc/codes" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -28,6 +29,7 @@ import ( "github.com/wormhole-foundation/wormhole/sdk/vaa" "github.com/xlabs/multi-party-sig/protocols/frost" tsscommon "github.com/xlabs/tss-common" + "github.com/xlabs/tss-common/service/signer" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -52,7 +54,7 @@ type ( IsReobservation() bool // HandleQuorum finishes processing the observation once a quorum of signatures have // been received for it. - HandleQuorum(sigs []*vaa.Signature, hash string, p *Processor) + HandleQuorum(ctx context.Context, sigs []*vaa.Signature, hash string, p *Processor) } // state represents the local view of a given observation @@ -389,16 +391,16 @@ func (p *Processor) Run(ctx context.Context) error { } p.handleMessage(ctx, k) - case sig := <-p.thresholdSigner.ProducedSignature(): - p.processTssSignature(sig) + case tssResp := <-p.thresholdSigner.Response(): + p.handleTssResponse(ctx, tssResp) case m := <-p.batchObsvC: batchObservationChanDelay.Observe(float64(time.Since(m.Timestamp).Microseconds())) - p.handleBatchObservation(m) + p.handleBatchObservation(ctx, m) case m := <-p.batchObsvC: batchObservationChanDelay.Observe(float64(time.Since(m.Timestamp).Microseconds())) - p.handleBatchObservation(m) + p.handleBatchObservation(ctx, m) case m := <-p.signedInC: - p.handleInboundSignedVAAWithQuorum(m) + p.handleInboundSignedVAAWithQuorum(ctx, m) case <-cleanup.C: p.handleCleanup(ctx) case <-pollTimer.C: @@ -478,6 +480,49 @@ func (p *Processor) Run(ctx context.Context) error { } } +func (p *Processor) handleTssResponse(ctx context.Context, tssResp *signer.SignResponse) { + if tssResp == nil || tssResp.Response == nil { + p.logger.Error("received nil TSS signer response") + return + } + + switch v := tssResp.Response.(type) { + case *signer.SignResponse_Signature: + if v.Signature == nil { + p.logger.Error("received nil TSS signer signature") + return + } + + p.processTssSignature(ctx, v.Signature) + case *signer.SignResponse_Status: + if v.Status == nil { + p.logger.Error("received nil TSS signer status") + return + } + p.processTssStatus(v.Status) + default: + p.logger.Error("received unknown TSS signer response type") + } +} + +func (p *Processor) processTssStatus(status *signer.SignStatus) { + if status == nil || len(status.GetDigest()) == 0 || status.GetCode() == 0 { + p.logger.Error("received nil TSS signer status") + return + } + hash := hex.EncodeToString(status.GetDigest()) + delete(p.tssWaiters, hash) + + p.logger.Info("TSS signing request stopped", + zap.String("hash", hash), + zap.String("status", status.GetProtocol()), + zap.String("code", codes.Code(status.GetCode()).String()), + zap.String("message", status.GetMessage()), + ) + + // TODO: consider additional handling based on status code +} + // storeSignedVAA schedules a database update for a VAA. func (p *Processor) storeSignedVAA(v *vaa.VAA) { key := string(db.VaaIDFromVAA(v).Bytes()) @@ -589,7 +634,7 @@ func (p *Processor) vaaWriter(ctx context.Context) error { } } -func (p *Processor) processTssSignature(sig *tsscommon.SignatureData) { +func (p *Processor) processTssSignature(ctx context.Context, sig *tsscommon.SignatureData) { if sig == nil { return } @@ -621,18 +666,24 @@ func (p *Processor) processTssSignature(sig *tsscommon.SignatureData) { return } - sigBytes, err := frostsig.MarshalBinary() + csig, err := frostsig.ToContractSig() if err != nil { - p.logger.Error("failed to convert TSS signature to bytes", zap.String("hash", hash), zap.Error(err)) + p.logger.Error("failed to convert TSS signature to contractForm", zap.String("hash", hash), zap.Error(err)) return } + sigBytes, err := csig.MarshalBinary() + if err != nil { + p.logger.Error("failed to marshal contract signature", zap.String("hash", hash), zap.Error(err)) + return + } + vaaSig := &vaa.Signature{} copy(vaaSig.Signature[:], sigBytes) // using single signature, since it was reached via threshold signing. - wtr.vaa.HandleQuorum([]*vaa.Signature{vaaSig}, hash, p) + wtr.vaa.HandleQuorum(ctx, []*vaa.Signature{vaaSig}, hash, p) } // GetFeatures returns the processor feature string that can be published in heartbeat messages. diff --git a/node/pkg/processor/processor_test.go b/node/pkg/processor/processor_test.go new file mode 100644 index 0000000000..02d45dcaf0 --- /dev/null +++ b/node/pkg/processor/processor_test.go @@ -0,0 +1,242 @@ +package processor + +import ( + "context" + "encoding/hex" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/wormhole-foundation/wormhole/sdk/vaa" + tsscommon "github.com/xlabs/tss-common" + "github.com/xlabs/tss-common/service/signer" + "go.uber.org/zap" + "go.uber.org/zap/zaptest/observer" + + tssmock "github.com/certusone/wormhole/node/pkg/tss/mock" +) + +func TestHandleTssResponse(t *testing.T) { + // Setup logger observer + observedZapCore, observedLogs := observer.New(zap.InfoLevel) + observedLogger := zap.New(observedZapCore) + + // Helper to create processor + createProcessor := func() *Processor { + return &Processor{ + logger: observedLogger, + tssWaiters: make(map[string]timedThresholdSignatureWaiter), + updatedVAAs: make(map[string]*updateVaaEntry), + gossipVaaSendC: make(chan []byte, 10), + thresholdSigner: tssmock.NewMockSigner(), + pythnetVaas: make(map[string]PythNetVaaEntry), + } + } + + t.Run("NilResponse", func(t *testing.T) { + p := createProcessor() + p.handleTssResponse(context.Background(), nil) + assert.Equal(t, 1, observedLogs.FilterMessage("received nil TSS signer response").Len()) + observedLogs.TakeAll() + }) + + t.Run("NilInnerResponse", func(t *testing.T) { + p := createProcessor() + p.handleTssResponse(context.Background(), &signer.SignResponse{Response: nil}) + assert.Equal(t, 1, observedLogs.FilterMessage("received nil TSS signer response").Len()) + observedLogs.TakeAll() + }) + + t.Run("NilSignature", func(t *testing.T) { + p := createProcessor() + resp := &signer.SignResponse{ + Response: &signer.SignResponse_Signature{ + Signature: nil, + }, + } + p.handleTssResponse(context.Background(), resp) + assert.Equal(t, 1, observedLogs.FilterMessage("received nil TSS signer signature").Len()) + observedLogs.TakeAll() + }) + + t.Run("NilStatus", func(t *testing.T) { + p := createProcessor() + resp := &signer.SignResponse{ + Response: &signer.SignResponse_Status{ + Status: nil, + }, + } + p.handleTssResponse(context.Background(), resp) + assert.Equal(t, 1, observedLogs.FilterMessage("received nil TSS signer status").Len()) + observedLogs.TakeAll() + }) + + t.Run("ProcessStatus", func(t *testing.T) { + p := createProcessor() + digest := []byte("digest") + hash := hex.EncodeToString(digest) + + // Add to waiters + p.tssWaiters[hash] = timedThresholdSignatureWaiter{ + startTime: time.Now(), + vaa: &VAA{}, + } + + resp := &signer.SignResponse{ + Response: &signer.SignResponse_Status{ + Status: &signer.SignStatus{ + Digest: digest, + Code: 1, // Error code + Message: "failed", + Protocol: "frost", + }, + }, + } + + p.handleTssResponse(context.Background(), resp) + + // Should be removed from waiters + _, exists := p.tssWaiters[hash] + assert.False(t, exists) + assert.Equal(t, 1, observedLogs.FilterMessage("TSS signing request stopped").Len()) + observedLogs.TakeAll() + }) + + t.Run("ProcessStatus_NilDigest", func(t *testing.T) { + p := createProcessor() + resp := &signer.SignResponse{ + Response: &signer.SignResponse_Status{ + Status: &signer.SignStatus{ + Digest: nil, + }, + }, + } + p.handleTssResponse(context.Background(), resp) + assert.Equal(t, 1, observedLogs.FilterMessage("received nil TSS signer status").Len()) + observedLogs.TakeAll() + }) + + t.Run("ProcessStatus_ZeroCode", func(t *testing.T) { + p := createProcessor() + resp := &signer.SignResponse{ + Response: &signer.SignResponse_Status{ + Status: &signer.SignStatus{ + Digest: []byte("digest"), + Code: 0, + }, + }, + } + p.handleTssResponse(context.Background(), resp) + assert.Equal(t, 1, observedLogs.FilterMessage("received nil TSS signer status").Len()) + observedLogs.TakeAll() + }) + + t.Run("ProcessSignature_NilS", func(t *testing.T) { + p := createProcessor() + resp := &signer.SignResponse{ + Response: &signer.SignResponse_Signature{ + Signature: &tsscommon.SignatureData{ + M: []byte("digest"), + S: nil, + }, + }, + } + p.handleTssResponse(context.Background(), resp) + assert.Equal(t, 1, observedLogs.FilterMessage("received TSS signature with nil signature").Len()) + observedLogs.TakeAll() + }) + + t.Run("ProcessSignature_NilM", func(t *testing.T) { + p := createProcessor() + resp := &signer.SignResponse{ + Response: &signer.SignResponse_Signature{ + Signature: &tsscommon.SignatureData{ + M: nil, + S: []byte("sig"), + }, + }, + } + p.handleTssResponse(context.Background(), resp) + assert.Equal(t, 1, observedLogs.FilterMessage("received TSS signature with nil message").Len()) + observedLogs.TakeAll() + }) + + t.Run("ProcessSignature_UnknownVAA", func(t *testing.T) { + p := createProcessor() + resp := &signer.SignResponse{ + Response: &signer.SignResponse_Signature{ + Signature: &tsscommon.SignatureData{ + M: []byte("unknown"), + S: []byte("sig"), + }, + }, + } + p.handleTssResponse(context.Background(), resp) + assert.Equal(t, 1, observedLogs.FilterMessage("received TSS signature for unknown VAA").Len()) + observedLogs.TakeAll() + }) + + t.Run("ProcessSignature_InvalidSig", func(t *testing.T) { + p := createProcessor() + digest := []byte("digest_for_sig") + hash := hex.EncodeToString(digest) + + v := &VAA{ + VAA: vaa.VAA{ + EmitterChain: vaa.ChainIDSolana, + }, + } + + p.tssWaiters[hash] = timedThresholdSignatureWaiter{ + startTime: time.Now(), + vaa: v, + } + + resp := &signer.SignResponse{ + Response: &signer.SignResponse_Signature{ + Signature: &tsscommon.SignatureData{ + M: digest, + S: []byte("invalid_frost_sig"), + }, + }, + } + p.handleTssResponse(context.Background(), resp) + assert.Equal(t, 1, observedLogs.FilterMessage("failed to translate TSS signature").Len()) + observedLogs.TakeAll() + }) + + t.Run("ProcessSignature_Success", func(t *testing.T) { + p := createProcessor() + digest := []byte("digest_for_sig") + hash := hex.EncodeToString(digest) + + v := &VAA{ + VAA: vaa.VAA{ + EmitterChain: vaa.ChainIDSolana, + }, + } + + p.tssWaiters[hash] = timedThresholdSignatureWaiter{ + startTime: time.Now(), + vaa: v, + } + sigResp := tssmock.NewMockSigner().Sign(digest, tsscommon.ProtocolFROSTSign) // Pre-initialize mock signer + + // Construct a dummy frost signature + + // Mock the witness call on the signer + + p.handleTssResponse(context.Background(), sigResp) + + // Check logs for success message from HandleQuorum + assert.Equal(t, 1, observedLogs.FilterMessage("signed VAA with quorum").Len()) + + // Verify VAA was stored + p.updateVAALock.Lock() + + assert.NotEmpty(t, p.updatedVAAs) + p.updateVAALock.Unlock() + + observedLogs.TakeAll() + }) +} diff --git a/node/pkg/processor/vaa.go b/node/pkg/processor/vaa.go index c321ca5dd2..0e9df983a1 100644 --- a/node/pkg/processor/vaa.go +++ b/node/pkg/processor/vaa.go @@ -1,6 +1,8 @@ package processor import ( + "context" + "github.com/wormhole-foundation/wormhole/sdk/vaa" "go.uber.org/zap" ) @@ -12,7 +14,7 @@ type VAA struct { } // HandleQuorum is called when a VAA reaches quorum. It publishes the VAA to the gossip network and stores it in the database. -func (v *VAA) HandleQuorum(sigs []*vaa.Signature, hash string, p *Processor) { +func (v *VAA) HandleQuorum(ctx context.Context, sigs []*vaa.Signature, hash string, p *Processor) { // Deep copy the observation and add signatures signed := &vaa.VAA{ Version: v.Version, @@ -36,7 +38,11 @@ func (v *VAA) HandleQuorum(sigs []*vaa.Signature, hash string, p *Processor) { // Broadcast the VAA and store it in the database. p.broadcastSignedVAA(signed) p.storeSignedVAA(signed) - p.thresholdSigner.WitnessNewVaa(signed) + + if err := p.thresholdSigner.WitnessNewVaaV1(ctx, signed); err != nil { + p.logger.Warn("witnessing new VAA v1 for TSS signing failed", + zap.Error(err), zap.Any("message", signed)) + } } func (v *VAA) IsReliable() bool { diff --git a/node/pkg/proto/gossip/v1/gossip.pb.go b/node/pkg/proto/gossip/v1/gossip.pb.go index d3b19d9b34..989d36559e 100644 --- a/node/pkg/proto/gossip/v1/gossip.pb.go +++ b/node/pkg/proto/gossip/v1/gossip.pb.go @@ -35,6 +35,7 @@ type GossipMessage struct { // *GossipMessage_SignedQueryRequest // *GossipMessage_SignedQueryResponse // *GossipMessage_SignedObservationBatch + // *GossipMessage_TssGossipMessage Message isGossipMessage_Message `protobuf_oneof:"message"` } @@ -133,6 +134,13 @@ func (x *GossipMessage) GetSignedObservationBatch() *SignedObservationBatch { return nil } +func (x *GossipMessage) GetTssGossipMessage() *TSSGossipMessage { + if x, ok := x.GetMessage().(*GossipMessage_TssGossipMessage); ok { + return x.TssGossipMessage + } + return nil +} + type isGossipMessage_Message interface { isGossipMessage_Message() } @@ -170,6 +178,10 @@ type GossipMessage_SignedObservationBatch struct { SignedObservationBatch *SignedObservationBatch `protobuf:"bytes,12,opt,name=signed_observation_batch,json=signedObservationBatch,proto3,oneof"` } +type GossipMessage_TssGossipMessage struct { + TssGossipMessage *TSSGossipMessage `protobuf:"bytes,13,opt,name=tss_gossip_message,json=tssGossipMessage,proto3,oneof"` +} + func (*GossipMessage_SignedHeartbeat) isGossipMessage_Message() {} func (*GossipMessage_SignedVaaWithQuorum) isGossipMessage_Message() {} @@ -186,6 +198,68 @@ func (*GossipMessage_SignedQueryResponse) isGossipMessage_Message() {} func (*GossipMessage_SignedObservationBatch) isGossipMessage_Message() {} +func (*GossipMessage_TssGossipMessage) isGossipMessage_Message() {} + +// TSSGossipMessage is used to forward different TSS messages between nodes. +// For instance: in the TSS protocol we incorporated a leader-based protocol where a specific guardian +// can forward a VAAv1 with a specific committee it wants others to participate in. +type TSSGossipMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // currently marshalled VAAv1. Can later be other items signers wish to gossip about. + Message []byte `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + // ECDSA signature using the node's guardian public key. + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` +} + +func (x *TSSGossipMessage) Reset() { + *x = TSSGossipMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_gossip_v1_gossip_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TSSGossipMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TSSGossipMessage) ProtoMessage() {} + +func (x *TSSGossipMessage) ProtoReflect() protoreflect.Message { + mi := &file_gossip_v1_gossip_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TSSGossipMessage.ProtoReflect.Descriptor instead. +func (*TSSGossipMessage) Descriptor() ([]byte, []int) { + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{1} +} + +func (x *TSSGossipMessage) GetMessage() []byte { + if x != nil { + return x.Message + } + return nil +} + +func (x *TSSGossipMessage) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + type SignedHeartbeat struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -204,7 +278,7 @@ type SignedHeartbeat struct { func (x *SignedHeartbeat) Reset() { *x = SignedHeartbeat{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[1] + mi := &file_gossip_v1_gossip_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -217,7 +291,7 @@ func (x *SignedHeartbeat) String() string { func (*SignedHeartbeat) ProtoMessage() {} func (x *SignedHeartbeat) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[1] + mi := &file_gossip_v1_gossip_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -230,7 +304,7 @@ func (x *SignedHeartbeat) ProtoReflect() protoreflect.Message { // Deprecated: Use SignedHeartbeat.ProtoReflect.Descriptor instead. func (*SignedHeartbeat) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{1} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{2} } func (x *SignedHeartbeat) GetHeartbeat() []byte { @@ -282,7 +356,7 @@ type Heartbeat struct { func (x *Heartbeat) Reset() { *x = Heartbeat{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[2] + mi := &file_gossip_v1_gossip_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -295,7 +369,7 @@ func (x *Heartbeat) String() string { func (*Heartbeat) ProtoMessage() {} func (x *Heartbeat) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[2] + mi := &file_gossip_v1_gossip_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -308,7 +382,7 @@ func (x *Heartbeat) ProtoReflect() protoreflect.Message { // Deprecated: Use Heartbeat.ProtoReflect.Descriptor instead. func (*Heartbeat) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{2} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{3} } func (x *Heartbeat) GetNodeName() string { @@ -388,7 +462,7 @@ type SignedVAAWithQuorum struct { func (x *SignedVAAWithQuorum) Reset() { *x = SignedVAAWithQuorum{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[3] + mi := &file_gossip_v1_gossip_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -401,7 +475,7 @@ func (x *SignedVAAWithQuorum) String() string { func (*SignedVAAWithQuorum) ProtoMessage() {} func (x *SignedVAAWithQuorum) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[3] + mi := &file_gossip_v1_gossip_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -414,7 +488,7 @@ func (x *SignedVAAWithQuorum) ProtoReflect() protoreflect.Message { // Deprecated: Use SignedVAAWithQuorum.ProtoReflect.Descriptor instead. func (*SignedVAAWithQuorum) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{3} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{4} } func (x *SignedVAAWithQuorum) GetVaa() []byte { @@ -445,7 +519,7 @@ type SignedObservationRequest struct { func (x *SignedObservationRequest) Reset() { *x = SignedObservationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[4] + mi := &file_gossip_v1_gossip_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -458,7 +532,7 @@ func (x *SignedObservationRequest) String() string { func (*SignedObservationRequest) ProtoMessage() {} func (x *SignedObservationRequest) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[4] + mi := &file_gossip_v1_gossip_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -471,7 +545,7 @@ func (x *SignedObservationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SignedObservationRequest.ProtoReflect.Descriptor instead. func (*SignedObservationRequest) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{4} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{5} } func (x *SignedObservationRequest) GetObservationRequest() []byte { @@ -508,7 +582,7 @@ type ObservationRequest struct { func (x *ObservationRequest) Reset() { *x = ObservationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[5] + mi := &file_gossip_v1_gossip_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -521,7 +595,7 @@ func (x *ObservationRequest) String() string { func (*ObservationRequest) ProtoMessage() {} func (x *ObservationRequest) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[5] + mi := &file_gossip_v1_gossip_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -534,7 +608,7 @@ func (x *ObservationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ObservationRequest.ProtoReflect.Descriptor instead. func (*ObservationRequest) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{5} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{6} } func (x *ObservationRequest) GetChainId() uint32 { @@ -575,7 +649,7 @@ type SignedChainGovernorConfig struct { func (x *SignedChainGovernorConfig) Reset() { *x = SignedChainGovernorConfig{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[6] + mi := &file_gossip_v1_gossip_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -588,7 +662,7 @@ func (x *SignedChainGovernorConfig) String() string { func (*SignedChainGovernorConfig) ProtoMessage() {} func (x *SignedChainGovernorConfig) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[6] + mi := &file_gossip_v1_gossip_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -601,7 +675,7 @@ func (x *SignedChainGovernorConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use SignedChainGovernorConfig.ProtoReflect.Descriptor instead. func (*SignedChainGovernorConfig) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{6} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{7} } func (x *SignedChainGovernorConfig) GetConfig() []byte { @@ -641,7 +715,7 @@ type ChainGovernorConfig struct { func (x *ChainGovernorConfig) Reset() { *x = ChainGovernorConfig{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[7] + mi := &file_gossip_v1_gossip_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -654,7 +728,7 @@ func (x *ChainGovernorConfig) String() string { func (*ChainGovernorConfig) ProtoMessage() {} func (x *ChainGovernorConfig) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[7] + mi := &file_gossip_v1_gossip_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -667,7 +741,7 @@ func (x *ChainGovernorConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use ChainGovernorConfig.ProtoReflect.Descriptor instead. func (*ChainGovernorConfig) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{7} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{8} } func (x *ChainGovernorConfig) GetNodeName() string { @@ -729,7 +803,7 @@ type SignedChainGovernorStatus struct { func (x *SignedChainGovernorStatus) Reset() { *x = SignedChainGovernorStatus{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[8] + mi := &file_gossip_v1_gossip_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -742,7 +816,7 @@ func (x *SignedChainGovernorStatus) String() string { func (*SignedChainGovernorStatus) ProtoMessage() {} func (x *SignedChainGovernorStatus) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[8] + mi := &file_gossip_v1_gossip_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -755,7 +829,7 @@ func (x *SignedChainGovernorStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use SignedChainGovernorStatus.ProtoReflect.Descriptor instead. func (*SignedChainGovernorStatus) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{8} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{9} } func (x *SignedChainGovernorStatus) GetStatus() []byte { @@ -793,7 +867,7 @@ type ChainGovernorStatus struct { func (x *ChainGovernorStatus) Reset() { *x = ChainGovernorStatus{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[9] + mi := &file_gossip_v1_gossip_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -806,7 +880,7 @@ func (x *ChainGovernorStatus) String() string { func (*ChainGovernorStatus) ProtoMessage() {} func (x *ChainGovernorStatus) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[9] + mi := &file_gossip_v1_gossip_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -819,7 +893,7 @@ func (x *ChainGovernorStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use ChainGovernorStatus.ProtoReflect.Descriptor instead. func (*ChainGovernorStatus) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{9} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{10} } func (x *ChainGovernorStatus) GetNodeName() string { @@ -864,7 +938,7 @@ type SignedQueryRequest struct { func (x *SignedQueryRequest) Reset() { *x = SignedQueryRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[10] + mi := &file_gossip_v1_gossip_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -877,7 +951,7 @@ func (x *SignedQueryRequest) String() string { func (*SignedQueryRequest) ProtoMessage() {} func (x *SignedQueryRequest) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[10] + mi := &file_gossip_v1_gossip_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -890,7 +964,7 @@ func (x *SignedQueryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SignedQueryRequest.ProtoReflect.Descriptor instead. func (*SignedQueryRequest) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{10} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{11} } func (x *SignedQueryRequest) GetQueryRequest() []byte { @@ -921,7 +995,7 @@ type SignedQueryResponse struct { func (x *SignedQueryResponse) Reset() { *x = SignedQueryResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[11] + mi := &file_gossip_v1_gossip_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -934,7 +1008,7 @@ func (x *SignedQueryResponse) String() string { func (*SignedQueryResponse) ProtoMessage() {} func (x *SignedQueryResponse) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[11] + mi := &file_gossip_v1_gossip_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -947,7 +1021,7 @@ func (x *SignedQueryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SignedQueryResponse.ProtoReflect.Descriptor instead. func (*SignedQueryResponse) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{11} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{12} } func (x *SignedQueryResponse) GetQueryResponse() []byte { @@ -980,7 +1054,7 @@ type SignedObservationBatch struct { func (x *SignedObservationBatch) Reset() { *x = SignedObservationBatch{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[12] + mi := &file_gossip_v1_gossip_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -993,7 +1067,7 @@ func (x *SignedObservationBatch) String() string { func (*SignedObservationBatch) ProtoMessage() {} func (x *SignedObservationBatch) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[12] + mi := &file_gossip_v1_gossip_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1006,7 +1080,7 @@ func (x *SignedObservationBatch) ProtoReflect() protoreflect.Message { // Deprecated: Use SignedObservationBatch.ProtoReflect.Descriptor instead. func (*SignedObservationBatch) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{12} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{13} } func (x *SignedObservationBatch) GetAddr() []byte { @@ -1044,7 +1118,7 @@ type Observation struct { func (x *Observation) Reset() { *x = Observation{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[13] + mi := &file_gossip_v1_gossip_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1057,7 +1131,7 @@ func (x *Observation) String() string { func (*Observation) ProtoMessage() {} func (x *Observation) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[13] + mi := &file_gossip_v1_gossip_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1070,7 +1144,7 @@ func (x *Observation) ProtoReflect() protoreflect.Message { // Deprecated: Use Observation.ProtoReflect.Descriptor instead. func (*Observation) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{13} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{14} } func (x *Observation) GetHash() []byte { @@ -1123,7 +1197,7 @@ type Heartbeat_Network struct { func (x *Heartbeat_Network) Reset() { *x = Heartbeat_Network{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[14] + mi := &file_gossip_v1_gossip_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1136,7 +1210,7 @@ func (x *Heartbeat_Network) String() string { func (*Heartbeat_Network) ProtoMessage() {} func (x *Heartbeat_Network) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[14] + mi := &file_gossip_v1_gossip_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1149,7 +1223,7 @@ func (x *Heartbeat_Network) ProtoReflect() protoreflect.Message { // Deprecated: Use Heartbeat_Network.ProtoReflect.Descriptor instead. func (*Heartbeat_Network) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{2, 0} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{3, 0} } func (x *Heartbeat_Network) GetId() uint32 { @@ -1207,7 +1281,7 @@ type ChainGovernorConfig_Chain struct { func (x *ChainGovernorConfig_Chain) Reset() { *x = ChainGovernorConfig_Chain{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[15] + mi := &file_gossip_v1_gossip_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1220,7 +1294,7 @@ func (x *ChainGovernorConfig_Chain) String() string { func (*ChainGovernorConfig_Chain) ProtoMessage() {} func (x *ChainGovernorConfig_Chain) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[15] + mi := &file_gossip_v1_gossip_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1233,7 +1307,7 @@ func (x *ChainGovernorConfig_Chain) ProtoReflect() protoreflect.Message { // Deprecated: Use ChainGovernorConfig_Chain.ProtoReflect.Descriptor instead. func (*ChainGovernorConfig_Chain) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{7, 0} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{8, 0} } func (x *ChainGovernorConfig_Chain) GetChainId() uint32 { @@ -1270,7 +1344,7 @@ type ChainGovernorConfig_Token struct { func (x *ChainGovernorConfig_Token) Reset() { *x = ChainGovernorConfig_Token{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[16] + mi := &file_gossip_v1_gossip_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1283,7 +1357,7 @@ func (x *ChainGovernorConfig_Token) String() string { func (*ChainGovernorConfig_Token) ProtoMessage() {} func (x *ChainGovernorConfig_Token) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[16] + mi := &file_gossip_v1_gossip_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1296,7 +1370,7 @@ func (x *ChainGovernorConfig_Token) ProtoReflect() protoreflect.Message { // Deprecated: Use ChainGovernorConfig_Token.ProtoReflect.Descriptor instead. func (*ChainGovernorConfig_Token) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{7, 1} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{8, 1} } func (x *ChainGovernorConfig_Token) GetOriginChainId() uint32 { @@ -1334,7 +1408,7 @@ type ChainGovernorStatus_EnqueuedVAA struct { func (x *ChainGovernorStatus_EnqueuedVAA) Reset() { *x = ChainGovernorStatus_EnqueuedVAA{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[17] + mi := &file_gossip_v1_gossip_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1347,7 +1421,7 @@ func (x *ChainGovernorStatus_EnqueuedVAA) String() string { func (*ChainGovernorStatus_EnqueuedVAA) ProtoMessage() {} func (x *ChainGovernorStatus_EnqueuedVAA) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[17] + mi := &file_gossip_v1_gossip_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1360,7 +1434,7 @@ func (x *ChainGovernorStatus_EnqueuedVAA) ProtoReflect() protoreflect.Message { // Deprecated: Use ChainGovernorStatus_EnqueuedVAA.ProtoReflect.Descriptor instead. func (*ChainGovernorStatus_EnqueuedVAA) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{9, 0} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{10, 0} } func (x *ChainGovernorStatus_EnqueuedVAA) GetSequence() uint64 { @@ -1404,7 +1478,7 @@ type ChainGovernorStatus_Emitter struct { func (x *ChainGovernorStatus_Emitter) Reset() { *x = ChainGovernorStatus_Emitter{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[18] + mi := &file_gossip_v1_gossip_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1417,7 +1491,7 @@ func (x *ChainGovernorStatus_Emitter) String() string { func (*ChainGovernorStatus_Emitter) ProtoMessage() {} func (x *ChainGovernorStatus_Emitter) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[18] + mi := &file_gossip_v1_gossip_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1430,7 +1504,7 @@ func (x *ChainGovernorStatus_Emitter) ProtoReflect() protoreflect.Message { // Deprecated: Use ChainGovernorStatus_Emitter.ProtoReflect.Descriptor instead. func (*ChainGovernorStatus_Emitter) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{9, 1} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{10, 1} } func (x *ChainGovernorStatus_Emitter) GetEmitterAddress() string { @@ -1470,7 +1544,7 @@ type ChainGovernorStatus_Chain struct { func (x *ChainGovernorStatus_Chain) Reset() { *x = ChainGovernorStatus_Chain{} if protoimpl.UnsafeEnabled { - mi := &file_gossip_v1_gossip_proto_msgTypes[19] + mi := &file_gossip_v1_gossip_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1483,7 +1557,7 @@ func (x *ChainGovernorStatus_Chain) String() string { func (*ChainGovernorStatus_Chain) ProtoMessage() {} func (x *ChainGovernorStatus_Chain) ProtoReflect() protoreflect.Message { - mi := &file_gossip_v1_gossip_proto_msgTypes[19] + mi := &file_gossip_v1_gossip_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1496,7 +1570,7 @@ func (x *ChainGovernorStatus_Chain) ProtoReflect() protoreflect.Message { // Deprecated: Use ChainGovernorStatus_Chain.ProtoReflect.Descriptor instead. func (*ChainGovernorStatus_Chain) Descriptor() ([]byte, []int) { - return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{9, 2} + return file_gossip_v1_gossip_proto_rawDescGZIP(), []int{10, 2} } func (x *ChainGovernorStatus_Chain) GetChainId() uint32 { @@ -1546,7 +1620,7 @@ var File_gossip_v1_gossip_proto protoreflect.FileDescriptor var file_gossip_v1_gossip_proto_rawDesc = []byte{ 0x0a, 0x16, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, - 0x2e, 0x76, 0x31, 0x22, 0xf9, 0x05, 0x0a, 0x0d, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x4d, 0x65, + 0x2e, 0x76, 0x31, 0x22, 0xc6, 0x06, 0x0a, 0x0d, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, @@ -1593,197 +1667,206 @@ var file_gossip_v1_gossip_proto_rawDesc = []byte{ 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x48, 0x00, 0x52, 0x16, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, - 0x61, 0x74, 0x63, 0x68, 0x42, 0x09, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, - 0x72, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, - 0x61, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, - 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x23, - 0x0a, 0x0d, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x41, - 0x64, 0x64, 0x72, 0x22, 0x88, 0x04, 0x0a, 0x09, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, - 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, - 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x38, 0x0a, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x73, 0x73, 0x69, - 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x2e, 0x4e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, - 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x75, - 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x12, - 0x25, 0x0a, 0x0e, 0x62, 0x6f, 0x6f, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x62, 0x6f, 0x6f, 0x74, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x70, 0x32, 0x70, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, - 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x32, 0x70, 0x4e, 0x6f, 0x64, 0x65, - 0x49, 0x64, 0x1a, 0xc9, 0x01, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x0e, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, - 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, - 0x63, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x61, 0x66, 0x65, 0x48, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, - 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x66, - 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x27, - 0x0a, 0x13, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x41, 0x41, 0x57, 0x69, 0x74, 0x68, 0x51, - 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x12, 0x10, 0x0a, 0x03, 0x76, 0x61, 0x61, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x03, 0x76, 0x61, 0x61, 0x22, 0x8e, 0x01, 0x0a, 0x18, 0x53, 0x69, 0x67, 0x6e, - 0x65, 0x64, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x13, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x12, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x5f, - 0x61, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x67, 0x75, 0x61, 0x72, - 0x64, 0x69, 0x61, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x22, 0x66, 0x0a, 0x12, 0x4f, 0x62, 0x73, 0x65, - 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, - 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x78, 0x5f, - 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, - 0x73, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x22, 0x76, 0x0a, 0x19, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x47, - 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, - 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x5f, - 0x61, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x67, 0x75, 0x61, 0x72, - 0x64, 0x69, 0x61, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x22, 0x81, 0x04, 0x0a, 0x13, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x47, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3c, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x47, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x73, 0x12, 0x3c, 0x0a, 0x06, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x05, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x68, 0x61, 0x69, 0x6e, 0x47, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x72, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, - 0x66, 0x6c, 0x6f, 0x77, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x1a, 0x7b, 0x0a, 0x05, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, - 0x6c, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6e, - 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x30, 0x0a, 0x14, - 0x62, 0x69, 0x67, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x12, 0x62, 0x69, 0x67, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x1a, 0x6c, - 0x0a, 0x05, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x6f, 0x72, 0x69, 0x67, 0x69, - 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, - 0x25, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x22, 0x76, 0x0a, 0x19, - 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x47, 0x6f, 0x76, 0x65, 0x72, - 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, - 0x23, 0x0a, 0x0d, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, - 0x41, 0x64, 0x64, 0x72, 0x22, 0xdb, 0x06, 0x0a, 0x13, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x47, 0x6f, - 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, - 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x12, 0x3c, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, - 0x61, 0x69, 0x6e, 0x47, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x1a, - 0x8c, 0x01, 0x0a, 0x0b, 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x56, 0x41, 0x41, 0x12, - 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x72, - 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x0b, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x25, - 0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x1a, 0xb3, - 0x01, 0x0a, 0x07, 0x45, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x6d, - 0x69, 0x74, 0x74, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x6e, 0x71, - 0x75, 0x65, 0x75, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x11, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x56, - 0x61, 0x61, 0x73, 0x12, 0x4f, 0x0a, 0x0d, 0x65, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x5f, - 0x76, 0x61, 0x61, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x67, 0x6f, 0x73, + 0x61, 0x74, 0x63, 0x68, 0x12, 0x4b, 0x0a, 0x12, 0x74, 0x73, 0x73, 0x5f, 0x67, 0x6f, 0x73, 0x73, + 0x69, 0x70, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x53, 0x53, + 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, + 0x10, 0x74, 0x73, 0x73, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x42, 0x09, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x4a, 0x0a, 0x10, + 0x54, 0x53, 0x53, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x72, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, + 0x65, 0x64, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x68, + 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, + 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x75, 0x61, 0x72, 0x64, + 0x69, 0x61, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, + 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x22, 0x88, 0x04, 0x0a, + 0x09, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, + 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, + 0x38, 0x0a, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x65, + 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, + 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x67, 0x75, 0x61, 0x72, + 0x64, 0x69, 0x61, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x6f, 0x6f, 0x74, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0d, 0x62, 0x6f, 0x6f, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, + 0x1a, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x70, + 0x32, 0x70, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x09, 0x70, 0x32, 0x70, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x1a, 0xc9, 0x01, 0x0a, 0x07, + 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, + 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x61, 0x63, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0a, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, + 0x61, 0x66, 0x65, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0a, 0x73, 0x61, 0x66, 0x65, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x29, 0x0a, 0x10, + 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, + 0x64, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x27, 0x0a, 0x13, 0x53, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x56, 0x41, 0x41, 0x57, 0x69, 0x74, 0x68, 0x51, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x12, 0x10, + 0x0a, 0x03, 0x76, 0x61, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x76, 0x61, 0x61, + 0x22, 0x8e, 0x01, 0x0a, 0x18, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4f, 0x62, 0x73, 0x65, 0x72, + 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, + 0x13, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x12, 0x6f, 0x62, 0x73, 0x65, + 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, + 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x23, 0x0a, 0x0d, + 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x41, 0x64, 0x64, + 0x72, 0x22, 0x66, 0x0a, 0x12, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x76, 0x0a, 0x19, 0x53, 0x69, 0x67, + 0x6e, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x47, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x72, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, + 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x23, 0x0a, 0x0d, + 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x41, 0x64, 0x64, + 0x72, 0x22, 0x81, 0x04, 0x0a, 0x13, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x47, 0x6f, 0x76, 0x65, 0x72, + 0x6e, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, + 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, + 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3c, + 0x0a, 0x06, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, + 0x2e, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x47, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x3c, 0x0a, 0x06, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, + 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x47, 0x6f, + 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x52, 0x06, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x66, 0x6c, + 0x6f, 0x77, 0x5f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x66, 0x6c, 0x6f, 0x77, 0x43, 0x61, 0x6e, + 0x63, 0x65, 0x6c, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x1a, 0x7b, 0x0a, 0x05, 0x43, 0x68, + 0x61, 0x69, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x25, + 0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x69, 0x67, 0x5f, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x12, 0x62, 0x69, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x1a, 0x6c, 0x0a, 0x05, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x12, 0x26, 0x0a, 0x0f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, + 0x69, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, + 0x70, 0x72, 0x69, 0x63, 0x65, 0x22, 0x76, 0x0a, 0x19, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x47, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x75, 0x61, 0x72, + 0x64, 0x69, 0x61, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0c, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x22, 0xdb, 0x06, + 0x0a, 0x13, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x47, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3c, 0x0a, 0x06, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x47, 0x6f, 0x76, 0x65, - 0x72, 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x45, 0x6e, 0x71, 0x75, 0x65, - 0x75, 0x65, 0x64, 0x56, 0x41, 0x41, 0x52, 0x0c, 0x65, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, - 0x56, 0x61, 0x61, 0x73, 0x1a, 0xeb, 0x02, 0x0a, 0x05, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x19, - 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x40, 0x0a, 0x1c, 0x72, 0x65, 0x6d, - 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, - 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x1a, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, - 0x62, 0x6c, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x42, 0x0a, 0x08, 0x65, - 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, - 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x47, - 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x45, 0x6d, - 0x69, 0x74, 0x74, 0x65, 0x72, 0x52, 0x08, 0x65, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x73, 0x12, - 0x3c, 0x0a, 0x1b, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x5f, 0x74, 0x78, 0x5f, 0x6e, 0x65, 0x74, 0x5f, - 0x6e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x17, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x54, 0x78, 0x4e, 0x65, 0x74, - 0x4e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x46, 0x0a, - 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x5f, 0x74, 0x78, 0x5f, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, - 0x6e, 0x67, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1c, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x54, 0x78, - 0x4f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3b, 0x0a, 0x1a, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x63, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x66, 0x6c, 0x6f, 0x77, 0x43, - 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x22, 0x57, 0x0a, 0x12, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x71, 0x75, 0x65, 0x72, - 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0c, 0x71, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, + 0x72, 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x52, 0x06, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x1a, 0x8c, 0x01, 0x0a, 0x0b, 0x45, 0x6e, 0x71, + 0x75, 0x65, 0x75, 0x65, 0x64, 0x56, 0x41, 0x41, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, + 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, + 0x65, 0x6e, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x72, 0x65, 0x6c, 0x65, + 0x61, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x6f, + 0x6e, 0x61, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x17, + 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x1a, 0xb3, 0x01, 0x0a, 0x07, 0x45, 0x6d, 0x69, 0x74, + 0x74, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x6d, + 0x69, 0x74, 0x74, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x0a, 0x13, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x5f, 0x76, + 0x61, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x56, 0x61, 0x61, 0x73, 0x12, 0x4f, 0x0a, 0x0d, + 0x65, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x61, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x68, 0x61, 0x69, 0x6e, 0x47, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x72, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x2e, 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x56, 0x41, 0x41, 0x52, + 0x0c, 0x65, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x56, 0x61, 0x61, 0x73, 0x1a, 0xeb, 0x02, + 0x0a, 0x05, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x49, 0x64, 0x12, 0x40, 0x0a, 0x1c, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x5f, + 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, + 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, + 0x69, 0x6e, 0x67, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x6f, 0x74, 0x69, + 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x42, 0x0a, 0x08, 0x65, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x47, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x72, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x45, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x52, 0x08, + 0x65, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x73, 0x12, 0x3c, 0x0a, 0x1b, 0x73, 0x6d, 0x61, 0x6c, + 0x6c, 0x5f, 0x74, 0x78, 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x17, 0x73, + 0x6d, 0x61, 0x6c, 0x6c, 0x54, 0x78, 0x4e, 0x65, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x46, 0x0a, 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x5f, + 0x74, 0x78, 0x5f, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x6e, 0x6f, 0x74, 0x69, + 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x1c, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x54, 0x78, 0x4f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, + 0x67, 0x4e, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3b, + 0x0a, 0x1a, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x5f, 0x6e, 0x6f, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x17, 0x66, 0x6c, 0x6f, 0x77, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4e, 0x6f, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x57, 0x0a, 0x12, 0x53, + 0x69, 0x67, 0x6e, 0x65, 0x64, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x71, 0x75, 0x65, 0x72, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x22, 0x5a, 0x0a, 0x13, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x22, 0x68, 0x0a, 0x16, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, + 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x3a, + 0x0a, 0x0c, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2e, 0x76, 0x31, + 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x6f, 0x62, + 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x77, 0x0a, 0x0b, 0x4f, 0x62, + 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, + 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x5a, 0x0a, 0x13, 0x53, - 0x69, 0x67, 0x6e, 0x65, 0x64, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x68, 0x0a, 0x16, 0x53, 0x69, 0x67, 0x6e, 0x65, - 0x64, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x74, 0x63, - 0x68, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x3a, 0x0a, 0x0c, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, - 0x73, 0x73, 0x69, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x22, 0x77, 0x0a, 0x0b, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, - 0x68, 0x61, 0x73, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x64, 0x42, 0x41, 0x5a, 0x3f, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x75, 0x73, 0x6f, - 0x6e, 0x65, 0x2f, 0x77, 0x6f, 0x72, 0x6d, 0x68, 0x6f, 0x6c, 0x65, 0x2f, 0x6e, 0x6f, 0x64, 0x65, - 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x73, 0x73, 0x69, - 0x70, 0x2f, 0x76, 0x31, 0x3b, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x76, 0x31, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, + 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x78, + 0x48, 0x61, 0x73, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x49, 0x64, 0x42, 0x41, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x75, 0x73, 0x6f, 0x6e, 0x65, 0x2f, 0x77, 0x6f, 0x72, 0x6d, + 0x68, 0x6f, 0x6c, 0x65, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2f, 0x76, 0x31, 0x3b, 0x67, 0x6f, + 0x73, 0x73, 0x69, 0x70, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1798,50 +1881,52 @@ func file_gossip_v1_gossip_proto_rawDescGZIP() []byte { return file_gossip_v1_gossip_proto_rawDescData } -var file_gossip_v1_gossip_proto_msgTypes = make([]protoimpl.MessageInfo, 20) +var file_gossip_v1_gossip_proto_msgTypes = make([]protoimpl.MessageInfo, 21) var file_gossip_v1_gossip_proto_goTypes = []interface{}{ (*GossipMessage)(nil), // 0: gossip.v1.GossipMessage - (*SignedHeartbeat)(nil), // 1: gossip.v1.SignedHeartbeat - (*Heartbeat)(nil), // 2: gossip.v1.Heartbeat - (*SignedVAAWithQuorum)(nil), // 3: gossip.v1.SignedVAAWithQuorum - (*SignedObservationRequest)(nil), // 4: gossip.v1.SignedObservationRequest - (*ObservationRequest)(nil), // 5: gossip.v1.ObservationRequest - (*SignedChainGovernorConfig)(nil), // 6: gossip.v1.SignedChainGovernorConfig - (*ChainGovernorConfig)(nil), // 7: gossip.v1.ChainGovernorConfig - (*SignedChainGovernorStatus)(nil), // 8: gossip.v1.SignedChainGovernorStatus - (*ChainGovernorStatus)(nil), // 9: gossip.v1.ChainGovernorStatus - (*SignedQueryRequest)(nil), // 10: gossip.v1.SignedQueryRequest - (*SignedQueryResponse)(nil), // 11: gossip.v1.SignedQueryResponse - (*SignedObservationBatch)(nil), // 12: gossip.v1.SignedObservationBatch - (*Observation)(nil), // 13: gossip.v1.Observation - (*Heartbeat_Network)(nil), // 14: gossip.v1.Heartbeat.Network - (*ChainGovernorConfig_Chain)(nil), // 15: gossip.v1.ChainGovernorConfig.Chain - (*ChainGovernorConfig_Token)(nil), // 16: gossip.v1.ChainGovernorConfig.Token - (*ChainGovernorStatus_EnqueuedVAA)(nil), // 17: gossip.v1.ChainGovernorStatus.EnqueuedVAA - (*ChainGovernorStatus_Emitter)(nil), // 18: gossip.v1.ChainGovernorStatus.Emitter - (*ChainGovernorStatus_Chain)(nil), // 19: gossip.v1.ChainGovernorStatus.Chain + (*TSSGossipMessage)(nil), // 1: gossip.v1.TSSGossipMessage + (*SignedHeartbeat)(nil), // 2: gossip.v1.SignedHeartbeat + (*Heartbeat)(nil), // 3: gossip.v1.Heartbeat + (*SignedVAAWithQuorum)(nil), // 4: gossip.v1.SignedVAAWithQuorum + (*SignedObservationRequest)(nil), // 5: gossip.v1.SignedObservationRequest + (*ObservationRequest)(nil), // 6: gossip.v1.ObservationRequest + (*SignedChainGovernorConfig)(nil), // 7: gossip.v1.SignedChainGovernorConfig + (*ChainGovernorConfig)(nil), // 8: gossip.v1.ChainGovernorConfig + (*SignedChainGovernorStatus)(nil), // 9: gossip.v1.SignedChainGovernorStatus + (*ChainGovernorStatus)(nil), // 10: gossip.v1.ChainGovernorStatus + (*SignedQueryRequest)(nil), // 11: gossip.v1.SignedQueryRequest + (*SignedQueryResponse)(nil), // 12: gossip.v1.SignedQueryResponse + (*SignedObservationBatch)(nil), // 13: gossip.v1.SignedObservationBatch + (*Observation)(nil), // 14: gossip.v1.Observation + (*Heartbeat_Network)(nil), // 15: gossip.v1.Heartbeat.Network + (*ChainGovernorConfig_Chain)(nil), // 16: gossip.v1.ChainGovernorConfig.Chain + (*ChainGovernorConfig_Token)(nil), // 17: gossip.v1.ChainGovernorConfig.Token + (*ChainGovernorStatus_EnqueuedVAA)(nil), // 18: gossip.v1.ChainGovernorStatus.EnqueuedVAA + (*ChainGovernorStatus_Emitter)(nil), // 19: gossip.v1.ChainGovernorStatus.Emitter + (*ChainGovernorStatus_Chain)(nil), // 20: gossip.v1.ChainGovernorStatus.Chain } var file_gossip_v1_gossip_proto_depIdxs = []int32{ - 1, // 0: gossip.v1.GossipMessage.signed_heartbeat:type_name -> gossip.v1.SignedHeartbeat - 3, // 1: gossip.v1.GossipMessage.signed_vaa_with_quorum:type_name -> gossip.v1.SignedVAAWithQuorum - 4, // 2: gossip.v1.GossipMessage.signed_observation_request:type_name -> gossip.v1.SignedObservationRequest - 6, // 3: gossip.v1.GossipMessage.signed_chain_governor_config:type_name -> gossip.v1.SignedChainGovernorConfig - 8, // 4: gossip.v1.GossipMessage.signed_chain_governor_status:type_name -> gossip.v1.SignedChainGovernorStatus - 10, // 5: gossip.v1.GossipMessage.signed_query_request:type_name -> gossip.v1.SignedQueryRequest - 11, // 6: gossip.v1.GossipMessage.signed_query_response:type_name -> gossip.v1.SignedQueryResponse - 12, // 7: gossip.v1.GossipMessage.signed_observation_batch:type_name -> gossip.v1.SignedObservationBatch - 14, // 8: gossip.v1.Heartbeat.networks:type_name -> gossip.v1.Heartbeat.Network - 15, // 9: gossip.v1.ChainGovernorConfig.chains:type_name -> gossip.v1.ChainGovernorConfig.Chain - 16, // 10: gossip.v1.ChainGovernorConfig.tokens:type_name -> gossip.v1.ChainGovernorConfig.Token - 19, // 11: gossip.v1.ChainGovernorStatus.chains:type_name -> gossip.v1.ChainGovernorStatus.Chain - 13, // 12: gossip.v1.SignedObservationBatch.observations:type_name -> gossip.v1.Observation - 17, // 13: gossip.v1.ChainGovernorStatus.Emitter.enqueued_vaas:type_name -> gossip.v1.ChainGovernorStatus.EnqueuedVAA - 18, // 14: gossip.v1.ChainGovernorStatus.Chain.emitters:type_name -> gossip.v1.ChainGovernorStatus.Emitter - 15, // [15:15] is the sub-list for method output_type - 15, // [15:15] is the sub-list for method input_type - 15, // [15:15] is the sub-list for extension type_name - 15, // [15:15] is the sub-list for extension extendee - 0, // [0:15] is the sub-list for field type_name + 2, // 0: gossip.v1.GossipMessage.signed_heartbeat:type_name -> gossip.v1.SignedHeartbeat + 4, // 1: gossip.v1.GossipMessage.signed_vaa_with_quorum:type_name -> gossip.v1.SignedVAAWithQuorum + 5, // 2: gossip.v1.GossipMessage.signed_observation_request:type_name -> gossip.v1.SignedObservationRequest + 7, // 3: gossip.v1.GossipMessage.signed_chain_governor_config:type_name -> gossip.v1.SignedChainGovernorConfig + 9, // 4: gossip.v1.GossipMessage.signed_chain_governor_status:type_name -> gossip.v1.SignedChainGovernorStatus + 11, // 5: gossip.v1.GossipMessage.signed_query_request:type_name -> gossip.v1.SignedQueryRequest + 12, // 6: gossip.v1.GossipMessage.signed_query_response:type_name -> gossip.v1.SignedQueryResponse + 13, // 7: gossip.v1.GossipMessage.signed_observation_batch:type_name -> gossip.v1.SignedObservationBatch + 1, // 8: gossip.v1.GossipMessage.tss_gossip_message:type_name -> gossip.v1.TSSGossipMessage + 15, // 9: gossip.v1.Heartbeat.networks:type_name -> gossip.v1.Heartbeat.Network + 16, // 10: gossip.v1.ChainGovernorConfig.chains:type_name -> gossip.v1.ChainGovernorConfig.Chain + 17, // 11: gossip.v1.ChainGovernorConfig.tokens:type_name -> gossip.v1.ChainGovernorConfig.Token + 20, // 12: gossip.v1.ChainGovernorStatus.chains:type_name -> gossip.v1.ChainGovernorStatus.Chain + 14, // 13: gossip.v1.SignedObservationBatch.observations:type_name -> gossip.v1.Observation + 18, // 14: gossip.v1.ChainGovernorStatus.Emitter.enqueued_vaas:type_name -> gossip.v1.ChainGovernorStatus.EnqueuedVAA + 19, // 15: gossip.v1.ChainGovernorStatus.Chain.emitters:type_name -> gossip.v1.ChainGovernorStatus.Emitter + 16, // [16:16] is the sub-list for method output_type + 16, // [16:16] is the sub-list for method input_type + 16, // [16:16] is the sub-list for extension type_name + 16, // [16:16] is the sub-list for extension extendee + 0, // [0:16] is the sub-list for field type_name } func init() { file_gossip_v1_gossip_proto_init() } @@ -1863,7 +1948,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SignedHeartbeat); i { + switch v := v.(*TSSGossipMessage); i { case 0: return &v.state case 1: @@ -1875,7 +1960,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Heartbeat); i { + switch v := v.(*SignedHeartbeat); i { case 0: return &v.state case 1: @@ -1887,7 +1972,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SignedVAAWithQuorum); i { + switch v := v.(*Heartbeat); i { case 0: return &v.state case 1: @@ -1899,7 +1984,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SignedObservationRequest); i { + switch v := v.(*SignedVAAWithQuorum); i { case 0: return &v.state case 1: @@ -1911,7 +1996,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ObservationRequest); i { + switch v := v.(*SignedObservationRequest); i { case 0: return &v.state case 1: @@ -1923,7 +2008,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SignedChainGovernorConfig); i { + switch v := v.(*ObservationRequest); i { case 0: return &v.state case 1: @@ -1935,7 +2020,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChainGovernorConfig); i { + switch v := v.(*SignedChainGovernorConfig); i { case 0: return &v.state case 1: @@ -1947,7 +2032,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SignedChainGovernorStatus); i { + switch v := v.(*ChainGovernorConfig); i { case 0: return &v.state case 1: @@ -1959,7 +2044,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChainGovernorStatus); i { + switch v := v.(*SignedChainGovernorStatus); i { case 0: return &v.state case 1: @@ -1971,7 +2056,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SignedQueryRequest); i { + switch v := v.(*ChainGovernorStatus); i { case 0: return &v.state case 1: @@ -1983,7 +2068,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SignedQueryResponse); i { + switch v := v.(*SignedQueryRequest); i { case 0: return &v.state case 1: @@ -1995,7 +2080,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SignedObservationBatch); i { + switch v := v.(*SignedQueryResponse); i { case 0: return &v.state case 1: @@ -2007,7 +2092,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Observation); i { + switch v := v.(*SignedObservationBatch); i { case 0: return &v.state case 1: @@ -2019,7 +2104,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Heartbeat_Network); i { + switch v := v.(*Observation); i { case 0: return &v.state case 1: @@ -2031,7 +2116,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChainGovernorConfig_Chain); i { + switch v := v.(*Heartbeat_Network); i { case 0: return &v.state case 1: @@ -2043,7 +2128,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChainGovernorConfig_Token); i { + switch v := v.(*ChainGovernorConfig_Chain); i { case 0: return &v.state case 1: @@ -2055,7 +2140,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChainGovernorStatus_EnqueuedVAA); i { + switch v := v.(*ChainGovernorConfig_Token); i { case 0: return &v.state case 1: @@ -2067,7 +2152,7 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChainGovernorStatus_Emitter); i { + switch v := v.(*ChainGovernorStatus_EnqueuedVAA); i { case 0: return &v.state case 1: @@ -2079,6 +2164,18 @@ func file_gossip_v1_gossip_proto_init() { } } file_gossip_v1_gossip_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChainGovernorStatus_Emitter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gossip_v1_gossip_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ChainGovernorStatus_Chain); i { case 0: return &v.state @@ -2100,6 +2197,7 @@ func file_gossip_v1_gossip_proto_init() { (*GossipMessage_SignedQueryRequest)(nil), (*GossipMessage_SignedQueryResponse)(nil), (*GossipMessage_SignedObservationBatch)(nil), + (*GossipMessage_TssGossipMessage)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -2107,7 +2205,7 @@ func file_gossip_v1_gossip_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_gossip_v1_gossip_proto_rawDesc, NumEnums: 0, - NumMessages: 20, + NumMessages: 21, NumExtensions: 0, NumServices: 0, }, diff --git a/node/pkg/proto/tsscomm/v1/tsscomm.pb.go b/node/pkg/proto/tsscomm/v1/tsscomm.pb.go deleted file mode 100644 index 8f2eefadf6..0000000000 --- a/node/pkg/proto/tsscomm/v1/tsscomm.pb.go +++ /dev/null @@ -1,785 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.27.1 -// protoc (unknown) -// source: tsscomm/v1/tsscomm.proto - -package tsscommv1 - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - emptypb "google.golang.org/protobuf/types/known/emptypb" - _ "google.golang.org/protobuf/types/known/timestamppb" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// copyOfTssLib proto. -type PartyId struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` -} - -func (x *PartyId) Reset() { - *x = PartyId{} - if protoimpl.UnsafeEnabled { - mi := &file_tsscomm_v1_tsscomm_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PartyId) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PartyId) ProtoMessage() {} - -func (x *PartyId) ProtoReflect() protoreflect.Message { - mi := &file_tsscomm_v1_tsscomm_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PartyId.ProtoReflect.Descriptor instead. -func (*PartyId) Descriptor() ([]byte, []int) { - return file_tsscomm_v1_tsscomm_proto_rawDescGZIP(), []int{0} -} - -func (x *PartyId) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -type TssContent struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` - MsgSerialNumber uint64 `protobuf:"varint,2,opt,name=msg_serial_number,json=msgSerialNumber,proto3" json:"msg_serial_number,omitempty"` -} - -func (x *TssContent) Reset() { - *x = TssContent{} - if protoimpl.UnsafeEnabled { - mi := &file_tsscomm_v1_tsscomm_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TssContent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TssContent) ProtoMessage() {} - -func (x *TssContent) ProtoReflect() protoreflect.Message { - mi := &file_tsscomm_v1_tsscomm_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TssContent.ProtoReflect.Descriptor instead. -func (*TssContent) Descriptor() ([]byte, []int) { - return file_tsscomm_v1_tsscomm_proto_rawDescGZIP(), []int{1} -} - -func (x *TssContent) GetPayload() []byte { - if x != nil { - return x.Payload - } - return nil -} - -func (x *TssContent) GetMsgSerialNumber() uint64 { - if x != nil { - return x.MsgSerialNumber - } - return 0 -} - -// SignedMessage is the content of a broadcast message. It may be echoed and as a result requires a signature. -type SignedMessage struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Although we don’t anticipate more senders than 255 (which is equivalent to 1 byte), - // the uint32 data type is highly optimized in protobuf for sending smaller - // numbers (refer to https://protobuf.dev/programming-guides/encoding/#simple). - Sender uint32 `protobuf:"varint,1,opt,name=sender,proto3" json:"sender,omitempty"` // pointer to specific PartyID - Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` - // Types that are assignable to Content: - // - // *SignedMessage_TssContent - // *SignedMessage_HashEcho - Content isSignedMessage_Content `protobuf_oneof:"content"` -} - -func (x *SignedMessage) Reset() { - *x = SignedMessage{} - if protoimpl.UnsafeEnabled { - mi := &file_tsscomm_v1_tsscomm_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SignedMessage) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SignedMessage) ProtoMessage() {} - -func (x *SignedMessage) ProtoReflect() protoreflect.Message { - mi := &file_tsscomm_v1_tsscomm_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SignedMessage.ProtoReflect.Descriptor instead. -func (*SignedMessage) Descriptor() ([]byte, []int) { - return file_tsscomm_v1_tsscomm_proto_rawDescGZIP(), []int{2} -} - -func (x *SignedMessage) GetSender() uint32 { - if x != nil { - return x.Sender - } - return 0 -} - -func (x *SignedMessage) GetSignature() []byte { - if x != nil { - return x.Signature - } - return nil -} - -func (m *SignedMessage) GetContent() isSignedMessage_Content { - if m != nil { - return m.Content - } - return nil -} - -func (x *SignedMessage) GetTssContent() *TssContent { - if x, ok := x.GetContent().(*SignedMessage_TssContent); ok { - return x.TssContent - } - return nil -} - -func (x *SignedMessage) GetHashEcho() *HashEcho { - if x, ok := x.GetContent().(*SignedMessage_HashEcho); ok { - return x.HashEcho - } - return nil -} - -type isSignedMessage_Content interface { - isSignedMessage_Content() -} - -type SignedMessage_TssContent struct { - TssContent *TssContent `protobuf:"bytes,3,opt,name=tss_content,json=tssContent,proto3,oneof"` -} - -type SignedMessage_HashEcho struct { - HashEcho *HashEcho `protobuf:"bytes,6,opt,name=hashEcho,proto3,oneof"` -} - -func (*SignedMessage_TssContent) isSignedMessage_Content() {} - -func (*SignedMessage_HashEcho) isSignedMessage_Content() {} - -type HashEcho struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - SessionUuid []byte `protobuf:"bytes,1,opt,name=sessionUuid,proto3" json:"sessionUuid,omitempty"` // should be the relevant uuid. without it, we cant math this echo with the tssContent that we receive a hash of. - OriginalContetDigest []byte `protobuf:"bytes,2,opt,name=originalContetDigest,proto3" json:"originalContetDigest,omitempty"` // should be a hash of what the original sender signed. -} - -func (x *HashEcho) Reset() { - *x = HashEcho{} - if protoimpl.UnsafeEnabled { - mi := &file_tsscomm_v1_tsscomm_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *HashEcho) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HashEcho) ProtoMessage() {} - -func (x *HashEcho) ProtoReflect() protoreflect.Message { - mi := &file_tsscomm_v1_tsscomm_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HashEcho.ProtoReflect.Descriptor instead. -func (*HashEcho) Descriptor() ([]byte, []int) { - return file_tsscomm_v1_tsscomm_proto_rawDescGZIP(), []int{3} -} - -func (x *HashEcho) GetSessionUuid() []byte { - if x != nil { - return x.SessionUuid - } - return nil -} - -func (x *HashEcho) GetOriginalContetDigest() []byte { - if x != nil { - return x.OriginalContetDigest - } - return nil -} - -// Echo is a message explicitly used by the Reliable Broadcast protocol. -type Echo struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Message *SignedMessage `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` -} - -func (x *Echo) Reset() { - *x = Echo{} - if protoimpl.UnsafeEnabled { - mi := &file_tsscomm_v1_tsscomm_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Echo) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Echo) ProtoMessage() {} - -func (x *Echo) ProtoReflect() protoreflect.Message { - mi := &file_tsscomm_v1_tsscomm_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Echo.ProtoReflect.Descriptor instead. -func (*Echo) Descriptor() ([]byte, []int) { - return file_tsscomm_v1_tsscomm_proto_rawDescGZIP(), []int{4} -} - -func (x *Echo) GetMessage() *SignedMessage { - if x != nil { - return x.Message - } - return nil -} - -type VaaV1Info struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Marshaled []byte `protobuf:"bytes,1,opt,name=marshaled,proto3" json:"marshaled,omitempty"` -} - -func (x *VaaV1Info) Reset() { - *x = VaaV1Info{} - if protoimpl.UnsafeEnabled { - mi := &file_tsscomm_v1_tsscomm_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *VaaV1Info) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*VaaV1Info) ProtoMessage() {} - -func (x *VaaV1Info) ProtoReflect() protoreflect.Message { - mi := &file_tsscomm_v1_tsscomm_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use VaaV1Info.ProtoReflect.Descriptor instead. -func (*VaaV1Info) Descriptor() ([]byte, []int) { - return file_tsscomm_v1_tsscomm_proto_rawDescGZIP(), []int{5} -} - -func (x *VaaV1Info) GetMarshaled() []byte { - if x != nil { - return x.Marshaled - } - return nil -} - -type Unicast struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Types that are assignable to Content: - // - // *Unicast_Tss - // *Unicast_Vaav1 - Content isUnicast_Content `protobuf_oneof:"content"` -} - -func (x *Unicast) Reset() { - *x = Unicast{} - if protoimpl.UnsafeEnabled { - mi := &file_tsscomm_v1_tsscomm_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Unicast) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Unicast) ProtoMessage() {} - -func (x *Unicast) ProtoReflect() protoreflect.Message { - mi := &file_tsscomm_v1_tsscomm_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Unicast.ProtoReflect.Descriptor instead. -func (*Unicast) Descriptor() ([]byte, []int) { - return file_tsscomm_v1_tsscomm_proto_rawDescGZIP(), []int{6} -} - -func (m *Unicast) GetContent() isUnicast_Content { - if m != nil { - return m.Content - } - return nil -} - -func (x *Unicast) GetTss() *TssContent { - if x, ok := x.GetContent().(*Unicast_Tss); ok { - return x.Tss - } - return nil -} - -func (x *Unicast) GetVaav1() *VaaV1Info { - if x, ok := x.GetContent().(*Unicast_Vaav1); ok { - return x.Vaav1 - } - return nil -} - -type isUnicast_Content interface { - isUnicast_Content() -} - -type Unicast_Tss struct { - Tss *TssContent `protobuf:"bytes,1,opt,name=tss,proto3,oneof"` -} - -type Unicast_Vaav1 struct { - Vaav1 *VaaV1Info `protobuf:"bytes,2,opt,name=vaav1,proto3,oneof"` -} - -func (*Unicast_Tss) isUnicast_Content() {} - -func (*Unicast_Vaav1) isUnicast_Content() {} - -// PropagatedMessage is a message that is sent across the network, -// either to a specific recipient or all nodes (using reliable broadcast). -type PropagatedMessage struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Types that are assignable to Message: - // - // *PropagatedMessage_Unicast - // *PropagatedMessage_Echo - Message isPropagatedMessage_Message `protobuf_oneof:"Message"` -} - -func (x *PropagatedMessage) Reset() { - *x = PropagatedMessage{} - if protoimpl.UnsafeEnabled { - mi := &file_tsscomm_v1_tsscomm_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PropagatedMessage) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PropagatedMessage) ProtoMessage() {} - -func (x *PropagatedMessage) ProtoReflect() protoreflect.Message { - mi := &file_tsscomm_v1_tsscomm_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PropagatedMessage.ProtoReflect.Descriptor instead. -func (*PropagatedMessage) Descriptor() ([]byte, []int) { - return file_tsscomm_v1_tsscomm_proto_rawDescGZIP(), []int{7} -} - -func (m *PropagatedMessage) GetMessage() isPropagatedMessage_Message { - if m != nil { - return m.Message - } - return nil -} - -func (x *PropagatedMessage) GetUnicast() *Unicast { - if x, ok := x.GetMessage().(*PropagatedMessage_Unicast); ok { - return x.Unicast - } - return nil -} - -func (x *PropagatedMessage) GetEcho() *Echo { - if x, ok := x.GetMessage().(*PropagatedMessage_Echo); ok { - return x.Echo - } - return nil -} - -type isPropagatedMessage_Message interface { - isPropagatedMessage_Message() -} - -type PropagatedMessage_Unicast struct { - Unicast *Unicast `protobuf:"bytes,1,opt,name=Unicast,proto3,oneof"` -} - -type PropagatedMessage_Echo struct { - Echo *Echo `protobuf:"bytes,2,opt,name=Echo,proto3,oneof"` -} - -func (*PropagatedMessage_Unicast) isPropagatedMessage_Message() {} - -func (*PropagatedMessage_Echo) isPropagatedMessage_Message() {} - -var File_tsscomm_v1_tsscomm_proto protoreflect.FileDescriptor - -var file_tsscomm_v1_tsscomm_proto_rawDesc = []byte{ - 0x0a, 0x18, 0x74, 0x73, 0x73, 0x63, 0x6f, 0x6d, 0x6d, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x73, 0x73, - 0x63, 0x6f, 0x6d, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x74, 0x73, 0x73, 0x63, - 0x6f, 0x6d, 0x6d, 0x2e, 0x76, 0x31, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x19, 0x0a, 0x07, 0x50, 0x61, 0x72, 0x74, 0x79, 0x49, 0x64, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, - 0x52, 0x0a, 0x0a, 0x54, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, - 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, - 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x73, 0x67, 0x5f, 0x73, - 0x65, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x73, 0x67, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, - 0x62, 0x65, 0x72, 0x22, 0xbf, 0x01, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x0a, - 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x74, - 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x74, 0x73, 0x73, 0x63, 0x6f, 0x6d, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x73, - 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x73, 0x73, 0x43, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x08, 0x68, 0x61, 0x73, 0x68, 0x45, 0x63, - 0x68, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x73, 0x73, 0x63, 0x6f, - 0x6d, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x45, 0x63, 0x68, 0x6f, 0x48, 0x00, - 0x52, 0x08, 0x68, 0x61, 0x73, 0x68, 0x45, 0x63, 0x68, 0x6f, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x60, 0x0a, 0x08, 0x48, 0x61, 0x73, 0x68, 0x45, 0x63, 0x68, - 0x6f, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x55, 0x75, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x55, - 0x75, 0x69, 0x64, 0x12, 0x32, 0x0a, 0x14, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x43, - 0x6f, 0x6e, 0x74, 0x65, 0x74, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x14, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x74, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x12, - 0x33, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x74, 0x73, 0x73, 0x63, 0x6f, 0x6d, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, - 0x67, 0x6e, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x22, 0x29, 0x0a, 0x09, 0x56, 0x61, 0x61, 0x56, 0x31, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x6d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x65, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x65, 0x64, 0x22, - 0x6f, 0x0a, 0x07, 0x55, 0x6e, 0x69, 0x63, 0x61, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x03, 0x74, 0x73, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x73, 0x73, 0x63, 0x6f, 0x6d, - 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x48, - 0x00, 0x52, 0x03, 0x74, 0x73, 0x73, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x61, 0x76, 0x31, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x73, 0x73, 0x63, 0x6f, 0x6d, 0x6d, 0x2e, - 0x76, 0x31, 0x2e, 0x56, 0x61, 0x61, 0x56, 0x31, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x05, - 0x76, 0x61, 0x61, 0x76, 0x31, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x22, 0x77, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x70, 0x61, 0x67, 0x61, 0x74, 0x65, 0x64, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x55, 0x6e, 0x69, 0x63, 0x61, 0x73, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x73, 0x73, 0x63, 0x6f, 0x6d, 0x6d, - 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x69, 0x63, 0x61, 0x73, 0x74, 0x48, 0x00, 0x52, 0x07, 0x55, - 0x6e, 0x69, 0x63, 0x61, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x73, 0x73, 0x63, 0x6f, 0x6d, 0x6d, 0x2e, 0x76, - 0x31, 0x2e, 0x45, 0x63, 0x68, 0x6f, 0x48, 0x00, 0x52, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x42, 0x09, - 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x4d, 0x0a, 0x0a, 0x44, 0x69, 0x72, - 0x65, 0x63, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x3f, 0x0a, 0x04, 0x53, 0x65, 0x6e, 0x64, 0x12, - 0x1d, 0x2e, 0x74, 0x73, 0x73, 0x63, 0x6f, 0x6d, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, - 0x70, 0x61, 0x67, 0x61, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x16, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x01, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x75, 0x73, 0x6f, 0x6e, 0x65, - 0x2f, 0x77, 0x6f, 0x72, 0x6d, 0x68, 0x6f, 0x6c, 0x65, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x70, - 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x73, 0x73, 0x2f, 0x76, 0x31, 0x3b, - 0x74, 0x73, 0x73, 0x63, 0x6f, 0x6d, 0x6d, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, -} - -var ( - file_tsscomm_v1_tsscomm_proto_rawDescOnce sync.Once - file_tsscomm_v1_tsscomm_proto_rawDescData = file_tsscomm_v1_tsscomm_proto_rawDesc -) - -func file_tsscomm_v1_tsscomm_proto_rawDescGZIP() []byte { - file_tsscomm_v1_tsscomm_proto_rawDescOnce.Do(func() { - file_tsscomm_v1_tsscomm_proto_rawDescData = protoimpl.X.CompressGZIP(file_tsscomm_v1_tsscomm_proto_rawDescData) - }) - return file_tsscomm_v1_tsscomm_proto_rawDescData -} - -var file_tsscomm_v1_tsscomm_proto_msgTypes = make([]protoimpl.MessageInfo, 8) -var file_tsscomm_v1_tsscomm_proto_goTypes = []interface{}{ - (*PartyId)(nil), // 0: tsscomm.v1.PartyId - (*TssContent)(nil), // 1: tsscomm.v1.TssContent - (*SignedMessage)(nil), // 2: tsscomm.v1.SignedMessage - (*HashEcho)(nil), // 3: tsscomm.v1.HashEcho - (*Echo)(nil), // 4: tsscomm.v1.Echo - (*VaaV1Info)(nil), // 5: tsscomm.v1.VaaV1Info - (*Unicast)(nil), // 6: tsscomm.v1.Unicast - (*PropagatedMessage)(nil), // 7: tsscomm.v1.PropagatedMessage - (*emptypb.Empty)(nil), // 8: google.protobuf.Empty -} -var file_tsscomm_v1_tsscomm_proto_depIdxs = []int32{ - 1, // 0: tsscomm.v1.SignedMessage.tss_content:type_name -> tsscomm.v1.TssContent - 3, // 1: tsscomm.v1.SignedMessage.hashEcho:type_name -> tsscomm.v1.HashEcho - 2, // 2: tsscomm.v1.Echo.message:type_name -> tsscomm.v1.SignedMessage - 1, // 3: tsscomm.v1.Unicast.tss:type_name -> tsscomm.v1.TssContent - 5, // 4: tsscomm.v1.Unicast.vaav1:type_name -> tsscomm.v1.VaaV1Info - 6, // 5: tsscomm.v1.PropagatedMessage.Unicast:type_name -> tsscomm.v1.Unicast - 4, // 6: tsscomm.v1.PropagatedMessage.Echo:type_name -> tsscomm.v1.Echo - 7, // 7: tsscomm.v1.DirectLink.Send:input_type -> tsscomm.v1.PropagatedMessage - 8, // 8: tsscomm.v1.DirectLink.Send:output_type -> google.protobuf.Empty - 8, // [8:9] is the sub-list for method output_type - 7, // [7:8] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name -} - -func init() { file_tsscomm_v1_tsscomm_proto_init() } -func file_tsscomm_v1_tsscomm_proto_init() { - if File_tsscomm_v1_tsscomm_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_tsscomm_v1_tsscomm_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PartyId); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_tsscomm_v1_tsscomm_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TssContent); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_tsscomm_v1_tsscomm_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SignedMessage); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_tsscomm_v1_tsscomm_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HashEcho); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_tsscomm_v1_tsscomm_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Echo); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_tsscomm_v1_tsscomm_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VaaV1Info); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_tsscomm_v1_tsscomm_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Unicast); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_tsscomm_v1_tsscomm_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PropagatedMessage); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_tsscomm_v1_tsscomm_proto_msgTypes[2].OneofWrappers = []interface{}{ - (*SignedMessage_TssContent)(nil), - (*SignedMessage_HashEcho)(nil), - } - file_tsscomm_v1_tsscomm_proto_msgTypes[6].OneofWrappers = []interface{}{ - (*Unicast_Tss)(nil), - (*Unicast_Vaav1)(nil), - } - file_tsscomm_v1_tsscomm_proto_msgTypes[7].OneofWrappers = []interface{}{ - (*PropagatedMessage_Unicast)(nil), - (*PropagatedMessage_Echo)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_tsscomm_v1_tsscomm_proto_rawDesc, - NumEnums: 0, - NumMessages: 8, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_tsscomm_v1_tsscomm_proto_goTypes, - DependencyIndexes: file_tsscomm_v1_tsscomm_proto_depIdxs, - MessageInfos: file_tsscomm_v1_tsscomm_proto_msgTypes, - }.Build() - File_tsscomm_v1_tsscomm_proto = out.File - file_tsscomm_v1_tsscomm_proto_rawDesc = nil - file_tsscomm_v1_tsscomm_proto_goTypes = nil - file_tsscomm_v1_tsscomm_proto_depIdxs = nil -} diff --git a/node/pkg/proto/tsscomm/v1/tsscomm.pb.gw.go b/node/pkg/proto/tsscomm/v1/tsscomm.pb.gw.go deleted file mode 100644 index b534e782c5..0000000000 --- a/node/pkg/proto/tsscomm/v1/tsscomm.pb.gw.go +++ /dev/null @@ -1,161 +0,0 @@ -// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: tsscomm/v1/tsscomm.proto - -/* -Package tsscommv1 is a reverse proxy. - -It translates gRPC into RESTful JSON APIs. -*/ -package tsscommv1 - -import ( - "context" - "io" - "net/http" - - "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" - "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/proto" -) - -// Suppress "imported and not used" errors -var _ codes.Code -var _ io.Reader -var _ status.Status -var _ = runtime.String -var _ = utilities.NewDoubleArray -var _ = metadata.Join - -func request_DirectLink_Send_0(ctx context.Context, marshaler runtime.Marshaler, client DirectLinkClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var metadata runtime.ServerMetadata - stream, err := client.Send(ctx) - if err != nil { - grpclog.Infof("Failed to start streaming: %v", err) - return nil, metadata, err - } - dec := marshaler.NewDecoder(req.Body) - for { - var protoReq PropagatedMessage - err = dec.Decode(&protoReq) - if err == io.EOF { - break - } - if err != nil { - grpclog.Infof("Failed to decode request: %v", err) - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err = stream.Send(&protoReq); err != nil { - if err == io.EOF { - break - } - grpclog.Infof("Failed to send request: %v", err) - return nil, metadata, err - } - } - - if err := stream.CloseSend(); err != nil { - grpclog.Infof("Failed to terminate client stream: %v", err) - return nil, metadata, err - } - header, err := stream.Header() - if err != nil { - grpclog.Infof("Failed to get header from client: %v", err) - return nil, metadata, err - } - metadata.HeaderMD = header - - msg, err := stream.CloseAndRecv() - metadata.TrailerMD = stream.Trailer() - return msg, metadata, err - -} - -// RegisterDirectLinkHandlerServer registers the http handlers for service DirectLink to "mux". -// UnaryRPC :call DirectLinkServer directly. -// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterDirectLinkHandlerFromEndpoint instead. -func RegisterDirectLinkHandlerServer(ctx context.Context, mux *runtime.ServeMux, server DirectLinkServer) error { - - mux.Handle("POST", pattern_DirectLink_Send_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") - _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - }) - - return nil -} - -// RegisterDirectLinkHandlerFromEndpoint is same as RegisterDirectLinkHandler but -// automatically dials to "endpoint" and closes the connection when "ctx" gets done. -func RegisterDirectLinkHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) - if err != nil { - return err - } - defer func() { - if err != nil { - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - return - } - go func() { - <-ctx.Done() - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - }() - }() - - return RegisterDirectLinkHandler(ctx, mux, conn) -} - -// RegisterDirectLinkHandler registers the http handlers for service DirectLink to "mux". -// The handlers forward requests to the grpc endpoint over "conn". -func RegisterDirectLinkHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { - return RegisterDirectLinkHandlerClient(ctx, mux, NewDirectLinkClient(conn)) -} - -// RegisterDirectLinkHandlerClient registers the http handlers for service DirectLink -// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "DirectLinkClient". -// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "DirectLinkClient" -// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in -// "DirectLinkClient" to call the correct interceptors. -func RegisterDirectLinkHandlerClient(ctx context.Context, mux *runtime.ServeMux, client DirectLinkClient) error { - - mux.Handle("POST", pattern_DirectLink_Send_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/tsscomm.v1.DirectLink/Send", runtime.WithHTTPPathPattern("/tsscomm.v1.DirectLink/Send")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_DirectLink_Send_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_DirectLink_Send_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - return nil -} - -var ( - pattern_DirectLink_Send_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"tsscomm.v1.DirectLink", "Send"}, "")) -) - -var ( - forward_DirectLink_Send_0 = runtime.ForwardResponseMessage -) diff --git a/node/pkg/proto/tsscomm/v1/tsscomm_grpc.pb.go b/node/pkg/proto/tsscomm/v1/tsscomm_grpc.pb.go deleted file mode 100644 index f0490b2831..0000000000 --- a/node/pkg/proto/tsscomm/v1/tsscomm_grpc.pb.go +++ /dev/null @@ -1,138 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. - -package tsscommv1 - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" - emptypb "google.golang.org/protobuf/types/known/emptypb" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -// DirectLinkClient is the client API for DirectLink service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type DirectLinkClient interface { - // Send uses a stream since the clients of this RPC will invoke it multiple times throughout the life of the server. - Send(ctx context.Context, opts ...grpc.CallOption) (DirectLink_SendClient, error) -} - -type directLinkClient struct { - cc grpc.ClientConnInterface -} - -func NewDirectLinkClient(cc grpc.ClientConnInterface) DirectLinkClient { - return &directLinkClient{cc} -} - -func (c *directLinkClient) Send(ctx context.Context, opts ...grpc.CallOption) (DirectLink_SendClient, error) { - stream, err := c.cc.NewStream(ctx, &DirectLink_ServiceDesc.Streams[0], "/tsscomm.v1.DirectLink/Send", opts...) - if err != nil { - return nil, err - } - x := &directLinkSendClient{stream} - return x, nil -} - -type DirectLink_SendClient interface { - Send(*PropagatedMessage) error - CloseAndRecv() (*emptypb.Empty, error) - grpc.ClientStream -} - -type directLinkSendClient struct { - grpc.ClientStream -} - -func (x *directLinkSendClient) Send(m *PropagatedMessage) error { - return x.ClientStream.SendMsg(m) -} - -func (x *directLinkSendClient) CloseAndRecv() (*emptypb.Empty, error) { - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - m := new(emptypb.Empty) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -// DirectLinkServer is the server API for DirectLink service. -// All implementations must embed UnimplementedDirectLinkServer -// for forward compatibility -type DirectLinkServer interface { - // Send uses a stream since the clients of this RPC will invoke it multiple times throughout the life of the server. - Send(DirectLink_SendServer) error - mustEmbedUnimplementedDirectLinkServer() -} - -// UnimplementedDirectLinkServer must be embedded to have forward compatible implementations. -type UnimplementedDirectLinkServer struct { -} - -func (UnimplementedDirectLinkServer) Send(DirectLink_SendServer) error { - return status.Errorf(codes.Unimplemented, "method Send not implemented") -} -func (UnimplementedDirectLinkServer) mustEmbedUnimplementedDirectLinkServer() {} - -// UnsafeDirectLinkServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to DirectLinkServer will -// result in compilation errors. -type UnsafeDirectLinkServer interface { - mustEmbedUnimplementedDirectLinkServer() -} - -func RegisterDirectLinkServer(s grpc.ServiceRegistrar, srv DirectLinkServer) { - s.RegisterService(&DirectLink_ServiceDesc, srv) -} - -func _DirectLink_Send_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(DirectLinkServer).Send(&directLinkSendServer{stream}) -} - -type DirectLink_SendServer interface { - SendAndClose(*emptypb.Empty) error - Recv() (*PropagatedMessage, error) - grpc.ServerStream -} - -type directLinkSendServer struct { - grpc.ServerStream -} - -func (x *directLinkSendServer) SendAndClose(m *emptypb.Empty) error { - return x.ServerStream.SendMsg(m) -} - -func (x *directLinkSendServer) Recv() (*PropagatedMessage, error) { - m := new(PropagatedMessage) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -// DirectLink_ServiceDesc is the grpc.ServiceDesc for DirectLink service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var DirectLink_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "tsscomm.v1.DirectLink", - HandlerType: (*DirectLinkServer)(nil), - Methods: []grpc.MethodDesc{}, - Streams: []grpc.StreamDesc{ - { - StreamName: "Send", - Handler: _DirectLink_Send_Handler, - ClientStreams: true, - }, - }, - Metadata: "tsscomm/v1/tsscomm.proto", -} diff --git a/node/pkg/tss/api.go b/node/pkg/tss/api.go index b316755fd1..7ba5426dc7 100644 --- a/node/pkg/tss/api.go +++ b/node/pkg/tss/api.go @@ -2,103 +2,189 @@ package tss import ( "context" - "crypto/tls" - "crypto/x509" - "time" - - whcommon "github.com/certusone/wormhole/node/pkg/common" - tsscommv1 "github.com/certusone/wormhole/node/pkg/proto/tsscomm/v1" + "encoding/json" + "errors" + "fmt" + "os" + "sync/atomic" + + "github.com/certusone/wormhole/node/pkg/common" + "github.com/certusone/wormhole/node/pkg/guardiansigner" + gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/wormhole-foundation/wormhole/sdk/vaa" "github.com/xlabs/multi-party-sig/pkg/math/curve" - common "github.com/xlabs/tss-common" - "github.com/xlabs/tss-lib/v2/party" + tsscommon "github.com/xlabs/tss-common" + "github.com/xlabs/tss-common/service/signer" + "google.golang.org/grpc" ) -type message interface { - IsBroadcast() bool - GetNetworkMessage() *tsscommv1.PropagatedMessage -} +type SignerConnection interface { + // Connect establishes a connection to the signer service. + // It blocks until the connection is established or an error occurs. + // Can be used as supervisor.Runnable [ensuring rapid restarts on failure]. + Connect(ctx context.Context) error -type Sendable interface { - message - GetDestinations() []*Identity + Signer + Gossiper - cloneSelf() Sendable // deep copy to avoid race condition in tests (ensuring no one shares the same sendable). + // informs the signerConnection on key updates. + UpdateKeys(ctx context.Context, req *signer.UpdateKeysRequest) error } -type Incoming interface { - message - IsUnicast() bool - GetSource() *Identity +// The TssGossiper interface represents the ability to listen and publish new TSS related gossip to the network. +// It is specifically used to abstract the interaction of the TSS signer with the P2P layer for VAA gossiping. +type Gossiper interface { + // tells the gossiper on new TSS message to inspect and learn about. (e.g., new VAA from a leader entity.) + Inform(*gossipv1.TSSGossipMessage) error - toUnicast() *tsscommv1.Unicast - toBroadcastMsg() *tsscommv1.Echo + // Outbound returns a channel that produces TSS gossip messages to be sent to the network. + // These messages are typically generated by the TSS signer when it needs to share information with other peers. + Outbound() <-chan *gossipv1.TSSGossipMessage // in essences, currently only a leader will produce messages. } -// ReliableMessenger is a component of tss, where it knows how to handle incoming tsscommv1.PropagatedMessage, -// it may produce messages (of type Sendable), which should be delivered to other guardians. -// these Sendable messages are produced by the tss engine, and are needed by the other guardians to -// complete a TSS round. In addition it supplies a server with certificates of any -// party member, including itself. -type ReliableMessenger interface { - // HandleIncomingTssMessage receives a network `message`` and process it using a reliable-broadcast protocol. - HandleIncomingTssMessage(msg Incoming) - ProducedOutputMessages() <-chan Sendable // just need to propagate this through the p2p network - - // Utilities for servers: - GetCertificate() *tls.Certificate // containing secret key. - GetPeers() []*x509.Certificate // containing public keys. - - // FetchPartyId returns the PartyId for a given certificate, it'll use the public key - // in the certificate and match it to the public key expected to be found in `*tsscommv1.PartyId`. - FetchIdentity(cert *x509.Certificate) (*Identity, error) -} - -// Signer is the interface to give any component with the ability to authorise a new threshold signature over a message. +/* +The Signer interface represents a TSS signer service that can be used to request signatures. +*/ type Signer interface { - // for consistency level see https://wormhole.com/docs/build/reference/consistency-levels/ - BeginAsyncThresholdSigningProtocol(vaaDigest []byte, chainID vaa.ChainID, vaaconsistency uint8) error - ProducedSignature() <-chan *common.SignatureData - - GetPublicKey() (curve.Point, error) - GetEthAddress() (ethcommon.Address, error) + /* + AsyncSign starts an asynchronous signing request. + The produced signature can be received from ProducedSignatures() channel. + The request must contain the digest to be signed and the protocol to use. + + If a specific committee is needed, it can be specified via rq.CommitteeMembers. (nil means use pseudo-random committee). + The committee members are specified via their public keys (guardian public keys). + If the signer is not configured to recognize the specified committee members, it'll return an error. + */ + AsyncSign(rq *signer.SignRequest) error + // Outputs signatures as they are produced. doesn't guarantee order. + Response() <-chan *signer.SignResponse + + // TODO: Missing signerService API! The SignerService should support both GetPublicData data and verify. + // GetPublicKey gets the public information of the signer. + GetPublicData(context.Context) (*signer.PublicData, error) + // GetPublicKey gets the public key for the specified protocol type. + GetPublicKey(context.Context, tsscommon.ProtocolType) (curve.Point, error) + // Verify verifies a signature against the provided public data. + Verify(context.Context, *signer.VerifySignatureRequest) error + + // Witness new VAA is used by a LEADER to inform all peers of a new VAA observed on the network and to sign it! + // If this signer is a leader: it'll use the p2p network to tell all peers to sign the + // the context parameter is due to the usage of guardianSigner which may need it for signing. + WitnessNewVaaV1(ctx context.Context, v *vaa.VAA) error + + // GetProtocol provides the mapping from emitter chain ID to TSS protocol type. + // based on the configuration provided during signer creation. + // if no mapping is found, it returns tsscommon.ProtocolFROSTSign (default). + GetProtocol(int) tsscommon.ProtocolType +} - // tells the maximal duration one might wait on a signature to be produced - // (realisticly, it should be produced within a few seconds). - MaxTTL() time.Duration +// Ensure interfaces are implemented. +var ( + _ SignerConnection = (*SignerClient)(nil) + _ Signer = (*SignerClient)(nil) +) - // WitnessNewVaa is a method to witness a new VAA, andto start a signing protocol for it. - WitnessNewVaa(v *vaa.VAA) error +type Parameters struct { + // configurations for the signer client. + Configurations `json:"configurations"` + // Path to the signer service socket (hostname, ip). + SocketPath string `json:"socket_path"` + // grpc dial options to connect to the signer service (including tls.credentials or insecure connection). + DialOpts []grpc.DialOption `json:"-"` // no json + + LeaderAddress ethcommon.Address `json:"leader_address"` + // index of this guardian in the guardian set. should be same for all guardians in the network. + Self ethcommon.Address `json:"self"` + GST *common.GuardianSetState `json:"-"` // no json + GuardianSigner guardiansigner.GuardianSigner `json:"-"` // no json } -type Starter interface { - // Start deploys the component and allocates its resources. - // The context is used to control the lifetime of the component. - Start(ctx context.Context) error +type Configurations struct { + // buffer sizes for internal channels. if no value is provided, choosing default value according to `defaultBufferSize`. + ChannelBufferSizes int `json:"channel_buffer_sizes"` + + // mapping from chain ID to protocol type. + // used to determine which protocol to use when signing VAAs from different chains. + // sets the mapping used in the API call EmitterChainToProtocolMapping. if nil, all chains map to FROST. + ChainToProtocol map[int]tsscommon.ProtocolType `json:"chain_to_protocol"` + + // specifies the threshold size for TSS operations. + // NOTE:[Should match the signer service configuration] + // This isn't for security, just to avoid sending requests + // that will be rejected by the signer. + // + // threshold represents the maximal number that will not be able to sign. For instance, + // if threshold is 2, then 3 or more parties will be able to sign. + ThresholdSize int `json:"threshold_size"` } -type KeyGenerator interface { - Starter - - // demands the usage of a reliable messenger. Either via full Reliable Broadcast, - // or via a hash-broadcast protocol. - ReliableMessenger +const ( + defaultSigningProtocol = tsscommon.ProtocolFROSTSign + defaultBufferSize = 1024 +) - // StartDKG starts a distributed key generation protocol, which will produce a frost.Config. - // Using this config, one can create a frost.Signer. - // this function doesn't change disk stored state. - StartDKG(party.DkgTask) (chan *party.TSSSecrets, error) +// The opts should provide the dial options to connect to the signer service. this includes tls.credentials. +func NewSigner(p Parameters) (*SignerClient, error) { + for _, opt := range p.DialOpts { + if opt == nil { + return nil, errors.New("nil grpc dial option provided") + } + } + + if p.GST == nil { + return nil, errors.New("guardian set state must not be nil") + } + if p.GuardianSigner == nil { + return nil, errors.New("guardian signer must not be nil") + } + if p.SocketPath == "" { + return nil, errors.New("socket path must not be empty") + } + + bufferSize := defaultBufferSize // default + if p.Configurations.ChannelBufferSizes > 0 { + bufferSize = p.Configurations.ChannelBufferSizes + } + + if p.Configurations.ChainToProtocol == nil { + p.Configurations.ChainToProtocol = make(map[int]tsscommon.ProtocolType) + } + + sc := &SignerClient{ + dialOpts: p.DialOpts, + socketPath: p.SocketPath, + conn: &connChans{ + signRequests: make(chan *signer.SignRequest, bufferSize), + signResponses: make(chan *signer.SignResponse, bufferSize), + unaryRequests: make(chan unaryRequest, bufferSize), + }, + connected: atomic.Int64{}, + vaaData: vaaHandling{ + isLeader: p.LeaderAddress == p.Self && p.Self != (ethcommon.Address{}), // only if self is defined. + leaderAddress: p.LeaderAddress, + gst: p.GST, + GuardianSigner: p.GuardianSigner, + gossipOutput: make(chan *gossipv1.TSSGossipMessage, bufferSize), + incomingGossip: make(chan *gossipv1.TSSGossipMessage, bufferSize), + }, + + configurations: p.Configurations, + } + + return sc, nil } -// ReliableTSS represents a TSS engine that can fully support logic of -// reliable broadcast needed for the security of TSS over the network. -type ReliableTSS interface { - Starter - // demands the usage of a reliable messenger. Either via full Reliable Broadcast, - // or via a hash-broadcast protocol. - ReliableMessenger - Signer - - SetGuardianSetState(gs *whcommon.GuardianSetState) error +// LoadFromFile loads the TSS configurations from a JSON file. +func (c *Configurations) LoadFromFile(configurationsPath string) error { + data, err := os.ReadFile(configurationsPath) + if err != nil { + return fmt.Errorf("failed to read tss configurations file: %w", err) + } + err = json.Unmarshal(data, c) + if err != nil { + return fmt.Errorf("failed to unmarshal tss configurations: %w", err) + } + + return nil } diff --git a/node/pkg/tss/api_test.go b/node/pkg/tss/api_test.go new file mode 100644 index 0000000000..b37fbe97b4 --- /dev/null +++ b/node/pkg/tss/api_test.go @@ -0,0 +1,330 @@ +package tss + +import ( + "context" + "crypto/ecdsa" + "crypto/rand" + "errors" + "io" + "net" + "os" + "testing" + "time" + + "github.com/certusone/wormhole/node/pkg/common" + "github.com/certusone/wormhole/node/pkg/guardiansigner" + "github.com/certusone/wormhole/node/pkg/supervisor" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" + "github.com/xlabs/multi-party-sig/pkg/math/curve" + "github.com/xlabs/multi-party-sig/pkg/math/sample" + tsscommon "github.com/xlabs/tss-common" + "github.com/xlabs/tss-common/service/signer" + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +const bufSize = 1024 * 1024 + +type mockSignerServer struct { + signer.UnimplementedSignerServer + signRequests chan *signer.SignRequest + signResponses chan *signer.SignResponse + publicData *signer.PublicData +} + +func (m *mockSignerServer) SignMessage(stream signer.Signer_SignMessageServer) error { + // Handle sending responses + go func() { + for resp := range m.signResponses { + if err := stream.Send(resp); err != nil { + return + } + } + }() + + // Handle receiving requests + for { + req, err := stream.Recv() + if err == io.EOF { + return nil + } + if err != nil { + return err + } + select { + case m.signRequests <- req: + default: + // Drop if full to avoid blocking test + } + } +} + +func (m *mockSignerServer) UpdateKeys(ctx context.Context, req *signer.UpdateKeysRequest) (*signer.UpdateKeysResponse, error) { + return &signer.UpdateKeysResponse{}, nil +} + +func (m *mockSignerServer) GetPublicData(ctx context.Context, req *signer.PublicDataRequest) (*signer.PublicData, error) { + if m.publicData == nil { + return nil, errors.New("no public data") + } + + return m.publicData, nil +} + +func (m *mockSignerServer) VerifySignature(ctx context.Context, req *signer.VerifySignatureRequest) (*signer.VerifySignatureResponse, error) { + return &signer.VerifySignatureResponse{IsValid: true}, nil +} + +func TestNewSignerValidation(t *testing.T) { + key, err := ecdsa.GenerateKey(ethcrypto.S256(), rand.Reader) + require.NoError(t, err) + + gs, err := guardiansigner.GenerateSignerWithPrivatekeyUnsafe(key) + require.NoError(t, err) + + validParams := Parameters{ + SocketPath: "localhost:1234", + DialOpts: []grpc.DialOption{ + grpc.WithTransportCredentials(insecure.NewCredentials()), + }, + GST: common.NewGuardianSetState(nil), + GuardianSigner: gs, + } + + testCases := []struct { + name string + modifier func(p *Parameters) + expectedErr string + }{ + { + name: "Valid parameters", + modifier: func(p *Parameters) {}, + expectedErr: "", + }, + { + name: "Nil DialOption", + modifier: func(p *Parameters) { + p.DialOpts = []grpc.DialOption{nil} + }, + expectedErr: "nil grpc dial option provided", + }, + { + name: "Nil GST", + modifier: func(p *Parameters) { + p.GST = nil + }, + expectedErr: "guardian set state must not be nil", + }, + { + name: "Nil GuardianSigner", + modifier: func(p *Parameters) { + p.GuardianSigner = nil + }, + expectedErr: "guardian signer must not be nil", + }, + { + name: "Empty SocketPath", + modifier: func(p *Parameters) { + p.SocketPath = "" + }, + expectedErr: "socket path must not be empty", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + params := validParams + tc.modifier(¶ms) + _, err := NewSigner(params) + if tc.expectedErr == "" { + require.NoError(t, err) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectedErr) + } + }) + } +} + +func TestSignerClient(t *testing.T) { + a := require.New(t) + + lis, err := net.Listen("tcp", "localhost:0") // listen on a random available port + a.NoError(err) + + s := grpc.NewServer() + + // Generate valid points for testing GetPublicKey + grp := curve.Secp256k1{} + priv := sample.Scalar(rand.Reader, grp) + pub := priv.ActOnBase() + pubBytes, err := grp.MarshalPoint(pub) + a.NoError(err) + + mock := &mockSignerServer{ + signRequests: make(chan *signer.SignRequest, 10), + signResponses: make(chan *signer.SignResponse, 10), + publicData: &signer.PublicData{ + FrostPublicData: pubBytes, + EcdsaPublicData: pubBytes, + }, + } + signer.RegisterSignerServer(s, mock) + + go func() { + if err := s.Serve(lis); err != nil { + t.Errorf("Server exited with error: %v", err) + } + }() + defer s.Stop() + + key, err := ecdsa.GenerateKey(ethcrypto.S256(), rand.Reader) + a.NoError(err) + + gs, err := guardiansigner.GenerateSignerWithPrivatekeyUnsafe(key) + a.NoError(err) + + // Create client with bufconn dialer and insecure credentials + client, err := NewSigner(Parameters{ + SocketPath: lis.Addr().String(), + DialOpts: []grpc.DialOption{ + grpc.WithTransportCredentials(insecure.NewCredentials()), + }, + // LeaderIndex: 0, + // Self: 0, + GST: common.NewGuardianSetState(nil), + GuardianSigner: gs, + }) + a.NoError(err) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Connect in background, ctx will be used for cancellation + supervisor.New(ctx, zap.L(), client.Connect) + + waitForConnection(t, client) + + t.Run("GetPublicData", func(t *testing.T) { + pd, err := client.GetPublicData(ctx) + a.NoError(err) + + a.Equal(pd.FrostPublicData, pubBytes) + }) + + t.Run("AsyncSign", func(t *testing.T) { + a.Error(client.AsyncSign(nil)) + a.Error(client.AsyncSign(&signer.SignRequest{})) // malformed + a.Error(client.AsyncSign(&signer.SignRequest{Digest: []byte("12341414123"), Protocol: ""})) // malformed (missing protocol) + + req := &signer.SignRequest{Digest: []byte("test_digest"), Protocol: tsscommon.ProtocolECDSASign.ToString()} + a.NoError(client.AsyncSign(req)) + + // Verify request received by server + select { + case received := <-mock.signRequests: + a.Equal(string(received.Digest), "test_digest") + case <-time.After(time.Second): + t.Fatal("timeout waiting for sign request") + } + + // Send response from server + resp := &signer.SignResponse{ + Response: &signer.SignResponse_Signature{ + Signature: &tsscommon.SignatureData{ + Signature: []byte("test_signature"), + }, + }, + } + mock.signResponses <- resp + + // Verify response received by client + select { + case received := <-client.Response(): + sig := received.GetSignature() + a.NotNil(sig) + a.Equal(string(sig.Signature), "test_signature") + + case <-time.After(time.Second): + t.Fatal("timeout waiting for sign response") + } + }) + + t.Run("Verify", func(t *testing.T) { + err := client.Verify(ctx, &signer.VerifySignatureRequest{}) + a.NoError(err) + }) + + t.Run("UpdateKeys", func(t *testing.T) { + err := client.UpdateKeys(ctx, &signer.UpdateKeysRequest{}) + a.NoError(err) + + err = client.UpdateKeys(ctx, nil) + a.Error(err) + a.Equal(err, errNilRequest) + }) + + t.Run("GetPublicKey", func(t *testing.T) { + pk, err := client.GetPublicKey(ctx, tsscommon.ProtocolFROSTSign) + a.NoError(err) + a.True(pk.Equal(pub)) + + pk, err = client.GetPublicKey(ctx, tsscommon.ProtocolECDSASign) + a.NoError(err) + a.True(pk.Equal(pub)) + + _, err = client.GetPublicKey(ctx, tsscommon.ProtocolType("unknown")) + a.Error(err) + }) +} + +func TestConfigurations_LoadFromFile(t *testing.T) { + t.Run("Valid", func(t *testing.T) { + f, err := os.CreateTemp("", "tss-config-*.json") + require.NoError(t, err) + defer os.Remove(f.Name()) + _, err = f.WriteString(`{"threshold_size": 2}`) + require.NoError(t, err) + f.Close() + + c := &Configurations{} + err = c.LoadFromFile(f.Name()) + require.NoError(t, err) + require.Equal(t, 2, c.ThresholdSize) + }) + + t.Run("InvalidPath", func(t *testing.T) { + c := &Configurations{} + err := c.LoadFromFile("/nonexistent/config.json") + require.Error(t, err) + require.Contains(t, err.Error(), "failed to read tss configurations file") + }) + + t.Run("InvalidContent", func(t *testing.T) { + f, err := os.CreateTemp("", "tss-config-*.json") + require.NoError(t, err) + defer os.Remove(f.Name()) + _, err = f.WriteString(`invalid-json`) + require.NoError(t, err) + f.Close() + + c := &Configurations{} + err = c.LoadFromFile(f.Name()) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to unmarshal tss configurations") + }) +} + +func waitForConnection(t *testing.T, client *SignerClient) { + t.Log("waiting for client to connect...") + for range 5 { + if client.isConnected() { + break + } + time.Sleep(50 * time.Millisecond) + } + + require.True(t, client.isConnected(), "client should be connected") +} diff --git a/node/pkg/tss/broadcast_logic.go b/node/pkg/tss/broadcast_logic.go deleted file mode 100644 index 3bba790f92..0000000000 --- a/node/pkg/tss/broadcast_logic.go +++ /dev/null @@ -1,351 +0,0 @@ -// The following code implements a hash broadcast protocol that guarantees that a -// -// message has been seen by at least f+1 honest guardians. -// -// Unlike Bracha’s reliable broadcast, it doesn’t guarantee the message’s -// delivery (some honest nodes may deliver it, while others may not). -// -// In essence, the protocol ensures that if an honest guardian delivers a -// message `x`, no other honest guardian will deliver a different -// message `y` for the same round and session. - -package tss - -import ( - "fmt" - "sync" - "time" - - tsscommv1 "github.com/certusone/wormhole/node/pkg/proto/tsscomm/v1" - - common "github.com/xlabs/tss-common" -) - -// voterId is comprised from the id and key of the signer, should match the guardians (in GuardianStorage) id and key. -type voterId string - -type broadcastMessage interface { - // We use the UUID to distinguish between messages the - // broadcast algorithm handles. - // When supporting a new uuid, take careful considertaions. - // for instance, TSS messages create their uuid from values that - // make each message unique, but also ensure the broadcast can - // detect equivication attacks. - getUUID(loadDistKey []byte) uuid -} - -type serialzeable interface { - serialize() []byte -} - -// Deliverable represents what the broadcast protocol returns. -// It is a message that its hash has been seen by at least 2f+1 guardians. -type deliverable interface { - serialzeable - - deliver(*Engine) error -} - -type deliverableMessage struct { - deliverable -} - -// serializeableMessage is a helper struct that converts a serializable message into a broadcastMessage. -type serializeableMessage struct { - serialzeable -} - -func (s *serializeableMessage) getUUID(loadDistKey []byte) uuid { - return uuid(hash(append(s.serialize(), loadDistKey...))) -} - -// implementing broadcastMessage interface for any deliverableMessage. -func (s *deliverableMessage) getUUID(loadDistKey []byte) uuid { - return (&serializeableMessage{s}).getUUID(loadDistKey) -} - -// serializeables -type tssMessageWrapper struct { - common.Message -} - -func (t *tssMessageWrapper) serialize() []byte { - return serializeTSSMessage(t.Message) -} - -type parsedTssContent struct { - common.ParsedMessage - signingRound -} - -// broadcastable only struct (not deliverable or serializable): -type parsedHashEcho struct { - *tsscommv1.HashEcho -} - -func (p *parsedHashEcho) getUUID(loadDistKey []byte) uuid { - uid := uuid{} - copy(uid[:], p.HashEcho.SessionUuid) - - return uid -} - -// This function sets a message's sessionID. It is crucial for SECURITY to ensure no equivocation -// -// We don't add the content of the message to the uuid, instead we collect all data that can put this message in a context. -// this is used by the broadcast protocol to check no two messages from the same sender will be used to update the full party -// in the same round for the specific session of the protocol. -func serializeTSSMessage(msg common.Message) []byte { - // The TackingID of a parsed message is tied to the run of the protocol for a single - // signature, thus we use it as a sessionID. - messageTrackingID := [trackingIDHexStrSize]byte{} - copy(messageTrackingID[:], []byte(msg.WireMsg().GetTrackingID().ToString())) - - fromId := [pemKeySize]byte{} - copy(fromId[:], []byte(msg.GetFrom().GetID())) - - // Adding the Message type allows the same sender to send messages for different rounds. - // but, sender j is not allowed to send two different messages to the same round. - tp := msg.Type() - - msgType := make([]byte, tssProtoMessageSize) - copy(msgType[:], tp[:]) - - d := make([]byte, 0, len(tssContentDomain)+trackingIDHexStrSize+pemKeySize) - - d = append(d, tssContentDomain...) - d = append(d, messageTrackingID[:]...) - d = append(d, fromId[:]...) - d = append(d, msgType[:]...) - - return d -} - -func (p *parsedTssContent) wrapError(err error) error { - if p == nil { - return err - } - - return logableError{ - cause: err, - trackingId: p.getTrackingID(), - round: p.signingRound, - } -} - -func (p *parsedTssContent) getTrackingID() *common.TrackingID { - if p == nil { - return nil - } - - if p.ParsedMessage == nil { - return nil - } - - if p.WireMsg() == nil { - return nil - } - - return p.WireMsg().GetTrackingID() -} - -func (p *parsedTssContent) deliver(t *Engine) error { - if err := t.feedIncomingToFp(p.ParsedMessage); err != nil { - return p.wrapError(fmt.Errorf("failed to update the full party: %w", err)) - } - - return nil -} - -func (p *parsedTssContent) serialize() []byte { - return serializeTSSMessage(p.ParsedMessage) -} - -type broadcaststate struct { - timeReceived time.Time - verifiedDigest *digest - - deliverable deliverable - - votes map[voterId]bool - // if set to true: don't echo again, even if received from original sender. - echoedAlready bool - // if set to true: don't deliver again. - alreadyDelivered bool - - mtx *sync.Mutex -} - -func (t *Engine) getDeliverableIfAllowed(s *broadcaststate) deliverable { - f := t.GuardianStorage.getMaxExpectedFaults() - - s.mtx.Lock() - defer s.mtx.Unlock() - - if s.alreadyDelivered { - return nil - } - - if len(s.votes) < f*2+1 { - return nil - } - - if s.deliverable == nil { - return nil - } - - s.alreadyDelivered = true - - return s.deliverable -} - -var ErrEquivicatingGuardian = fmt.Errorf("equivication, guardian sent two different messages for the same round and session") - -func (t *Engine) updateState(s *broadcaststate, parsed broadcastMessage, unparsedContent Incoming) (shouldEcho bool, err error) { - unparsedSignedMessage := unparsedContent.toBroadcastMsg().Message - echoer := unparsedContent.GetSource() - - senderIndex := SenderIndex(unparsedSignedMessage.Sender) - id, err := t.GuardianStorage.fetchIdentityFromIndex(senderIndex) - if err != nil { - return false, fmt.Errorf("failed to fetch sender identity: %w", err) - } - - isMsgSrc := id.Pid.Equals(echoer.Pid) - - _, isEcho := unparsedSignedMessage.Content.(*tsscommv1.SignedMessage_HashEcho) - - s.mtx.Lock() - defer s.mtx.Unlock() - - // the incoming message is valid when this function is reached. - // So if the incomming message is not an echo, we can set the deliverable (which we'll return once we should deliver). - if !isEcho && s.deliverable == nil { - deliverable, ok := parsed.(deliverable) - if ok { // has actual content. - s.deliverable = deliverable - } - } - - s.votes[voterId(echoer.KeyPEM)] = true - if s.echoedAlready { - return shouldEcho, err - } - - if isMsgSrc && !isEcho { - s.echoedAlready = true - shouldEcho = true - - return shouldEcho, err - } - - return shouldEcho, err -} - -func (st *GuardianStorage) getMaxExpectedFaults() int { - // since threshold is 2/3*n+1, f = (st.Threshold - 1) / 2 - // in our case st.Threshold is not inclusive, so we don't need to subtract 1. - return (st.Threshold) / 2 // this is the floor of the result. -} - -// broadcastInspection is the main function that handles the hash-broadcast algorithm. -// it returns whether a message should be echoed, delivered, or an error. -// Once a deliverable is returned from this function, it can be used by the caller. -func (t *Engine) broadcastInspection(parsed broadcastMessage, msg Incoming) (bool, deliverable, error) { - state := t.fetchOrCreateState(parsed) - - if err := t.validateBroadcastState(state, parsed, msg); err != nil { - return false, nil, err - } - - shouldBroadcast, err := t.updateState(state, parsed, msg) - if err != nil { - return false, nil, err - } - - if shouldBroadcast && msg.toBroadcastMsg().Message.Sender == uint32(t.Self.CommunicationIndex) { - shouldBroadcast = false // no need to echo if we're the original sender. - } - - return shouldBroadcast, t.getDeliverableIfAllowed(state), nil -} - -func (t *Engine) fetchOrCreateState(parsed broadcastMessage) *broadcaststate { - uuid := parsed.getUUID(t.LoadDistributionKey) - - state := &broadcaststate{ - timeReceived: time.Now(), - verifiedDigest: nil, - deliverable: nil, - - votes: make(map[voterId]bool), - echoedAlready: false, - alreadyDelivered: false, - mtx: &sync.Mutex{}, - } - - t.mtx.Lock() - defer t.mtx.Unlock() - - st, ok := t.received[uuid] - if !ok { - t.received[uuid] = state - st = state - } - - return st -} - -func (t *Engine) validateBroadcastState(s *broadcaststate, parsed broadcastMessage, msg Incoming) error { - unparsedSignedMessage := msg.toBroadcastMsg().Message - src := msg.GetSource() - - // locking a single state. Can be reached by multiple echoers. - s.mtx.Lock() - defer s.mtx.Unlock() - - _, isDeliverable := parsed.(deliverable) - // only non-echo messages should have the same sender as the source. (Echo messages should have different source then original sender). - if isDeliverable { - index := SenderIndex(unparsedSignedMessage.Sender) - - senderID, err := t.GuardianStorage.fetchIdentityFromIndex(index) - if err != nil { - return fmt.Errorf("failed to fetch sender identity: %w", err) - } - - if !senderID.Pid.Equals(src.Pid) { - return fmt.Errorf("any non echo message should have the same sender as the source") - } - } - - uid := parsed.getUUID(t.LoadDistributionKey) - - signedMsgHash := hashSignedMessage(unparsedSignedMessage) - - // verify incoming - if s.verifiedDigest == nil { - if err := t.verifySignedMessage(uid, unparsedSignedMessage); err != nil { - return err - } - - s.verifiedDigest = &signedMsgHash - - } else if *s.verifiedDigest != signedMsgHash { - // TODO: VaaV1 leader can cause two signatures with the same trackingID, to run. - // Perhaps we need to add auxilary data to the uuid to remove this bug. - if err := t.verifySignedMessage(uid, unparsedSignedMessage); err != nil { - // two different digest and bad signature. - return fmt.Errorf("caught bad behaviour: Echoer %v sent a digest that can't be verified", src.Hostname) - } - - // no error and two different digests: - // NOTICE: this error will be triggered when a leader has no valid mapping from VAAv1 addresses/ publicKeys to - // the VAAv2 keys/ identities. As a result, the leader sends effectively the same VAA to be signed by the - // exact same guardians, which will result in TWO attempts to sign, creating a false 'equivocation' error. - return fmt.Errorf("equivication attack detected. Sender %v sent two different digests. "+ - "Time %v passed from prev msg", unparsedSignedMessage.Sender, time.Since(s.timeReceived)) - } - - return nil -} diff --git a/node/pkg/tss/client.go b/node/pkg/tss/client.go new file mode 100644 index 0000000000..52dcdce581 --- /dev/null +++ b/node/pkg/tss/client.go @@ -0,0 +1,403 @@ +package tss + +import ( + "context" + "errors" + "sync/atomic" + + "github.com/certusone/wormhole/node/pkg/common" + "github.com/certusone/wormhole/node/pkg/guardiansigner" + gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1" + "github.com/certusone/wormhole/node/pkg/supervisor" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/gogo/status" + "github.com/xlabs/multi-party-sig/pkg/math/curve" + tsscommon "github.com/xlabs/tss-common" + "github.com/xlabs/tss-common/service/signer" + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/protobuf/proto" +) + +type vaaHandling struct { + isLeader bool + leaderAddress ethcommon.Address + + gst *common.GuardianSetState + guardiansigner.GuardianSigner + + gossipOutput chan *gossipv1.TSSGossipMessage // channel to send outgoing gossip messages. + incomingGossip chan *gossipv1.TSSGossipMessage // channel to receive incoming gossip messages. +} + +type SignerClient struct { + // immutable fields: + dialOpts []grpc.DialOption + socketPath string + + // used to communicate with the signer service. + conn *connChans + + vaaData vaaHandling + + connected atomic.Int64 // 0 is not connected, 1 is connected. + + configurations Configurations +} + +type unaryResult struct { + item proto.Message + err error +} + +type unaryRequest struct { + item proto.Message + responseChan chan unaryResult +} + +// This might fail suddenly. if it does, the runnable should restart it. +type connChans struct { + // streams for sign request/response. + signRequests chan *signer.SignRequest + signResponses chan *signer.SignResponse + + // unary requests (GetPublicData, VerifySignature). + unaryRequests chan unaryRequest +} + +type signatureStream grpc.BidiStreamingClient[signer.SignRequest, signer.SignResponse] + +const ( + notConnected = iota + connected +) + +var ( + ErrSignerClientSignRequestChannelFull = errors.New("signer client sign request channel is full") + ErrSignerClientNil = errors.New("tss signer client is nil") + errInvalidUnaryResponseError = errors.New("internal error: invalid response type from signer service") + errMalformedSignRequest = errors.New("malformed sign request") +) + +// a blocking call that connects to the signer service and maintains the connection. +// +// Connect implements a connection that can be used for the supervisor.Runnable interface. +// it connects to the signer service, forwards requests from the in channel, and outputs responses to the out channel. +// It runs until the context is cancelled or an error occurs. +// (expects the supervisor to restart it on failure). +func (s *SignerClient) Connect(ctx context.Context) error { + return s.connect(ctx, supervisor.Logger(ctx).Named("tss-signer-connection")) +} + +func (s *SignerClient) GetProtocol(chainID int) tsscommon.ProtocolType { + if s == nil { + return defaultSigningProtocol + } + + protocol, ok := s.configurations.ChainToProtocol[chainID] + if !ok { + return defaultSigningProtocol + } + + return protocol +} + +// AsyncSign implements Signer. +func (s *SignerClient) AsyncSign(rq *signer.SignRequest) error { + if s == nil { + return ErrSignerClientNil + } + + if rq == nil || len(rq.Digest) == 0 || rq.Protocol == "" { + return errMalformedSignRequest + } + + select { + case s.conn.signRequests <- rq: + return nil + default: + return ErrSignerClientSignRequestChannelFull + } +} + +// GetPublicData implements Signer. +func (s *SignerClient) GetPublicData(ctx context.Context) (*signer.PublicData, error) { + if s == nil { + return nil, ErrSignerClientNil + } + + response, err := s.sendUnaryRequest(ctx, &signer.PublicDataRequest{}) + if err != nil { + return nil, err + } + + publicData, ok := response.(*signer.PublicData) + if !ok { + return nil, errInvalidUnaryResponseError + } + + return publicData, nil +} + +var errNilRequest = errors.New("nil request") + +func (s *SignerClient) UpdateKeys(ctx context.Context, req *signer.UpdateKeysRequest) error { + if s == nil { + return ErrSignerClientNil + } + if req == nil { + return errNilRequest + } + + res, err := s.sendUnaryRequest(ctx, req) + if err != nil { + return err + } + + // unused response, but validate type. + if _, ok := res.(*signer.UpdateKeysResponse); !ok { + return errInvalidUnaryResponseError + } + + return nil +} + +func (s *SignerClient) GetPublicKey(ctx context.Context, protocol tsscommon.ProtocolType) (curve.Point, error) { + if s == nil { + return nil, ErrSignerClientNil + } + + publicData, err := s.GetPublicData(ctx) + if err != nil { + return nil, err + } + + grp := curve.Secp256k1{} // supporting only secp256k1 for now. + switch protocol { + case tsscommon.ProtocolECDSASign: + return grp.UnmarshalPoint(publicData.EcdsaPublicData) + case tsscommon.ProtocolFROSTSign: + return grp.UnmarshalPoint(publicData.FrostPublicData) + default: + return nil, errors.New("unsupported protocol type for public key retrieval") + } +} + +// outputs the SignerService responses. +func (s *SignerClient) Response() <-chan *signer.SignResponse { + if s == nil { + return nil // ensure we don't panic, but return nil channel (which blocks forever, and ignored in select). + } + + return s.conn.signResponses +} + +// Verify implements Signer. +func (s *SignerClient) Verify(ctx context.Context, toVerify *signer.VerifySignatureRequest) error { + if s == nil { + return ErrSignerClientNil + } + if toVerify == nil { + return errNilRequest + } + + response, err := s.sendUnaryRequest(ctx, toVerify) + if err != nil { + return err + } + + verifyResult, ok := response.(*signer.VerifySignatureResponse) + if !ok { + return errInvalidUnaryResponseError + } + + if !verifyResult.IsValid { + return errors.New("signature verification failed") + } + + return nil // no error. signature is valid. +} + +func (s *SignerClient) sendUnaryRequest(ctx context.Context, request proto.Message) (proto.Message, error) { + chn := make(chan unaryResult, 1) + + select { + case s.conn.unaryRequests <- unaryRequest{ + item: request, + responseChan: chn, + }: + case <-ctx.Done(): + return nil, ctx.Err() + } + + select { + case response := <-chn: + if response.err != nil { + return nil, response.err + } + + if response.item == nil { + return nil, errors.New("internal error: nil response from signer service") + } + + return response.item, nil + case <-ctx.Done(): + return nil, ctx.Err() + } +} + +// mainly used for tests. +func (s *SignerClient) isConnected() bool { + if s == nil { + return false + } + + return s.connected.Load() == connected +} + +func (s *SignerClient) connect(ctx context.Context, logger *zap.Logger) error { + ctx, cancel := context.WithCancel(ctx) + defer cancel() // we cancel on exit to ensure all goroutines exit. + + logger.Info("connecting to signer service...") + + // setup conn: + cc, err := grpc.NewClient(s.socketPath, s.dialOpts...) + if err != nil { + logger.Error("connecting to signer service failed", zap.Error(err)) + + return err + } + defer cc.Close() + + client := signer.NewSignerClient(cc) + + // Setting up the stream for signing requests and responses. + stream, err := client.SignMessage(ctx) + if err != nil { + logger.Error("stream setup failed", zap.Error(err)) + + return err + } + defer stream.CloseSend() + + logger.Info("connection to signer service established") + + s.connected.Store(connected) + defer s.connected.Store(notConnected) + + // buffer to avoid goroutine leaks. + errchan := make(chan error, 3) + + go s.receivingStream(ctx, logger, stream, errchan) + go s.sendingStream(ctx, stream, errchan) + go s.unaryRequestsHandler(ctx, client, logger, errchan) + go s.gossipListener(ctx, logger) + + supervisor.Signal(ctx, supervisor.SignalHealthy) + + select { + case <-ctx.Done(): + return ctx.Err() + case err := <-errchan: + logger.Error("closing connection", zap.Error(err)) + + return err + } +} + +func (s *SignerClient) receivingStream(ctx context.Context, logger *zap.Logger, stream signatureStream, errchan chan<- error) { + for { + resp, err := stream.Recv() + if err != nil { + errchan <- err // error from stream is stream-fatal. + + return + } + + select { + case <-ctx.Done(): + return + case s.conn.signResponses <- resp: // forward response to consumer. + default: + // drop response if channel is full to avoid blocking. This is not ideal, but prevents deadlocks. + // log as an error, since it indicates that the consumer is not keeping up. + logger.Error("signResponses channel full, dropping response", zap.Stringer("response", resp)) + } + } +} + +// responsible to send sign requests to the signer-service. +func (s *SignerClient) sendingStream(ctx context.Context, stream signatureStream, errchan chan<- error) { + for { + select { + case <-ctx.Done(): // context cancelled, or error from other peer. + return + case rq := <-s.conn.signRequests: + if err := stream.Send(rq); err != nil { + errchan <- err // error from stream is stream-fatal. + + return + } + } + } +} + +// responsible to receive unary requests and send the to the signer-service for processing. +func (s *SignerClient) unaryRequestsHandler(ctx context.Context, client signer.SignerClient, logger *zap.Logger, errchan chan<- error) { + for { + select { + case <-ctx.Done(): + return + case urq := <-s.conn.unaryRequests: + if urq.item == nil { + continue // malformed request, ignore. + } + + var resp proto.Message + var errResponse error + + switch req := urq.item.(type) { + case *signer.PublicDataRequest: + resp, errResponse = client.GetPublicData(ctx, req) + case *signer.VerifySignatureRequest: + resp, errResponse = client.VerifySignature(ctx, req) + case *signer.UpdateKeysRequest: + resp, errResponse = client.UpdateKeys(ctx, req) + default: + errResponse = errors.New("unknown unary request type") + } + + select { + case urq.responseChan <- unaryResult{item: resp, err: errResponse}: + default: + logger.Error("unary response channel full, dropping response", zap.Any("response", resp)) + } + + if isFatalError(errResponse) { + errchan <- errResponse + + return + } + } + } +} + +func isFatalError(err error) bool { + if err == nil { + return false + } + + st, ok := status.FromError(err) + if !ok { + return false + } + + switch st.Code() { + case codes.Unavailable, codes.Internal: + return true + default: + return false + } +} diff --git a/node/pkg/tss/client_test.go b/node/pkg/tss/client_test.go new file mode 100644 index 0000000000..cbce7cf31e --- /dev/null +++ b/node/pkg/tss/client_test.go @@ -0,0 +1,134 @@ +package tss + +import ( + "context" + "errors" + "testing" + "time" + + gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1" + "github.com/stretchr/testify/assert" + "github.com/xlabs/tss-common/service/signer" + "go.uber.org/zap" +) + +func TestAsyncSign_ChannelFull(t *testing.T) { + client := &SignerClient{ + conn: &connChans{ + signRequests: make(chan *signer.SignRequest, 1), + }, + } + + // Fill the channel + client.conn.signRequests <- &signer.SignRequest{} + + // Attempt to sign + req := &signer.SignRequest{Digest: []byte("digest"), Protocol: "protocol"} + err := client.AsyncSign(req) + + assert.Error(t, err) + assert.Equal(t, ErrSignerClientSignRequestChannelFull, err) +} + +func TestGetPublicData_NilClient(t *testing.T) { + var client *SignerClient + _, err := client.GetPublicData(context.Background()) + assert.Error(t, err) + assert.Equal(t, ErrSignerClientNil, err) +} + +func TestVerify_NilClient(t *testing.T) { + var client *SignerClient + err := client.Verify(context.Background(), &signer.VerifySignatureRequest{}) + assert.Error(t, err) + assert.Equal(t, ErrSignerClientNil, err) +} + +func TestVerify_NilRequest(t *testing.T) { + client := &SignerClient{} + err := client.Verify(context.Background(), nil) + assert.Error(t, err) + assert.Equal(t, errNilRequest, err) +} + +func TestSendUnaryRequest_ContextCancelled(t *testing.T) { + client := &SignerClient{ + conn: &connChans{ + unaryRequests: make(chan unaryRequest), // Unbuffered + }, + } + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + _, err := client.sendUnaryRequest(ctx, &signer.PublicDataRequest{}) + assert.Error(t, err) + assert.Equal(t, context.Canceled, err) +} + +func TestSendUnaryRequest_ResponseError(t *testing.T) { + client := &SignerClient{ + conn: &connChans{ + unaryRequests: make(chan unaryRequest, 1), + }, + } + + go func() { + req := <-client.conn.unaryRequests + req.responseChan <- unaryResult{ + err: errors.New("mock error"), + } + }() + + _, err := client.sendUnaryRequest(context.Background(), &signer.PublicDataRequest{}) + assert.Error(t, err) + assert.Equal(t, "mock error", err.Error()) +} + +func TestSendUnaryRequest_NilResponseItem(t *testing.T) { + client := &SignerClient{ + conn: &connChans{ + unaryRequests: make(chan unaryRequest, 1), + }, + } + + go func() { + req := <-client.conn.unaryRequests + req.responseChan <- unaryResult{ + item: nil, + err: nil, + } + }() + + _, err := client.sendUnaryRequest(context.Background(), &signer.PublicDataRequest{}) + assert.Error(t, err) + assert.Contains(t, err.Error(), "internal error: nil response") +} + +func TestUnaryRequestsHandler_UnknownRequest(t *testing.T) { + client := &SignerClient{ + conn: &connChans{ + unaryRequests: make(chan unaryRequest, 1), + }, + } + logger := zap.NewNop() + errChan := make(chan error, 1) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + respChan := make(chan unaryResult, 1) + client.conn.unaryRequests <- unaryRequest{ + item: &gossipv1.Heartbeat{}, // Invalid type + responseChan: respChan, + } + + go client.unaryRequestsHandler(ctx, nil, logger, errChan) + + select { + case res := <-respChan: + assert.Error(t, res.err) + assert.Contains(t, res.err.Error(), "unknown unary request type") + case <-time.After(time.Second): + t.Fatal("timeout waiting for response") + } +} diff --git a/node/pkg/tss/cnfgs.go b/node/pkg/tss/cnfgs.go deleted file mode 100644 index b6d174b7c8..0000000000 --- a/node/pkg/tss/cnfgs.go +++ /dev/null @@ -1,226 +0,0 @@ -package tss - -import ( - "bytes" - "crypto/ecdsa" - "crypto/tls" - "crypto/x509" - "encoding/gob" - "encoding/json" - "fmt" - "os" - - "github.com/certusone/wormhole/node/pkg/tss/internal" - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/xlabs/multi-party-sig/pkg/math/curve" - "github.com/xlabs/multi-party-sig/protocols/frost" - common "github.com/xlabs/tss-common" - "github.com/xlabs/tss-lib/v2/party" -) - -func init() { - // gob registrations for possible curve.Scalar/curve.Point implementations. - // gob has trouble unmarshalling interface types without a one to one mapping to a concrete type. - gob.Register(&curve.Secp256k1Scalar{}) - gob.Register(&curve.Secp256k1Point{}) - gob.Register(&party.TSSSecrets{}) -} - -func (s *GuardianStorage) unmarshalFromJSON(storageData []byte) error { - if err := json.Unmarshal(storageData, &s); err != nil { - return err - } - - if s.PrivateKey == nil { - return fmt.Errorf("TlsPrivateKey is nil") - } - - if len(s.IdentitiesKeep.Identities) == 0 { - return fmt.Errorf("no guardians array given") - } - - if s.Threshold > len(s.IdentitiesKeep.Identities) { - return fmt.Errorf("threshold is higher than the number of guardians") - } - - return s.attemptLoadTssSecrets() -} - -func (s *GuardianStorage) attemptLoadTssSecrets() error { - if s.TSSSecrets == nil { - return nil - } - - buff := bytes.NewBuffer(s.TSSSecrets) - dec := gob.NewDecoder(buff) - - cnf := &party.TSSSecrets{ - Config: frost.EmptyConfig(curve.Secp256k1{}), - } - - if err := dec.Decode(cnf); err != nil { - return fmt.Errorf("error unmarshalling TSSSecrets: %v", err) - } - - if len(cnf.VerificationShares.Points) != len(s.IdentitiesKeep.Identities) { - return fmt.Errorf("number of verification shares does not match number of guardians") - } - - s.frostconf = cnf.Config - - return nil -} - -func (s *GuardianStorage) load(storagePath string) error { - if s == nil { - return fmt.Errorf("GuardianStorage is nil") - } - - storageData, err := os.ReadFile(storagePath) - if err != nil { - return err - } - - if err := s.unmarshalFromJSON(storageData); err != nil { - return err - } - - return s.SetInnerFields() -} - -func (s *GuardianStorage) SetInnerFields() error { - signingKey, err := internal.PemToPrivateKey(s.PrivateKey) - if err != nil { - return fmt.Errorf("error parsing tls private key: %v", err) - } - - s.signingKey = signingKey - - pk, err := internal.PemToPublicKey(s.Self.KeyPEM) - if err != nil { - return err - } - - if !s.signingKey.PublicKey.Equal(pk) { - return fmt.Errorf("signing key does not match the public key stored in Self.Key") - } - - if !s.signingKey.Curve.IsOnCurve(pk.X, pk.Y) { - return fmt.Errorf("invalid public key, it isn't on the curve") - } - - tlsCert, err := tls.X509KeyPair(s.TlsX509, s.PrivateKey) - if err != nil { - return fmt.Errorf("error loading tls cert: %v", err) - } - - s.tlsCert = &tlsCert - - if err := s.fillAndValidateStoredIdentities(); err != nil { - return err - } - - numGuardians := len(s.IdentitiesKeep.Identities) - - s.IdentitiesKeep.peerCerts = make([]*x509.Certificate, numGuardians) - s.IdentitiesKeep.partyIds = make([]*common.PartyID, numGuardians) - s.IdentitiesKeep.pemkeyToIndex = make(map[string]int) - s.IdentitiesKeep.vaav1PubToIdentity = make(map[ethcommon.Address]int) - s.IdentitiesKeep.partyidToIndex = make(map[string]int) - // Since the guardians are sorted by key, we can use their position as their index. - for i := range numGuardians { - s.IdentitiesKeep.peerCerts[i] = s.IdentitiesKeep.Identities[i].Cert - s.IdentitiesKeep.partyIds[i] = s.IdentitiesKeep.Identities[i].Pid - s.IdentitiesKeep.pemkeyToIndex[string(s.IdentitiesKeep.Identities[i].KeyPEM)] = i - s.IdentitiesKeep.partyidToIndex[string(s.IdentitiesKeep.Identities[i].Pid.GetID())] = i - - if s.IdentitiesKeep.Identities[i].VAAv1PubKey != nil { - s.IdentitiesKeep.vaav1PubToIdentity[*(s.IdentitiesKeep.Identities[i].VAAv1PubKey)] = i - } - } - - if s.LeaderIdentity == nil { - // since the guardians are expected to be sorted already, the first guardian is the leader. - s.LeaderIdentity = s.IdentitiesKeep.Identities[0].KeyPEM - } - - s.isleader = bytes.Equal(s.Self.KeyPEM, s.LeaderIdentity) - - return nil -} - -// validates the stored Identity structs. Ensures that the cert and key are valid and match. -// ensures no nil values are stored. Verifies that the tss-lib.PartyIDs are unique. -func (s *GuardianStorage) fillAndValidateStoredIdentities() error { - uniquePidIDs := make(map[string]struct{}) - - for i, id := range s.Identities { - if id == nil { - return fmt.Errorf("error guardian %v is nil", i) - } - - c, key, err := extractCertAndKeyFromPem(id.CertPem) - if err != nil { - return fmt.Errorf("error parsing guardian %v: %w", i, err) - } - - if id.Pid == nil { - return fmt.Errorf("error guardian %v PartyID is nil", i) - } - - if len(id.Hostname) == 0 { - return fmt.Errorf("error guardian %v hostname is empty", i) - } - - if len(id.Pid.GetID()) == 0 { - return fmt.Errorf("error guardian %v PartyID.Id is empty", i) - } - - if _, ok := uniquePidIDs[id.Pid.GetID()]; ok { - return fmt.Errorf("error guardian %v PartyID.Id is not unique", i) - } - uniquePidIDs[id.Pid.GetID()] = struct{}{} - - // storing the cert and key in the identity struct. - id.Key = key - id.Cert = c - - keypem, err := internal.PublicKeyToPem(key) - if err != nil { - return fmt.Errorf("error converting guardian %v cert's PK to pem: %v", i, err) - } - - id.KeyPEM = keypem - - id.CommunicationIndex = SenderIndex(i) - id.networkname = id.portAndHostToNetName() - - if bytes.Equal(id.KeyPEM, s.Self.KeyPEM) { - s.Self = id.Copy() // ensuring Self is set up correctly. - } - } - - return nil -} - -func extractCertAndKeyFromPem(pem PEM) (*x509.Certificate, *ecdsa.PublicKey, error) { - c, err := internal.PemToCert(pem) - if err != nil { - return nil, nil, err - } - - key, ok := c.PublicKey.(*ecdsa.PublicKey) - if !ok { - return nil, nil, fmt.Errorf("cert stored with non-ecdsa publickey") - } - - return c, key, nil -} - -func (s *GuardianStorage) NumGuardians() int { - if s == nil { - return 0 - } - - return len(s.Identities) -} diff --git a/node/pkg/tss/comm/backoff_heap.go b/node/pkg/tss/comm/backoff_heap.go deleted file mode 100644 index bdb2165832..0000000000 --- a/node/pkg/tss/comm/backoff_heap.go +++ /dev/null @@ -1,117 +0,0 @@ -package comm - -import ( - "time" - - "github.com/certusone/wormhole/node/pkg/tss/internal" -) - -const ( - maxBackoffTimeModifier = 10 // max backoff attempts before time doesn't increase. - minBackoffTime = time.Millisecond * 100 -) - -// NOT THREAD SAFE! do NOT share between two different goroutines. -// -// This struct is a conjunction of min-heap and timer to create a list of tasks with deadlines cheaply. -// Mainly similar to multiple cases of `time.After(func(){})“, but without using goroutines under the hood for each -// invocation of Enqueue. -type backoffHeap struct { - internal.Ttlheap[*dialWithBackoff] - - alreadyInHeap map[string]bool - attemptsPerPeer map[string]uint64 // on successful dial, reset to 0. -} - -type dialWithBackoff struct { - hostname string - attempt uint64 - - /* - We use this redialTime to calculate when to set timers for redial requests. - - IMPORTANT: - Go 1.19 introduced to `time.Time` type monotonic clocks to ensure local changes to - the system clock does not affect time comparisons methods - like t.After(u), t.Before(u), t.Equal(u), t.Compare(u), and t.Sub(u) - (see time package docs for further information). - - Therefore, we can safely use `time.Time` in this struct to set timers for correctly redialing. - */ - nextRedialTime time.Time -} - -func (d *dialWithBackoff) GetEndTime() time.Time { - return d.nextRedialTime -} - -// Enqueue adds a hostname to the heap, with a new backoff time. -func (d *backoffHeap) Enqueue(hostname string) { - if d.alreadyInHeap[hostname] { - return - } - - if v, ok := d.attemptsPerPeer[hostname]; ok { - if v+1 <= maxBackoffTimeModifier { - v += 1 - } - - d.attemptsPerPeer[hostname] = v - } else { - d.attemptsPerPeer[hostname] = 0 - } - - elem := &dialWithBackoff{ - hostname: hostname, - attempt: d.attemptsPerPeer[hostname], - } - - elem.setBackoff() - - d.alreadyInHeap[hostname] = true - - d.Ttlheap.Enqueue(elem) -} - -func (d *backoffHeap) Dequeue() string { - elem := d.Ttlheap.Dequeue() - if elem == nil { - return "" - } - - d.alreadyInHeap[elem.hostname] = false - - return elem.hostname -} - -func (d *backoffHeap) ResetAttempts(hostname string) { - delete(d.attemptsPerPeer, hostname) -} - -func newBackoffHeap() backoffHeap { - b := backoffHeap{ - Ttlheap: internal.NewTtlHeap[*dialWithBackoff](), - - alreadyInHeap: map[string]bool{}, - attemptsPerPeer: map[string]uint64{}, - } - - return b -} - -func (d *dialWithBackoff) _durationBasedOnNumberOfAttempts() time.Duration { - return minBackoffTime * (1 << uint(d.attempt)) -} - -func (d *dialWithBackoff) setBackoff() { - if d.attempt >= maxBackoffTimeModifier { - d.attempt = maxBackoffTimeModifier - } - - duration := d._durationBasedOnNumberOfAttempts() - if duration < minBackoffTime { - duration = minBackoffTime // ensuring overflow doesn't write minus duration. - } - - d.nextRedialTime = time.Now().Add(duration) -} diff --git a/node/pkg/tss/comm/backoff_test.go b/node/pkg/tss/comm/backoff_test.go deleted file mode 100644 index 193605d071..0000000000 --- a/node/pkg/tss/comm/backoff_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package comm - -import ( - "context" - "testing" - "time" -) - -func TestBackoffRepeats(t *testing.T) { - waiters := newBackoffHeap() - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*60) - defer cancel() - - waiters.Enqueue("test1") - - for range 7 { - dialTo := "" - - select { - case <-waiters.WaitOnTimer(): // waiting on blocker - case <-ctx.Done(): - t.FailNow() - - return - } - dialTo = waiters.Dequeue() - - if dialTo == "" { - continue // skip (nothing to dial to) - } - - waiters.Enqueue(dialTo) // ensuring a retry. - } - -} diff --git a/node/pkg/tss/comm/runnable.go b/node/pkg/tss/comm/runnable.go deleted file mode 100644 index 17dd297624..0000000000 --- a/node/pkg/tss/comm/runnable.go +++ /dev/null @@ -1,156 +0,0 @@ -package comm - -import ( - "context" - "crypto/tls" - "crypto/x509" - "errors" - "fmt" - "net" - "strconv" - - tsscommv1 "github.com/certusone/wormhole/node/pkg/proto/tsscomm/v1" - "github.com/certusone/wormhole/node/pkg/tss" - "go.uber.org/zap" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" -) - -type DirectLink interface { - tsscommv1.DirectLinkServer - - Run(ctx context.Context) error - - // Will wait until either the context is expired, or the number of active connections - // reaches the DirectLink Server target number. - // NOTICE: this function might return, and the server might still lose a connection to a peer. - // This is a best-effort function, and it is not guaranteed that all connections are active - // when it returns. - WaitForConnections(ctx context.Context) error -} - -func NewServer(logger *zap.Logger, tssMessenger tss.ReliableMessenger) (DirectLink, error) { - cert := tssMessenger.GetCertificate() - if cert == nil { - return nil, errors.New("tssMessenger returned nil certificate") - } - - selfID, err := tssMessenger.FetchIdentity(cert.Leaf) - if err != nil { - return nil, fmt.Errorf("failed to fetch self identity from tssMessenger: %w", err) - } - - port := selfID.Port - if port == 0 { - p, err := strconv.Atoi(tss.DefaultPort) - if err != nil { - return nil, fmt.Errorf("failed to parse default port: %w", err) - } - - port = p - } - - return newServer(fmt.Sprintf("[::]:%d", port), logger, tssMessenger) -} - -func newServer(socketPath string, logger *zap.Logger, tssMessenger tss.ReliableMessenger) (DirectLink, error) { - if socketPath == "" { - return nil, errors.New("can't create DirectLink server: socketPath is empty") - } - if logger == nil { - return nil, errors.New("can't create DirectLink server: logger is nil") - } - if tssMessenger == nil { - return nil, errors.New("can't create DirectLink server: tssMessenger is nil") - } - - peers := tssMessenger.GetPeers() - partyIds := make([]*tss.Identity, len(peers)) - peerToCert := make(map[string]*x509.Certificate, len(peers)) - - var err error - for i, peer := range peers { - partyIds[i], err = tssMessenger.FetchIdentity(peer) - if err != nil { - return nil, err - } - - peerToCert[partyIds[i].NetworkName()] = peer - } - - return &server{ - UnimplementedDirectLinkServer: tsscommv1.UnimplementedDirectLinkServer{}, - ctx: nil, // set up in Run(ctx) - logger: logger, - socketPath: socketPath, - tssMessenger: tssMessenger, - peers: partyIds, - peerToCert: peerToCert, - connections: make(map[string]*connection, len(peers)), - requestRedial: make(chan redialRequest, len(peers)), - redials: make(chan redialResponse, 1), - fullyConnected: make(chan struct{}, 1), // buffered to avoid blocking - }, nil -} - -// Run initialise the server and starts listening on the socket. -// In addition, it will set up connections to all given peers (guardians). -func (s *server) Run(ctx context.Context) error { - if s == nil { - return fmt.Errorf("tsscomm.server is nil") - } - - s.ctx = ctx - - listener, err := net.Listen("tcp", s.socketPath) - if err != nil { - return err - } - - errC := make(chan error) - gserver := grpc.NewServer( - s.makeServerCredentials(), - ) - - tsscommv1.RegisterDirectLinkServer(gserver, s) - - go func() { - errC <- gserver.Serve(listener) - }() - s.run() - - s.logger.Info("tsscomm.server listening on", zap.String("path", s.socketPath)) - - select { - case <-ctx.Done(): - err = ctx.Err() - case err = <-errC: - } - - gserver.Stop() - - if err := listener.Close(); err != nil { - s.logger.Error("failed to close listener", zap.Error(err)) - } - - return err -} - -func (s *server) makeServerCredentials() grpc.ServerOption { - certPool := x509.NewCertPool() - for _, peer := range s.tssMessenger.GetPeers() { - certPool.AddCert(peer) - } - - creds := grpc.Creds(credentials.NewTLS( - &tls.Config{ - MinVersion: tls.VersionTLS13, // version 1.3 - Certificates: []tls.Certificate{*s.tssMessenger.GetCertificate()}, - - ClientAuth: tls.RequireAndVerifyClientCert, - ClientCAs: certPool, // treating each peer as its own CA, will use the given cert as the ID of the peer. - }, - )) - - return creds -} diff --git a/node/pkg/tss/comm/server.go b/node/pkg/tss/comm/server.go deleted file mode 100644 index 2c6feec57e..0000000000 --- a/node/pkg/tss/comm/server.go +++ /dev/null @@ -1,387 +0,0 @@ -package comm - -import ( - "context" - "crypto/tls" - "crypto/x509" - "errors" - "fmt" - "io" - "net" - "time" - - tsscommv1 "github.com/certusone/wormhole/node/pkg/proto/tsscomm/v1" - "github.com/certusone/wormhole/node/pkg/tss" - "github.com/gogo/status" - "go.uber.org/zap" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/peer" -) - -type connection struct { - cc *grpc.ClientConn - stream tsscommv1.DirectLink_SendClient -} - -type redialResponse struct { - name string - conn *connection -} - -type redialRequest struct { - hostname string - immediately bool //used to skip waiting in the dialer backoff mechanism -} - -type server struct { - tsscommv1.UnimplementedDirectLinkServer - ctx context.Context - logger *zap.Logger - socketPath string - - tssMessenger tss.ReliableMessenger - - peers []*tss.Identity - peerToCert map[string]*x509.Certificate - // to ensure thread-safety without locks, only the sender is allowed to change this map. - connections map[string]*connection - requestRedial chan redialRequest - redials chan redialResponse - - fullyConnected chan struct{} // used to signal that the server is fully connected to all peers. -} - -func (s *server) WaitForConnections(ctx context.Context) error { - for { - select { - case <-s.ctx.Done(): - return ctx.Err() - case <-s.fullyConnected: - return nil - } - } -} - -func (s *server) run() { - go s.dialer() - go s.sender() - - for _, id := range s.peers { - hostname := id.NetworkName() - s.enqueueRedialRequest(redialRequest{ - hostname: hostname, - immediately: false, - }) - } -} - -const connectionCheckTime = time.Second * 5 - -func (s *server) sender() { - connectionCheckTicker := time.NewTicker(connectionCheckTime) - - for { - select { - case <-s.ctx.Done(): - for _, con := range s.connections { - s.closeConnection(con) - } - - return - - case o := <-s.tssMessenger.ProducedOutputMessages(): - s.send(o) - - case redial := <-s.redials: - if _, ok := s.connections[redial.name]; ok { - // shouldn't open the same connection twice. - // if a redial request is still needed, it will be enqueued again either - // on the next send attempt, or once the ticker pops. - s.closeConnection(redial.conn) - - continue - } - - s.connections[redial.name] = redial.conn - if len(s.connections) == len(s.peers) { - select { - case s.fullyConnected <- struct{}{}: - default: // don't block if the channel is already full. - } - } - - case <-connectionCheckTicker.C: - s.forceDialIfNotConnected() - } - } -} - -func (s *server) closeConnection(con *connection) { - if err := con.cc.Close(); err != nil { - s.logger.Error( - "couldn't close connection while shutting down", - zap.Error(err), - ) - } -} - -func (s *server) forceDialIfNotConnected() { - if len(s.connections) != len(s.peers) { - for _, id := range s.peers { - hostname := id.NetworkName() - if _, ok := s.connections[hostname]; !ok { - s.enqueueRedialRequest(redialRequest{ - hostname: hostname, - immediately: true, - }) - } - } - } -} - -func (s *server) send(msg tss.Sendable) { - for _, recipient := range msg.GetDestinations() { - hostname := recipient.NetworkName() - - conn, ok := s.connections[hostname] - if !ok { - s.enqueueRedialRequest(redialRequest{ - hostname: hostname, - immediately: false, - }) - - // This spams the logs, but it's useful for debugging. - s.logger.Debug( - "Couldn't send message to peer. No connection found.", - zap.String("hostname", hostname), - ) - - continue - } - - if err := conn.stream.Send(msg.GetNetworkMessage()); err != nil { - if err == io.EOF { - _, err2 := conn.stream.CloseAndRecv() - err = fmt.Errorf("stream closed by peer. peer's reason: %w", err2) - } - - delete(s.connections, hostname) - - s.enqueueRedialRequest(redialRequest{ - hostname: hostname, - immediately: false, - }) - - s.logger.Error( - "couldn't send message to peer due to error.", - zap.Error(err), - zap.String("hostname", hostname), - ) - } - } -} - -func (s *server) enqueueRedialRequest(rqst redialRequest) { - select { - case s.requestRedial <- rqst: - s.logger.Debug("requested redial", zap.String("hostname", rqst.hostname)) - - return - default: - s.logger.Debug("channel to request redial blocked dropping redial request to", zap.String("hostname", rqst.hostname)) - } -} - -func (s *server) dialer() { - // using a heap instead of time.AfterFunc/ After to reduce the number of - // goroutines generated to 0 (not including the dialer itself). - waiters := newBackoffHeap() - - for { - dialTo := "" - - select { - case <-s.ctx.Done(): - return - case <-waiters.WaitOnTimer(): - dialTo = waiters.Dequeue() - case rqst := <-s.requestRedial: - if rqst.immediately { - dialTo = rqst.hostname // will drop down to the dialing section. - } else { - waiters.Enqueue(rqst.hostname) - } - } - - if dialTo == "" { - continue // skip (nothing to dial to) - } - - if err := s.dial(dialTo); err != nil { - s.logger.Error( - "couldn't create direct link to peer", - zap.Error(err), - zap.String("hostname", dialTo), - ) - - waiters.Enqueue(dialTo) // ensuring a retry. - - continue - } - - s.logger.Info("dialed to peer", zap.String("hostname", dialTo)) - waiters.ResetAttempts(dialTo) - } -} - -func addDefaultPortIfMissing(addr string) (string, error) { - _, _, err := net.SplitHostPort(addr) - - if err != nil { - // Check if error is due to missing port - var addrErr *net.AddrError - if errors.As(err, &addrErr) && addrErr.Err == "missing port in address" { - return addr + ":" + tss.DefaultPort, nil - } - - return "", err - } - - return addr, nil -} - -func (s *server) dial(hostname string) error { - crt, ok := s.peerToCert[hostname] - if !ok { - return fmt.Errorf("no cert found for peer %s", hostname) - } - - pool := x509.NewCertPool() - pool.AddCert(crt) // dialing to peer and accepting his cert only. - - dialToAddress, err := addDefaultPortIfMissing(hostname) - if err != nil { - return err - } - - cc, err := grpc.Dial(dialToAddress, - grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ - MinVersion: tls.VersionTLS13, // tls 1.3 - Certificates: []tls.Certificate{*s.tssMessenger.GetCertificate()}, // our cert to be sent to the peer. - RootCAs: pool, - })), - ) - if err != nil { - return err - } - - stream, err := tsscommv1.NewDirectLinkClient(cc).Send(s.ctx) - if err != nil { - cc.Close() - - return err - } - - s.redials <- redialResponse{ - name: hostname, - conn: &connection{ - cc: cc, - stream: stream, - }, - } - - return nil -} - -func (s *server) Send(inStream tsscommv1.DirectLink_SendServer) error { - clientId, err := s.getIdentityFromIncomingStream(inStream) - if err != nil { - s.logger.Warn( - "did not accept incoming peer connection", - zap.Error(err), - ) - - return status.Error(codes.Unauthenticated, fmt.Sprintf("couldn't accept incoming connection: %s", err)) - } - - for { - m, err := inStream.Recv() - if err != nil { - if err == io.EOF { - s.logger.Info( - "closing input stream", - zap.String("peer", clientId.Hostname), - ) - - return status.Error(codes.Canceled, "client closed the connection") - } - - s.logger.Error( - "error receiving from guardian. Closing connection", - zap.Error(err), - zap.String("peer", clientId.Hostname), - ) - - return status.Error(codes.Unknown, "error receiving message from client "+err.Error()) //fmt.Errorf("received error while receiving message: %w", err) - } - - s.tssMessenger.HandleIncomingTssMessage(&tss.IncomingMessage{ - Source: clientId, - Content: m, - }) - } -} - -// getIdentityFromIncomingStream extracts the peer identity from the -// incoming TLS certificate embbeded into the stream. -// adds various checks to ensure the client is a valid guardian. -func (s *server) getIdentityFromIncomingStream(inStream tsscommv1.DirectLink_SendServer) (*tss.Identity, error) { - p, ok := peer.FromContext(inStream.Context()) - if !ok { - return nil, status.Error(codes.InvalidArgument, "unable to retrieve peer from context") - } - - // Extract AuthInfo (TLS information) - tlsInfo, ok := p.AuthInfo.(credentials.TLSInfo) - if !ok { - return nil, status.Error(codes.InvalidArgument, "unexpected peer transport credentials type, please use tls") - } - - // check incoming TLS cert doesn't contain a chain (should be a leaf cert). - // this is more of a precaution. - if len(tlsInfo.State.PeerCertificates) == 0 { - return nil, status.Error(codes.InvalidArgument, "no client certificate provided") - } - - if len(tlsInfo.State.PeerCertificates) != 1 { - return nil, status.Error(codes.PermissionDenied, "expected certificate to be a CA") - } - - for _, chain := range tlsInfo.State.VerifiedChains { - if len(chain) != 1 { - return nil, status.Error(codes.PermissionDenied, "certificate has a chain") - } - } - - // Get the peer's certificate: The first element is the leaf certificate - // that the connection is verified against - clientCert := tlsInfo.State.PeerCertificates[0] - - if clientCert.PublicKeyAlgorithm != x509.ECDSA { - return nil, status.Error(codes.InvalidArgument, "certificate must use ECDSA") - } - - if !clientCert.IsCA { - return nil, status.Error(codes.PermissionDenied, "client certificate is not a CA, but a leaf certificate") - } - - // fetch the party ID according to the public key used to verify this certificate (embbded in the cert). - clientId, err := s.tssMessenger.FetchIdentity(clientCert) - if err != nil { - return nil, fmt.Errorf("client certificate wasn't found: %w", err) - } - - return clientId, nil -} diff --git a/node/pkg/tss/comm/server_test.go b/node/pkg/tss/comm/server_test.go deleted file mode 100644 index 58cf7a077a..0000000000 --- a/node/pkg/tss/comm/server_test.go +++ /dev/null @@ -1,1001 +0,0 @@ -package comm - -import ( - "bytes" - "context" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/tls" - "crypto/x509" - "errors" - "fmt" - "io" - "net" - "sync/atomic" - "testing" - "time" - - "github.com/certusone/wormhole/node/pkg/internal/testutils" - tsscommv1 "github.com/certusone/wormhole/node/pkg/proto/tsscomm/v1" - "github.com/certusone/wormhole/node/pkg/supervisor" - "github.com/certusone/wormhole/node/pkg/tss" - "github.com/certusone/wormhole/node/pkg/tss/internal" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" -) - -const workingServerSock = "127.0.0.1:5933" -const workingServerName = "127.0.0.1" -const workingServerPort = 5933 - -var workingServerAsMessageRecipient = []*tss.Identity{&tss.Identity{ - Hostname: workingServerName, - Port: workingServerPort, -}} - -type mockTssMessageHandler struct { - chn chan tss.Sendable - selfCert *tls.Certificate - peersToConnectTo []*x509.Certificate - peerId *tss.Identity -} - -func (m *mockTssMessageHandler) GetCertificate() *tls.Certificate { return m.selfCert } -func (m *mockTssMessageHandler) GetPeers() []*x509.Certificate { return m.peersToConnectTo } -func (m *mockTssMessageHandler) FetchIdentity(*x509.Certificate) (*tss.Identity, error) { - return m.peerId, nil -} -func (m *mockTssMessageHandler) ProducedOutputMessages() <-chan tss.Sendable { - return m.chn -} - -func (m *mockTssMessageHandler) HandleIncomingTssMessage(msg tss.Incoming) { - fmt.Println("received message from", msg.GetSource()) -} - -// wraps regular server and changes its Send function. -type testServer struct { - *server - atomic.Uint32 - done chan struct{} - numberOfReconnectionAttempts int - // when set to true, the server will block for 30 seconds. - isMaliciousBlocker bool -} - -func (w *testServer) Send(in tsscommv1.DirectLink_SendServer) error { - prevVal := w.Uint32.Add(1) - if int(prevVal) == w.numberOfReconnectionAttempts { - close(w.done) - } - if w.isMaliciousBlocker { - time.Sleep(time.Second * 30) - } - - return io.EOF -} - -func TestTLSConnectAndRedial(t *testing.T) { - a := require.New(t) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - ctx = testutils.MakeSupervisorContext(ctx) - - en, err := _loadGuardians(2) - a.NoError(err) - - tmpSrvr, err := newServer(workingServerSock, supervisor.Logger(ctx), &mockTssMessageHandler{ - chn: nil, - selfCert: en[0].GetCertificate(), - // connect to no one. - peersToConnectTo: en[0].GetPeers(), // Give the peer a certificate. - peerId: &tss.Identity{}, - }) - a.NoError(err) - - tstServer := testServer{ - server: tmpSrvr.(*server), - Uint32: atomic.Uint32{}, - done: make(chan struct{}), - numberOfReconnectionAttempts: 2, - } - tstServer.server.ctx = ctx - - listener, err := net.Listen("tcp", workingServerSock) - a.NoError(err) - defer listener.Close() - - gserver := grpc.NewServer(tstServer.makeServerCredentials()) - defer gserver.Stop() - - tsscommv1.RegisterDirectLinkServer(gserver, &tstServer) - go gserver.Serve(listener) - - PEMCert := en[0].GuardianStorage.TlsX509 - serverCert, err := internal.PemToCert(PEMCert) - a.NoError(err) - - msgChan := make(chan tss.Sendable) - srvr, err := newServer("localhost:5930", supervisor.Logger(ctx), &mockTssMessageHandler{ - chn: msgChan, - selfCert: en[1].GetCertificate(), - peersToConnectTo: []*x509.Certificate{serverCert}, // will ask to fetch each peer (and return the below peerId) - peerId: &tss.Identity{ - Cert: serverCert, - Hostname: workingServerName, - Port: workingServerPort, - }, - }) - a.NoError(err) - - srv := srvr.(*server) - srv.ctx = ctx - // setting up server dailer and sender - srv.run() - time.Sleep(time.Second) - - //should cause disconnect - msgChan <- &tss.Echo{ - Echo: &tsscommv1.Echo{}, - Recipients: workingServerAsMessageRecipient, - } - time.Sleep(time.Second * 2) - - msgChan <- &tss.Unicast{ - Receipients: workingServerAsMessageRecipient, - } - - select { - case <-ctx.Done(): - t.FailNow() - case <-tstServer.done: - } -} - -func TestRelentlessReconnections(t *testing.T) { - a := require.New(t) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*15) - defer cancel() - ctx = testutils.MakeSupervisorContext(ctx) - - en, err := _loadGuardians(2) - a.NoError(err) - - PEMCert := en[0].GuardianStorage.TlsX509 - serverCert, err := internal.PemToCert(PEMCert) - a.NoError(err) - - msgChan := make(chan tss.Sendable) - srvr, err := newServer("localhost:5930", supervisor.Logger(ctx), &mockTssMessageHandler{ - chn: msgChan, - selfCert: en[1].GetCertificate(), - peersToConnectTo: []*x509.Certificate{serverCert}, // will ask to fetch each peer (and return the below peerId) - peerId: &tss.Identity{ - Cert: serverCert, - Hostname: workingServerName, - Port: workingServerPort, - }, - }) - a.NoError(err) - - srv := srvr.(*server) - srv.ctx = ctx - // setting up server dailer and sender - srv.run() - - tmpSrvr, err := newServer(workingServerSock, supervisor.Logger(ctx), &mockTssMessageHandler{ - chn: nil, - selfCert: en[0].GetCertificate(), - // connect to no one. - peersToConnectTo: en[0].GetPeers(), // Give the peer a certificate. - peerId: &tss.Identity{}, - }) - a.NoError(err) - - tstServer := testServer{ - server: tmpSrvr.(*server), - Uint32: atomic.Uint32{}, - done: make(chan struct{}), - numberOfReconnectionAttempts: 5, - } - tstServer.server.ctx = ctx - - listener, err := net.Listen("tcp", workingServerSock) - a.NoError(err) - defer listener.Close() - - gserver := grpc.NewServer(tstServer.makeServerCredentials()) - defer gserver.Stop() - - tsscommv1.RegisterDirectLinkServer(gserver, &tstServer) - go gserver.Serve(listener) - - for i := 0; i < 10; i++ { - msgChan <- &tss.Unicast{ - Unicast: &tsscommv1.Unicast{ - Content: &tsscommv1.Unicast_Tss{ - Tss: &tsscommv1.TssContent{ - Payload: []byte{1}, - MsgSerialNumber: 2, - }, - }, - }, - Receipients: workingServerAsMessageRecipient, - } - - select { - case <-ctx.Done(): - t.FailNow() - case <-tstServer.done: - return // only way to pass the test. - default: - time.Sleep(time.Millisecond * 100) - } - } - - t.FailNow() -} - -type tssMockJustForMessageGeneration struct { - tss.ReliableMessenger - chn chan tss.Sendable -} - -func (m *tssMockJustForMessageGeneration) ProducedOutputMessages() <-chan tss.Sendable { - return m.chn -} -func TestNonBlockedBroadcast(t *testing.T) { - a := require.New(t) - - workingServers := []string{"localhost:5500", "localhost:5501"} - ctx, cancel := context.WithTimeout(context.Background(), time.Second*40) - defer cancel() - ctx = testutils.MakeSupervisorContext(ctx) - - en, err := _loadGuardians(3) - a.NoError(err) - - donechns := make([]chan struct{}, 2) - // set servers up. - for i := 0; i < 2; i++ { - tmpSrvr, err := newServer(workingServers[i], supervisor.Logger(ctx), &mockTssMessageHandler{ - chn: nil, - selfCert: en[i].GetCertificate(), - peersToConnectTo: en[0].GetPeers(), // Give the peer a certificate. - peerId: &tss.Identity{}, - }) - a.NoError(err) - - tstServer := testServer{ - server: tmpSrvr.(*server), - Uint32: atomic.Uint32{}, - done: make(chan struct{}), - numberOfReconnectionAttempts: 1, - isMaliciousBlocker: true, - } - donechns[i] = tstServer.done - tstServer.server.ctx = ctx - - listener, err := net.Listen("tcp", workingServers[i]) - a.NoError(err) - defer listener.Close() - - gserver := grpc.NewServer(tstServer.makeServerCredentials()) - defer gserver.Stop() - - tsscommv1.RegisterDirectLinkServer(gserver, &tstServer) - go gserver.Serve(listener) - } - - for _, v := range en[2].GuardianStorage.Identities { - v.Hostname = "localhost" - if bytes.Equal(v.KeyPEM, en[0].Self.KeyPEM) { - v.Port = 5500 - - continue - } - - if bytes.Equal(v.KeyPEM, en[1].Self.KeyPEM) { - v.Port = 5501 - - continue - } - - v.Hostname = "..." // ensuring connection is not possible. - } - - // ensuring the identity sets up correctly (after the above loop). - en[2].GuardianStorage.SetInnerFields() - - msgChan := make(chan tss.Sendable) - srvr, err := newServer("localhost:5930", supervisor.Logger(ctx), &tssMockJustForMessageGeneration{ - ReliableMessenger: en[2], - chn: msgChan, - }) - a.NoError(err) - - srv := srvr.(*server) - srv.ctx = ctx - // setting up server dailer and sender - srv.run() - time.Sleep(time.Second) - - numDones := 0 - for i := 0; i < 10; i++ { - msgChan <- &tss.Echo{ - Recipients: workingServerAsMessageRecipient, - } - - select { - case <-ctx.Done(): - t.FailNow() - case <-donechns[0]: - numDones += 1 - case <-donechns[1]: - numDones += 1 - default: - time.Sleep(time.Millisecond * 100) - } - } - if numDones >= 2 { - return - } - - cancel() - t.FailNow() - -} - -func TestBackoff(t *testing.T) { - a := require.New(t) - ctx, cncl := context.WithTimeout(context.Background(), time.Second*60) - defer cncl() - - t.Run("basic1", func(t *testing.T) { - heap := newBackoffHeap() - - heap.Enqueue("3") - a.Equal("3", heap.Dequeue()) - heap.Enqueue("3") - heap.Enqueue("1") - heap.Enqueue("2") - - expected := []string{"1", "2", "3"} - for i := 0; i < 3; i++ { - select { - case <-ctx.Done(): - t.FailNow() - case <-heap.WaitOnTimer(): - hostname := heap.Dequeue() - a.Equal(expected[i], hostname) - } - } - }) - - t.Run("basic2", func(t *testing.T) { - heap := newBackoffHeap() - - heap.Enqueue("1") - a.Equal("1", heap.Dequeue()) - heap.ResetAttempts("1") - heap.Enqueue("1") - heap.Enqueue("2") - heap.Enqueue("3") - - expected := []string{"1", "2", "3"} - for i := 0; i < 3; i++ { - select { - case <-ctx.Done(): - t.FailNow() - case <-heap.WaitOnTimer(): - hostname := heap.Dequeue() - a.Equal(expected[i], hostname) - } - } - }) - - t.Run("complex", func(t *testing.T) { - heap := newBackoffHeap() - - // operations on an empty heap: - a.Equal("", heap.Dequeue()) - - heap.ResetAttempts("1") - heap.Enqueue("1") - heap.Enqueue("1") - heap.Enqueue("1") - heap.Enqueue("1") - a.Equal("1", heap.Dequeue()) - - heap.ResetAttempts("1") - heap.Enqueue("1") - heap.Enqueue("2") - heap.Enqueue("3") - heap.Enqueue("2") - a.Equal("1", heap.Dequeue()) - a.Equal("2", heap.Dequeue()) - - heap.ResetAttempts("2") - heap.Enqueue("2") - heap.Enqueue("4") - heap.Enqueue("5") - - expected := []string{"3", "2", "4", "5"} - for i := 0; i < 3; i++ { - select { - case <-ctx.Done(): - t.FailNow() - case <-heap.WaitOnTimer(): - hostname := heap.Dequeue() - a.Equal(expected[i], hostname) - } - } - }) - - t.Run("maxAndMinValue", func(t *testing.T) { - maxBackoffTime := (&dialWithBackoff{attempt: maxBackoffTimeModifier})._durationBasedOnNumberOfAttempts() - - heap := newBackoffHeap() - - heap.attemptsPerPeer["1"] = 23144532345345665 // large number. - heap.Enqueue("1") - v := heap.Peek() - - a.True(v.nextRedialTime.Before(time.Now().Add(maxBackoffTime))) - a.True(v.nextRedialTime.After(time.Now().Add(maxBackoffTime - time.Second))) - - a.Equal("1", heap.Dequeue()) - timenow := time.Now() - heap.ResetAttempts("1") - heap.Enqueue("1") - v = heap.Peek() - a.True(v.nextRedialTime.Before(timenow.Add(minBackoffTime + 10*time.Millisecond))) - a.True(v.nextRedialTime.After(timenow.Add(minBackoffTime - 10*time.Millisecond))) - - }) -} - -// TODO: this is a copy-paste from tss/implementation_test.go -func loadMockGuardianStorage(gstorageIndex int) (*tss.GuardianStorage, error) { - path, err := testutils.GetMockGuardianTssStorage(gstorageIndex) - if err != nil { - return nil, err - } - - st, err := tss.NewGuardianStorageFromFile(path) - if err != nil { - return nil, err - } - return st, nil -} - -// TODO: this is a copy-paste from tss/implementation_test.go -func _loadGuardians(numParticipants int) ([]*tss.Engine, error) { - engines := make([]*tss.Engine, numParticipants) - - for i := 0; i < numParticipants; i++ { - gs, err := loadMockGuardianStorage(i) - if err != nil { - return nil, err - } - - e, err := tss.NewReliableTSS(gs) - if err != nil { - return nil, err - } - - en, ok := e.(*tss.Engine) - if !ok { - return nil, errors.New("not an engine") - } - engines[i] = en - } - - return engines, nil -} - -type testCAInspectionFailForNonCACerts struct { - *server - atomic.Uint32 - done chan struct{} - numberOfReconnectionAttempts int - // when set to true, the server will block for 30 seconds. - isMaliciousBlocker bool -} - -func TestNotAcceptNonCAs(t *testing.T) { - a := require.New(t) - - en, err := _loadGuardians(2) - a.NoError(err) - - // ============ - // Creating new Cert which is NOT a CA - // ============ - - serverCert, err := internal.PemToCert(en[0].GuardianStorage.TlsX509) - a.NoError(err) - - rootKey, err := internal.PemToPrivateKey(en[0].PrivateKey) - a.NoError(err) - clientTlsCert, clientCert := tlsCert(serverCert, rootKey) - - // ============ - // setting server up, with this Cert allowed - // ============ - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*60) - defer cancel() - ctx = testutils.MakeSupervisorContext(ctx) - - tmp, err := newServer(workingServerSock, supervisor.Logger(ctx), &mockTssMessageHandler{ - chn: nil, - selfCert: en[0].GetCertificate(), - // connect to no one. - peersToConnectTo: []*x509.Certificate{clientCert}, // Give the peer a certificate. - peerId: &tss.Identity{}, - }) - a.NoError(err) - - server := tmp.(*server) - server.ctx = ctx - - listener, err := net.Listen("tcp", workingServerSock) - a.NoError(err) - defer listener.Close() - - gserver := grpc.NewServer(server.makeServerCredentials()) - defer gserver.Stop() - - tsscommv1.RegisterDirectLinkServer(gserver, server) - go gserver.Serve(listener) - - time.Sleep(time.Millisecond * 200) - // ============ - // trying to send message using cert - // ============ - pool := x509.NewCertPool() - - runningServerX509, err := internal.PemToCert(en[0].GuardianStorage.TlsX509) - a.NoError(err) - - pool.AddCert(runningServerX509) // dialing to peer and accepting his cert only. - - cc, err := grpc.Dial(workingServerSock, - grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ - MinVersion: tls.VersionTLS13, // tls 1.3 - Certificates: []tls.Certificate{*clientTlsCert}, // our cert to be sent to the peer. - RootCAs: pool, - })), - ) - a.NoError(err) - - defer cc.Close() - - stream, err := tsscommv1.NewDirectLinkClient(cc).Send(ctx) - a.NoError(err) - - stream.Send(&tsscommv1.PropagatedMessage{}) - _, err = stream.CloseAndRecv() - a.ErrorContains(err, "not a CA") -} - -func tlsCert(rootCA *x509.Certificate, rootKey *ecdsa.PrivateKey) (*tls.Certificate, *x509.Certificate) { - template := *rootCA - // this cert will be the CA that we will use to sign the server cert - template.IsCA = false - // describe what the certificate will be used for - template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature - template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth} - - priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - panic(err) - } - - pubcert, certpem, err := internal.CreateCert(&template, rootCA, &priv.PublicKey, rootKey) - if err != nil { - panic(err) - } - - tlscert, err := tls.X509KeyPair(certpem, internal.PrivateKeyToPem(priv)) - if err != nil { - panic(err) - } - return &tlscert, pubcert -} - -func TestDialWithDefaultPort(t *testing.T) { - a := require.New(t) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*40) - defer cancel() - ctx = testutils.MakeSupervisorContext(ctx) - - en, err := _loadGuardians(3) - a.NoError(err) - - communicatingEngine := en[0] - listenerEngine := en[1] - - listenerServerPath := "localhost:" + tss.DefaultPort - // set up server that only listent and aren't able to connect to anyone. - listenerServer, err := newServer(listenerServerPath, supervisor.Logger(ctx), &mockTssMessageHandler{ - chn: nil, - selfCert: listenerEngine.GetCertificate(), - // the listening server will expect this cert to connect with. - peersToConnectTo: []*x509.Certificate{communicatingEngine.GetCertificate().Leaf}, - peerId: &tss.Identity{}, - }) - a.NoError(err) - - ListenerWrapper := testServer{ - server: listenerServer.(*server), - Uint32: atomic.Uint32{}, - done: make(chan struct{}), - numberOfReconnectionAttempts: 1, - isMaliciousBlocker: true, - } - - ListenerWrapper.server.ctx = ctx - - l, err := net.Listen("tcp", listenerServerPath) - a.NoError(err) - defer l.Close() - - gserver := grpc.NewServer(ListenerWrapper.makeServerCredentials()) - defer gserver.Stop() - - tsscommv1.RegisterDirectLinkServer(gserver, &ListenerWrapper) - go gserver.Serve(l) - - // SETTING THE ID TO CONNECT TO WITHOUT A PORT: - // ensuring the communicating server will have to use the default port to dial. - for _, v := range communicatingEngine.Identities { - if bytes.Equal(v.KeyPEM, listenerEngine.Self.KeyPEM) { - v.Hostname = "localhost" - v.Port = 0 - - continue - } - - v.Hostname = "..." // ensuring connection is not possible. - } - - a.NoError(communicatingEngine.GuardianStorage.SetInnerFields()) - - msgChan := make(chan tss.Sendable) - communicator, err := newServer("localhost:5930", supervisor.Logger(ctx), &tssMockJustForMessageGeneration{ - ReliableMessenger: communicatingEngine, - chn: msgChan, - }) - a.NoError(err) - - tmp := communicator.(*server) - tmp.ctx = ctx - tmp.run() - - time.Sleep(time.Second) - - for i := 0; i < 10; i++ { - tmp := communicatingEngine.Identities - msgChan <- &tss.Echo{ - Recipients: tmp, - } - - select { - case <-ctx.Done(): - t.FailNow() - case <-ListenerWrapper.done: - return - default: - time.Sleep(time.Millisecond * 100) - } - } - - t.FailNow() -} - -type mockJustHandleIncomingMessage struct { - tss.ReliableMessenger - receivedData chan tss.Incoming -} - -func (m *mockJustHandleIncomingMessage) HandleIncomingTssMessage(msg tss.Incoming) { - m.receivedData <- msg - close(m.receivedData) -} - -func TestDialWithDefaultPortDeliverCorrectSrc(t *testing.T) { - a := require.New(t) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*40) - defer cancel() - ctx = testutils.MakeSupervisorContext(ctx) - - en, err := _loadGuardians(3) - a.NoError(err) - - streamReceiverEngine := en[0] - senderEngine := en[1] - - streamReceiverPath := "localhost" - - // ensuring when a message arrives, the server idetntifies the source according to - // the tls key, then returns the tss.PartyID according to what is - // stored in the guardian storage. - expectedText := "This text is what i expect to see in the incoming message." - for _, v := range streamReceiverEngine.Identities { - if bytes.Equal(v.KeyPEM, senderEngine.Self.KeyPEM) { - v.Hostname = expectedText - - continue - } - - v.Pid.ID = "" - } - - streamReceiverEngine.GuardianStorage.SetInnerFields() - - // Setting the ID as they will be sent and used to connect to the other party. - for _, v := range senderEngine.Identities { - if bytes.Equal(v.KeyPEM, streamReceiverEngine.Self.KeyPEM) { - v.Hostname = streamReceiverPath // ensuring the server connects - v.Port = 5930 - continue - } - - v.Hostname = "..." // ensuring connection is not possible. - } - - senderEngine.GuardianStorage.SetInnerFields() - - incomingDataChan := make(chan tss.Incoming) - listenerServer, err := newServer(streamReceiverPath, supervisor.Logger(ctx), - &mockJustHandleIncomingMessage{ - ReliableMessenger: streamReceiverEngine, - receivedData: incomingDataChan, - }, - ) - a.NoError(err) - - ListenerWrapper := listenerServer.(*server) - ListenerWrapper.ctx = ctx - - l, err := net.Listen("tcp", streamReceiverPath+":5930") - a.NoError(err) - defer l.Close() - - gserver := grpc.NewServer(ListenerWrapper.makeServerCredentials()) - defer gserver.Stop() - - tsscommv1.RegisterDirectLinkServer(gserver, ListenerWrapper) - go gserver.Serve(l) - - msgChan := make(chan tss.Sendable) - sender, err := newServer("nonsensePort", supervisor.Logger(ctx), &tssMockJustForMessageGeneration{ - ReliableMessenger: senderEngine, - chn: msgChan, - }) - a.NoError(err) - - tmp := sender.(*server) - tmp.ctx = ctx - tmp.run() // demanding this server run. - - time.Sleep(time.Second * 1) - - //should set up connection with the stream r - - msgChan <- &tss.Echo{ - Echo: &tsscommv1.Echo{}, - Recipients: []*tss.Identity{ - { - CommunicationIndex: 0, - Hostname: streamReceiverPath, - Port: 5930, - }, - }, - } - - select { - case <-ctx.Done(): - t.FailNow() - case incoming := <-incomingDataChan: - // ensuring the incoming message has the hostname i've eddited (proof we are matching the TLS cert, - // with the correct identity, even if things like hostname change) - a.Equal(expectedText, incoming.GetSource().Hostname) - return - } -} - -// uses the reliable messenger to send messages, but mocks the ProducedOutputMessages -type mockProduceOutputMessages struct { - mockJustHandleIncomingMessage - fakeDataToSendChan chan tss.Sendable -} - -func (m *mockProduceOutputMessages) ProducedOutputMessages() <-chan tss.Sendable { - return m.fakeDataToSendChan -} - -func TestConnectingToServers(t *testing.T) { - t.SkipNow() // Manual test, help inspect connections via logs. - a := require.New(t) - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*40) - defer cancel() - ctx = testutils.MakeSupervisorContext(ctx) - - en, err := _loadGuardians(5) - a.NoError(err) - // ============ - incomingMsgChn := make([]chan tss.Incoming, len(en)) - channelForGeneratedMessages := make(chan tss.Sendable) - // setting up servers - srvrs := make([]*server, 5) - for i, e := range en { - incomingMsgChn[i] = make(chan tss.Incoming) - e.GuardianStorage.Self.Hostname = "localhost" - e.GuardianStorage.Self.Port = 5930 + i - for j, id := range e.Identities { - id.Hostname = "localhost" - id.Port = 5930 + j - } - - e.GuardianStorage.SetInnerFields() - e.Start(ctx) - s, err := newServer(e.GuardianStorage.Self.NetworkName(), supervisor.Logger(ctx), &mockProduceOutputMessages{ - mockJustHandleIncomingMessage: mockJustHandleIncomingMessage{ - ReliableMessenger: e, - receivedData: incomingMsgChn[i], - }, - - fakeDataToSendChan: channelForGeneratedMessages, - }) - a.NoError(err) - - srvrs[i] = s.(*server) - - } - - for _, s := range srvrs { - go s.Run(ctx) - } - - // ============ - time.Sleep(time.Second * 2) - channelForGeneratedMessages <- &tss.Echo{ - Recipients: en[0].Identities, - Echo: &tsscommv1.Echo{}, - } - time.Sleep(time.Second * 2) - - for _, chn := range incomingMsgChn { - select { - case <-chn: - case <-ctx.Done(): - t.FailNow() - } - } - cancel() - time.Sleep(100 * time.Millisecond) - -} - -func TestDetectConnectionsDone(t *testing.T) { - a := require.New(t) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*40) - defer cancel() - ctx = testutils.MakeSupervisorContext(ctx) - - en, err := _loadGuardians(2) - a.NoError(err) - - tmpSrvr, err := newServer(workingServerSock, supervisor.Logger(ctx), &mockTssMessageHandler{ - chn: nil, - selfCert: en[0].GetCertificate(), - // connect to no one. - peersToConnectTo: []*x509.Certificate{en[1].GetCertificate().Leaf}, // Give the peer a certificate. - peerId: &tss.Identity{}, - }) - a.NoError(err) - - tstServer := testServer{ - server: tmpSrvr.(*server), - Uint32: atomic.Uint32{}, - done: make(chan struct{}), - numberOfReconnectionAttempts: 1, - } - tstServer.server.ctx = ctx - - listener, err := net.Listen("tcp", workingServerSock) - a.NoError(err) - defer listener.Close() - - gserver := grpc.NewServer(tstServer.makeServerCredentials()) - defer gserver.Stop() - - tsscommv1.RegisterDirectLinkServer(gserver, &tstServer) - // go gserver.Serve(listener) - - PEMCert := en[0].GuardianStorage.TlsX509 - serverCert, err := internal.PemToCert(PEMCert) - a.NoError(err) - - msgChan := make(chan tss.Sendable) - srvr, err := newServer("localhost:5930", supervisor.Logger(ctx), &mockTssMessageHandler{ - chn: msgChan, - selfCert: en[1].GetCertificate(), - peersToConnectTo: []*x509.Certificate{serverCert}, // will ask to fetch each peer (and return the below peerId) - peerId: &tss.Identity{ - Cert: serverCert, - Hostname: workingServerName, - Port: workingServerPort, - }, - }) - a.NoError(err) - - srv := srvr.(*server) - srv.ctx = ctx - // setting up server dailer and sender - srv.run() - // time.Sleep(time.Second * 2) - - time.Sleep(time.Second) - // go gserver.Serve(listener) - - go gserver.Serve(listener) - time.Sleep(time.Second * 5) - if err := srv.WaitForConnections(ctx); err != nil { - t.Fatalf("failed to wait for connections: %v", err) - } - - msgChan <- &tss.Echo{ - Echo: &tsscommv1.Echo{}, - Recipients: workingServerAsMessageRecipient, - } - - select { - case <-ctx.Done(): - t.FailNow() - case <-tstServer.done: - } -} - -func TestSocketPathCreation(t *testing.T) { - a := require.New(t) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - ctx = testutils.MakeSupervisorContext(ctx) - - en, err := _loadGuardians(2) - a.NoError(err) - - en[0].Self.Port = 1233456 - en[0].GuardianStorage.Identities[en[0].Self.CommunicationIndex].Port = 1233456 - - tmpSrvr, err := NewServer(supervisor.Logger(ctx), en[0]) - a.NoError(err) - - a.Equal(fmt.Sprintf("[::]:%d", en[0].Self.Port), tmpSrvr.(*server).socketPath) - - // now checking correct behavior when port is 0 - en[1].Self.Port = 0 - en[1].GuardianStorage.Identities[en[1].Self.CommunicationIndex].Port = 0 - tmpSrvr2, err := NewServer(supervisor.Logger(ctx), en[1]) - a.NoError(err) - a.Equal(fmt.Sprintf("[::]:%s", tss.DefaultPort), tmpSrvr2.(*server).socketPath) - - // For coverage completness we Invoke Run and close the server. - - go tmpSrvr2.Run(ctx) - time.Sleep(time.Second * 1) - - cancel() - select { - case <-ctx.Done(): - case <-time.After(time.Second * 2): - t.Fatal("server run did not stop after context cancel") - } -} diff --git a/node/pkg/tss/constants.go b/node/pkg/tss/constants.go deleted file mode 100644 index 9ea976d511..0000000000 --- a/node/pkg/tss/constants.go +++ /dev/null @@ -1,103 +0,0 @@ -package tss - -import ( - "time" - "unsafe" - - "github.com/wormhole-foundation/wormhole/sdk/vaa" - - "github.com/xlabs/multi-party-sig/protocols/frost/keygen" - "github.com/xlabs/multi-party-sig/protocols/frost/sign" - - "google.golang.org/protobuf/reflect/protoreflect" -) - -var tssProtoMessageNames = []string{} - -var tssProtoMessageSize = 0 - -func init() { - tssProtoMessageNames = append(tssProtoMessageNames, extractProtoTypeNames(sign.File_proto_frost_signing_proto)...) - tssProtoMessageNames = append(tssProtoMessageNames, extractProtoTypeNames(keygen.File_proto_frost_keygen_proto)...) - - for _, name := range tssProtoMessageNames { - tssProtoMessageSize = max(tssProtoMessageSize, len(name)) - } -} -func extractProtoTypeNames(protoreflectDesc protoreflect.FileDescriptor) []string { - names := make([]string, protoreflectDesc.Messages().Len()) - - for i := range protoreflectDesc.Messages().Len() { - m := protoreflectDesc.Messages().Get(i) - names[i] = string(m.FullName()) - } - - return names -} - -const ( - DefaultPort = "8998" - - digestSize = 32 - - notStarted uint32 = 0 // using 0 since it's the default value - started uint32 = 1 - - // byte sizes - hostnameSize = 255 - pemKeySize = 178 - - // auxiliaryData is emmiterChain in bytes. - auxiliaryDataSize = int(unsafe.Sizeof(vaa.ChainID(0))) - maxParties = 256 - - // hex string sizes use 2x since each byte is represented by 2 hex characters - // e.g. 0xFF = "FF" - auxiliaryDataStrHexSize = 2 * auxiliaryDataSize - maxPartiesStrHexSize = 2 * (maxParties / 8) // divided by 8 since it's a bitmap - digestStrHexSize = 2 * digestSize - protocolTypeSize = int(unsafe.Sizeof(uint8(0))) // uint8 currently - numdashesInTrackingIDStr = 3 - // TrackingID is a string composed of: protocolType-Digest-AuxiliaryData-MaxParties - // where: - // *protocolType is 1 byte (uint8) - // *Digest is 32 bytes (sha256) - // *AuxiliaryData is 2 bytes (emitterChain) - // *MaxParties is 32 bytes (bitmap of max parties, currently set to 256 max parties) - trackingIDHexStrSize = protocolTypeSize + digestStrHexSize + auxiliaryDataStrHexSize + maxPartiesStrHexSize + numdashesInTrackingIDStr - - defaultMaxLiveSignatures = 20000 - - // Since each sigState is created via almost any of the ftCommands, I decided on setting it as 1000 sigs a minute - // and multiplied it by number of minutes we have - sigStateRateLimit = defaultMaxLiveSignatures * int(2*defaultMaxSignerTTL/time.Minute) - - defaultMaxSignerTTL = time.Minute * 5 - defaultDelayGraceTime = time.Minute - defaultGuardianDownTime = time.Minute * 10 - - numBroadcastsPerSignature = 8 // GG18 - numUnicastsRounds = 2 // GG18 - - // the assumed time that a message can be delayed between two parties. - // for instance guardian 1 received a problem report at time 00:07, then guardian 2 can be - // assumed to have received the same problem report between times 00:02 and 00:12 - synchronsingInterval = time.Second * 5 - - // Domain separation strings for hashing. - // Ensures that similar digest are different for different domains. - parsedProblemDomain = "problem" - tssContentDomain = "content" - newAnouncementDomain = "anncmnt" - - defaultMaxDownTimeJitter = time.Minute - maxHeartbeatInterval = defaultGuardianDownTime - - // Consistency levels (following https://wormhole.com/docs/build/reference/consistency-levels/): - instantConsistencyLevel uint8 = vaa.ConsistencyLevelPublishImmediately // low consistancy - - pythnetFinalizedConsistencyLevel uint8 = 1 - solanaFinalizedConsistencyLevel uint8 = 1 - - senderIndexSize = int(unsafe.Sizeof(SenderIndex(0))) -) diff --git a/node/pkg/tss/crypto.go b/node/pkg/tss/crypto.go deleted file mode 100644 index 18b758bee2..0000000000 --- a/node/pkg/tss/crypto.go +++ /dev/null @@ -1,49 +0,0 @@ -package tss - -import ( - "bytes" - "encoding/binary" - - tsscommv1 "github.com/certusone/wormhole/node/pkg/proto/tsscomm/v1" - "github.com/wormhole-foundation/wormhole/sdk/vaa" - "golang.org/x/crypto/sha3" -) - -type digest [digestSize]byte - -func hash(msg []byte) digest { - d := sha3.Sum256(msg) - - return d -} - -// using this function since proto.Marshal is either non-deterministic, -// or it isn't canonical - as stated in proto.MarshalOptions docs. - -func hashSignedMessage(msg *tsscommv1.SignedMessage) digest { - if msg == nil { - return digest{} - } - - var b *bytes.Buffer - - // Since the msg is a protobug, we need to switch on the type of - // the content (instead of adding an interface to the protogen file). - switch m := msg.Content.(type) { - case *tsscommv1.SignedMessage_TssContent: - b = bytes.NewBuffer(nil) - - b.Write(m.TssContent.Payload) - vaa.MustWrite(b, binary.BigEndian, m.TssContent.MsgSerialNumber) - - vaa.MustWrite(b, binary.BigEndian, msg.Sender) - - case *tsscommv1.SignedMessage_HashEcho: - d := digest{} - copy(d[:], m.HashEcho.OriginalContetDigest) - - return d - } - - return hash(b.Bytes()) -} diff --git a/node/pkg/tss/fault_tolerance.go b/node/pkg/tss/fault_tolerance.go deleted file mode 100644 index 74c418feac..0000000000 --- a/node/pkg/tss/fault_tolerance.go +++ /dev/null @@ -1,339 +0,0 @@ -package tss - -import ( - "time" - - "github.com/wormhole-foundation/wormhole/sdk/vaa" - common "github.com/xlabs/tss-common" - "github.com/xlabs/tss-lib/v2/party" - "go.uber.org/zap" -) - -// This file represents tracking mechanism for signatures, their trackingID, and who is currently working on them. -// It provide the Engine with information, for instance whether if the guardian saw the digest already. -// -// ftCommand represents commands that reach the sigTracker. -// to become ftCommand, the struct must implement the apply(*Engine, *sigTracker) method. -// -// the commands include signCommand, prepareToSignCommand, and reportProblemCommand. -// - signCommand is used to inform the sigTracker that a guardian saw a digest, and what related information -// it has about the digest. -// - prepareToSignCommand is used to know which guardians aren't to be used in the protocol for -// specific chainID. -type ftCommand interface { - apply(*Engine, *sigTracker) -} - -type trackidStr string - -type signCommand struct { - SigningInfo *party.SigningInfo - passedToFP bool - digestconsistancy uint8 - signingMeta signingMeta -} - -type SigEndCommand struct { - *common.TrackingID // either from error, or from success. -} - -type sigPreparationInfo struct { - alreadyStartedSigningTrackingIDs map[trackidStr]bool -} - -// Used to know which guardians aren't to be used in the protocol for specific chainID. -type prepareToSignCommand struct { - ChainID vaa.ChainID - Digest party.Digest - - reply chan sigPreparationInfo -} - -type tackingIDContext struct { - sawProtocolMessagesFrom map[strPartyId]bool - alreadyPassedTIDtoFP bool -} - -// the signatureState struct is used to keep track of a signature. -type signatureState struct { - chain vaa.ChainID // blockchain the message relates to (e.g. Ethereum, Solana, etc). - - digest party.Digest - // States whether the guardian saw the digest and forwarded it to the engine to be signed by TSS. - approvedToSign bool - - // each trackingId is a unique attempt to sign a message. - trackidContext map[trackidStr]*tackingIDContext - - // consistansy states the level of finality floats over the digest. - // Bad consistancy means the digest is not final, and even might not be seen by all guardians. - digestconsistancy uint8 - isFromVaav1 bool - - beginTime time.Time // used to do cleanups. -} - -type keyAndTTL struct { - key sigKey - ttl time.Time -} - -type ftChainContext struct { - timeToRevive time.Time // the time this party is expected to come back and be part of the protocol again. - liveSigsWaitingForThisParty map[sigKey]*signatureState // sigs that once the revive time expire should be retried. -} - -// Describes a specfic party's data in terms of fault tolerance. -type ftParty struct { - partyID *common.PartyID - ftChainContext map[vaa.ChainID]*ftChainContext -} - -type sigTracker struct { - ttlKeys []keyAndTTL - - sigsState map[sigKey]*signatureState - chainIdsToSigs map[vaa.ChainID]map[sigKey]*signatureState - - membersData map[strPartyId]*ftParty -} - -func newChainContext() *ftChainContext { - return &ftChainContext{ - // ensuring the first time we see this party, we don't assume it's down. - timeToRevive: time.Time{}, - - liveSigsWaitingForThisParty: map[sigKey]*signatureState{}, - } -} - -// a single threaded env, that inspects incoming signatures request, message deliveries etc. -func (t *Engine) sigTracker() { - f := &sigTracker{ - sigsState: make(map[sigKey]*signatureState), - membersData: make(map[strPartyId]*ftParty), - - chainIdsToSigs: map[vaa.ChainID]map[sigKey]*signatureState{}, - } - - for _, id := range t.GuardianStorage.Identities { - strPid := strPartyId(id.Pid.ToString()) - f.membersData[strPid] = &ftParty{ - partyID: id.Pid, - ftChainContext: map[vaa.ChainID]*ftChainContext{}, - } - } - - maxttl := t.GuardianStorage.maxSignerTTL() - - ticker := time.NewTicker(maxttl) - defer ticker.Stop() - - for { - select { - case <-t.ctx.Done(): - return - case cmd := <-t.ftCommandChan: - cmd.apply(t, f) - - if len(f.ttlKeys) > sigStateRateLimit { - f.cleanup(t, maxttl) - } - - case <-ticker.C: - f.cleanup(t, maxttl) - } - } -} - -func (f *sigTracker) cleanup(t *Engine, maxttl time.Duration) { - now := time.Now() - - toRemove := []sigKey{} - - if len(f.ttlKeys) > sigStateRateLimit { - diff := len(f.ttlKeys) - sigStateRateLimit - - toRemove = make([]sigKey, diff) - - for i, keyAndTime := range f.ttlKeys[:diff] { - toRemove[i] = keyAndTime.key - } - - f.ttlKeys = f.ttlKeys[diff:] // remove the first diff elements. - - t.logger.Warn("sigTracker's limit reached, removing the oldest stored signature states", zap.Int("amount", diff)) - } - - cutoff := 0 - - for i, keyAndTtl := range f.ttlKeys { - if now.Sub(keyAndTtl.ttl) >= maxttl { - toRemove = append(toRemove, keyAndTtl.key) - } else { - cutoff = i - - break - } - } - - f.ttlKeys = f.ttlKeys[cutoff:] // remove the first cutoff elements. - - for _, key := range toRemove { - sigState, ok := f.sigsState[key] - if !ok { - continue - } - - f.remove(sigState) - } - -} - -func (f *sigTracker) remove(sigState *signatureState) { - if sigState == nil { - return - } - - key := intoSigKey(sigState.digest, sigState.chain) - - for _, m := range f.membersData { - if chainData, ok := m.ftChainContext[sigState.chain]; ok { - delete(chainData.liveSigsWaitingForThisParty, key) - } - } - - chn, ok := f.chainIdsToSigs[sigState.chain] - if ok { - delete(chn, key) - } - - delete(f.sigsState, key) -} - -func (cmd *prepareToSignCommand) apply(t *Engine, f *sigTracker) { - if cmd.reply == nil { - t.logger.Error("reply channel is nil") - - return - } - - reply := sigPreparationInfo{ - alreadyStartedSigningTrackingIDs: map[trackidStr]bool{}, - } - - sigKey := intoSigKey(cmd.Digest, cmd.ChainID) - - sigState, ok := f.sigsState[sigKey] - if ok { - for tidStr, ctx := range sigState.trackidContext { - if ctx.alreadyPassedTIDtoFP { - reply.alreadyStartedSigningTrackingIDs[tidStr] = true - } - } - } - - if err := intoChannelOrDone(t.ctx, cmd.reply, reply); err != nil { - t.logger.Error("error on telling on inactive guardians on specific chain", zap.Error(err)) - } - - close(cmd.reply) -} - -// This changes the signatureState and sets it as Seen/Started/Approved to sign. -// As a result, once the alertHeap expires, it will not report a problem after -// f+1 guardians started signing since this guardian started as well. -func (cmd *signCommand) apply(t *Engine, f *sigTracker) { - tid := cmd.SigningInfo.TrackingID - if tid == nil { - t.logger.Error("signCommand: tracking id is nil") - - return - } - - dgst := party.Digest{} - copy(dgst[:], tid.Digest[:]) - - chain := extractChainIDFromTrackingID(tid) - - state, ok := f.sigsState[intoSigKey(dgst, chain)] - if !ok { - state = f.setNewSigState(dgst, chain, time.Now()) - - state.digestconsistancy = cmd.digestconsistancy - state.isFromVaav1 = cmd.signingMeta.isFromVaav1 // this is only interesting in case the guardian first saw this message via VAAv1. - } - - state.approvedToSign = true - - for _, pid := range cmd.SigningInfo.SigningCommittee { - m, ok := f.membersData[strPartyId(pid.ToString())] - if !ok { - t.logger.Error("signCommand: party not found in the members data") - - continue - } - - chainData, ok := m.ftChainContext[state.chain] - if !ok { - chainData = newChainContext() - m.ftChainContext[state.chain] = chainData - } - - chainData.liveSigsWaitingForThisParty[intoSigKey(dgst, state.chain)] = state - } - - // if this guardian has request a signature for this TID, then we store it to ensure it doesn't attempt to sign again later. - if cmd.passedToFP { - tidStr := trackidStr(tid.ToString()) - - tidData, ok := state.trackidContext[tidStr] - if !ok { - tidData = &tackingIDContext{ - sawProtocolMessagesFrom: map[strPartyId]bool{}, - alreadyPassedTIDtoFP: false, - } - - state.trackidContext[tidStr] = tidData - } - - tidData.alreadyPassedTIDtoFP = true - } -} - -func (cmd *SigEndCommand) apply(t *Engine, f *sigTracker) { - dgst := party.Digest{} - copy(dgst[:], cmd.Digest[:]) - - chain := extractChainIDFromTrackingID(cmd.TrackingID) - key := intoSigKey(dgst, chain) - - if sigstate, ok := f.sigsState[key]; ok { - f.remove(sigstate) - } -} - -func (f *sigTracker) setNewSigState(digest party.Digest, chain vaa.ChainID, alertTime time.Time) *signatureState { - state := &signatureState{ - chain: chain, - digest: digest, - approvedToSign: false, - trackidContext: map[trackidStr]*tackingIDContext{}, - beginTime: time.Now(), - } - - sigkey := intoSigKey(digest, chain) - f.sigsState[sigkey] = state - - chn, ok := f.chainIdsToSigs[chain] - if !ok { - chn = map[sigKey]*signatureState{} - f.chainIdsToSigs[chain] = chn - } - - chn[sigkey] = state - - f.ttlKeys = append(f.ttlKeys, keyAndTTL{key: sigkey, ttl: alertTime}) - - return state -} diff --git a/node/pkg/tss/gossiper.go b/node/pkg/tss/gossiper.go new file mode 100644 index 0000000000..4275a8d0ee --- /dev/null +++ b/node/pkg/tss/gossiper.go @@ -0,0 +1,210 @@ +package tss + +import ( + "context" + "errors" + "fmt" + + "github.com/certusone/wormhole/node/pkg/common" + gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1" + ethcommon "github.com/ethereum/go-ethereum/common" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/wormhole-foundation/wormhole/sdk/vaa" + "github.com/xlabs/tss-common/service/signer" + "go.uber.org/zap" +) + +var ( + errNilGuardianSetState = errors.New("tss' guardianSetState nil") + errNilVaa = errors.New("nil VAAv1") + errNilGuardianSet = errors.New("nil guardian set") + errNotVaaV1 = errors.New("not a VAAv1") + errNetworkOutputChannelFull = errors.New("network output channel buffer is full") + errCouldntInformVaav1 = errors.New("couldn't inform tss on new VAAv1") + errNilGuardianSigner = errors.New("guardianSigner is nil") +) + +func (s *SignerClient) WitnessNewVaaV1(ctx context.Context, v *vaa.VAA) error { + if s == nil { + return ErrSignerClientNil + } + + if v == nil { + return errNilVaa + } + + if !s.vaaData.isLeader { + return nil + } + + if s.vaaData.gst == nil { + return errNilGuardianSetState + } + + if s.vaaData.GuardianSigner == nil { + return errNilGuardianSigner + } + + if v.Version != vaa.VaaVersion1 { + // not an error. but will not accept. + return nil + } + + gs := s.vaaData.gst.Get() + if gs == nil { + return errNilGuardianSet + } + + if err := v.Verify(gs.Keys); err != nil { + return err // won't send invalid VAAs. + } + + bts, err := v.Marshal() + if err != nil { + return fmt.Errorf("failed to marshal VAA: %w", err) + } + + // signing the content the leader will gossip. + sig, err := s.vaaData.GuardianSigner.Sign(ctx, ethcrypto.Keccak256(bts)) + if err != nil { + return fmt.Errorf("failed to sign VAA: %w", err) + } + m := &gossipv1.TSSGossipMessage{ + Message: bts, + Signature: sig, + } + + // send to network. + select { + case s.vaaData.gossipOutput <- m: + return nil + default: + return errNetworkOutputChannelFull + } +} + +func (s *SignerClient) Outbound() <-chan *gossipv1.TSSGossipMessage { + if s == nil { + return nil + } + + // nothing to publish if not leader. + if !s.vaaData.isLeader { + return nil + } + + return s.vaaData.gossipOutput +} + +// Inform is used to inform the TSS signer of a new incoming gossip message. +// it returns an error if the message couldn't be delivered. +func (s *SignerClient) Inform(v *gossipv1.TSSGossipMessage) error { + if s == nil { + return ErrSignerClientNil + } + + if s.vaaData.isLeader { // leader doesn't need to be informed. + return nil + } + + select { + case s.vaaData.incomingGossip <- v: + return nil + default: + return errCouldntInformVaav1 + } +} + +// the gossipListener listens for incoming gossip messages and processes them. +// closes when the context is done. +func (s *SignerClient) gossipListener(ctx context.Context, logger *zap.Logger) { + logger = logger.Named("gossipListener") + dt := s.vaaData + for { + select { + case <-ctx.Done(): + return + case msg := <-dt.incomingGossip: + gs := dt.gst.Get() + if gs == nil { + logger.Debug("nil guardian set") + continue + } + + if err := dt.verifyGossipSig(msg); err != nil { + logger.Warn("invalid gossip signature", zap.Error(err)) + continue + } + + newVaa, err := vaa.Unmarshal(msg.Message) + if err != nil { + logger.Warn("malformed VAA", zap.Error(err)) + continue + } + + if newVaa.Version != vaa.VaaVersion1 { + continue + } + + if err := newVaa.Verify(gs.Keys); err != nil { + logger.Warn("invalid VAA", zap.Error(err)) + continue + } + + if err := s.vaaToSignRequest(newVaa, gs); err != nil { + logger.Error("failed to create sign request from VAA", zap.Error(err)) + continue + } + } + } +} + +func (s *SignerClient) vaaToSignRequest(newVaa *vaa.VAA, gs *common.GuardianSet) error { + rq := &signer.SignRequest{ + Digest: newVaa.SigningDigest().Bytes(), + Committee: make([]*signer.TypedKey, 0, len(newVaa.Signatures)), + Protocol: s.GetProtocol(int(newVaa.EmitterChain)).ToString(), + } + + // set committee members to be the current guardian set. + for _, sig := range newVaa.Signatures { + if sig == nil { + continue + } + + if sig.Index >= uint8(len(gs.Keys)) { + continue + } + + addr := gs.Keys[sig.Index] + key := &signer.TypedKey{ + Type: signer.TypedKey_EthKey, + Key: addr.Bytes(), + } + + rq.Committee = append(rq.Committee, key) + } + + // This isn't for security, just to avoid sending requests that will be rejected by the signer. + if len(rq.Committee) < s.configurations.ThresholdSize { + return fmt.Errorf("not enough committee members: have %d, need %d", len(rq.Committee), s.configurations.ThresholdSize) + } + + return s.AsyncSign(rq) +} + +func (dt vaaHandling) verifyGossipSig(msg *gossipv1.TSSGossipMessage) error { + pubKey, err := ethcrypto.Ecrecover(ethcrypto.Keccak256(msg.Message), msg.Signature) + if err != nil { + return fmt.Errorf("failed to recover public key: %w", err) + } + + signerAddr := ethcommon.BytesToAddress(ethcrypto.Keccak256(pubKey[1:])[12:]) + + if signerAddr != dt.leaderAddress { + return fmt.Errorf("signature not from leader: got %s, want %s", signerAddr.Hex(), dt.leaderAddress.Hex()) + } + // We don't verify that the leaderAddress is in the guardian set because we acccept gossip messages from any leader (even outside the guardian set). + + return nil +} diff --git a/node/pkg/tss/gossiper_test.go b/node/pkg/tss/gossiper_test.go new file mode 100644 index 0000000000..93442b9a05 --- /dev/null +++ b/node/pkg/tss/gossiper_test.go @@ -0,0 +1,315 @@ +package tss + +import ( + "context" + "crypto/ecdsa" + "crypto/rand" + "testing" + "time" + + "github.com/certusone/wormhole/node/pkg/common" + node_common "github.com/certusone/wormhole/node/pkg/common" + "github.com/certusone/wormhole/node/pkg/guardiansigner" + gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/wormhole-foundation/wormhole/sdk/vaa" + "github.com/xlabs/tss-common/service/signer" + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func TestWitnessNewVaaV1(t *testing.T) { + privKey, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader) + require.NoError(t, err) + signer, err := guardiansigner.GenerateSignerWithPrivatekeyUnsafe(privKey) + require.NoError(t, err) + + gst := node_common.NewGuardianSetState(nil) + gs := &node_common.GuardianSet{ + Keys: []ethcommon.Address{crypto.PubkeyToAddress(privKey.PublicKey)}, + Index: 1, + } + gst.Set(gs) + + s := &SignerClient{ + vaaData: vaaHandling{ + isLeader: true, + gst: gst, + GuardianSigner: signer, + gossipOutput: make(chan *gossipv1.TSSGossipMessage, 1), + }, + } + + v := &vaa.VAA{ + Version: vaa.VaaVersion1, + GuardianSetIndex: 1, + Signatures: []*vaa.Signature{}, + Timestamp: time.Now(), + Nonce: 1, + Sequence: 1, + ConsistencyLevel: 1, + EmitterChain: vaa.ChainIDSolana, + EmitterAddress: vaa.Address{1}, + Payload: []byte("payload"), + } + v.AddSignature(privKey, 0) + + ctx := context.Background() + err = s.WitnessNewVaaV1(ctx, v) + assert.NoError(t, err) + + select { + case msg := <-s.vaaData.gossipOutput: + assert.NotNil(t, msg) + // Verify signature on gossip message + pubKey, err := crypto.Ecrecover(crypto.Keccak256(msg.Message), msg.Signature) + require.NoError(t, err) + addr := crypto.PubkeyToAddress(privKey.PublicKey) + recoveredAddr := ethcommon.BytesToAddress(crypto.Keccak256(pubKey[1:])[12:]) + assert.Equal(t, addr, recoveredAddr) + default: + t.Fatal("expected message in gossipOutput") + } +} + +func TestWitnessNewVaaV1_NotLeader(t *testing.T) { + s := &SignerClient{ + vaaData: vaaHandling{ + isLeader: false, + }, + } + v := &vaa.VAA{} + err := s.WitnessNewVaaV1(context.Background(), v) + assert.NoError(t, err) +} + +func TestInform(t *testing.T) { + s := &SignerClient{ + vaaData: vaaHandling{ + isLeader: false, + incomingGossip: make(chan *gossipv1.TSSGossipMessage, 1), + }, + } + msg := &gossipv1.TSSGossipMessage{} + err := s.Inform(msg) + assert.NoError(t, err) + select { + case m := <-s.vaaData.incomingGossip: + assert.Equal(t, msg, m) + default: + t.Fatal("expected message in incomingGossip") + } +} + +func TestInform_Leader(t *testing.T) { + s := &SignerClient{ + vaaData: vaaHandling{ + isLeader: true, + }, + } + msg := &gossipv1.TSSGossipMessage{} + err := s.Inform(msg) + assert.NoError(t, err) +} + +func TestGossipListener(t *testing.T) { + // Setup keys + leaderKey, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader) + require.NoError(t, err) + + // Setup Guardian Set + gst := node_common.NewGuardianSetState(nil) + gs := &node_common.GuardianSet{ + Keys: []ethcommon.Address{crypto.PubkeyToAddress(leaderKey.PublicKey)}, + Index: 1, + } + gst.Set(gs) + + // Setup Client + s := &SignerClient{ + conn: &connChans{ + signRequests: make(chan *signer.SignRequest, 1), + }, + vaaData: vaaHandling{ + isLeader: false, + gst: gst, + incomingGossip: make(chan *gossipv1.TSSGossipMessage, 1), + leaderAddress: crypto.PubkeyToAddress(leaderKey.PublicKey), + // GuardianSigner: nil, + gossipOutput: make(chan *gossipv1.TSSGossipMessage), + }, + } + + // Start listener + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go s.gossipListener(ctx, zap.NewNop()) + + // Create VAA + v := &vaa.VAA{ + Version: vaa.VaaVersion1, + GuardianSetIndex: 1, + Signatures: []*vaa.Signature{}, + Timestamp: time.Now(), + Nonce: 1, + Sequence: 1, + ConsistencyLevel: 1, + EmitterChain: vaa.ChainIDSolana, + EmitterAddress: vaa.Address{1}, + Payload: []byte("payload"), + } + v.AddSignature(leaderKey, 0) + vaaBytes, err := v.Marshal() + require.NoError(t, err) + + // Create Gossip Message signed by leader + hash := crypto.Keccak256(vaaBytes) + sig, err := crypto.Sign(hash, leaderKey) + require.NoError(t, err) + + gossipMsg := &gossipv1.TSSGossipMessage{ + Message: vaaBytes, + Signature: sig, + } + + // Send message + s.vaaData.incomingGossip <- gossipMsg + + // Verify SignRequest received + select { + case req := <-s.conn.signRequests: + assert.Equal(t, v.SigningDigest().Bytes(), req.Digest) + case <-time.After(time.Second): + t.Fatal("timeout waiting for sign request") + } +} + +func TestWitnessVaa(t *testing.T) { + key, err := ecdsa.GenerateKey(ethcrypto.S256(), rand.Reader) + require.NoError(t, err) + + gs, err := guardiansigner.GenerateSignerWithPrivatekeyUnsafe(key) + require.NoError(t, err) + + v := &vaa.VAA{ + Version: 1, + GuardianSetIndex: 1, + Signatures: nil, + Timestamp: time.Unix(12345, 0), + Nonce: 1, + Sequence: 1, + ConsistencyLevel: 1, + EmitterChain: 1, + EmitterAddress: vaa.Address([32]byte{1}), + Payload: []byte("hello"), + } + v.AddSignature(key, 0) + + leaderAddr := ethcommon.Address(crypto.PubkeyToAddress(key.PublicKey)) + + t.Run("Leader", func(t *testing.T) { + gst := common.NewGuardianSetState(nil) + gst.Set(&common.GuardianSet{ + Keys: []ethcommon.Address{leaderAddr}, + Index: 1, + }) + a := require.New(t) + client, err := NewSigner(Parameters{ + SocketPath: "unused", + DialOpts: []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}, + LeaderAddress: leaderAddr, + Self: leaderAddr, + GST: gst, + GuardianSigner: gs, + }) + a.NoError(err) + + err = client.WitnessNewVaaV1(context.Background(), v) + a.NoError(err) + + select { + case msg := <-client.Outbound(): + a.NotNil(msg) + + valid, err := gs.Verify(context.Background(), msg.Signature, ethcrypto.Keccak256(msg.Message)) + a.NoError(err) + a.True(valid) + + case <-time.After(time.Second): + t.Fatal("timeout waiting for gossip message") + } + }) + + t.Run("Non-leader", func(t *testing.T) { + a := require.New(t) + client, err := NewSigner(Parameters{ + SocketPath: "unused", + DialOpts: []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}, + LeaderAddress: leaderAddr, + Self: ethcommon.Address{}, + GST: common.NewGuardianSetState(nil), + GuardianSigner: gs, + }) + a.NoError(err) + + err = client.WitnessNewVaaV1(context.Background(), v) + a.NoError(err) + + select { + case msg := <-client.Outbound(): + t.Fatalf("received unexpected gossip message: %+v", msg) + case <-time.After(50 * time.Millisecond): + // Success, no message received + } + }) +} + +func TestWitnessNewVaaV1_Errors(t *testing.T) { + s := &SignerClient{} + + // Nil client + var nilClient *SignerClient + err := nilClient.WitnessNewVaaV1(context.Background(), &vaa.VAA{}) + assert.Equal(t, ErrSignerClientNil, err) + + // Nil VAA + err = s.WitnessNewVaaV1(context.Background(), nil) + assert.Equal(t, errNilVaa, err) + + // Not leader + s.vaaData.isLeader = false + err = s.WitnessNewVaaV1(context.Background(), &vaa.VAA{}) + assert.NoError(t, err) + + // Leader but nil GST + s.vaaData.isLeader = true + s.vaaData.gst = nil + err = s.WitnessNewVaaV1(context.Background(), &vaa.VAA{}) + assert.Equal(t, errNilGuardianSetState, err) + + // Leader but nil GuardianSigner + s.vaaData.gst = common.NewGuardianSetState(nil) + s.vaaData.GuardianSigner = nil + err = s.WitnessNewVaaV1(context.Background(), &vaa.VAA{}) + assert.Equal(t, errNilGuardianSigner, err) +} + +func TestInform_ChannelFull(t *testing.T) { + s := &SignerClient{ + vaaData: vaaHandling{ + isLeader: false, + incomingGossip: make(chan *gossipv1.TSSGossipMessage, 1), + }, + } + + // Fill channel + s.vaaData.incomingGossip <- &gossipv1.TSSGossipMessage{} + + err := s.Inform(&gossipv1.TSSGossipMessage{}) + assert.Equal(t, errCouldntInformVaav1, err) +} diff --git a/node/pkg/tss/identity.go b/node/pkg/tss/identity.go deleted file mode 100644 index eb6005a1f1..0000000000 --- a/node/pkg/tss/identity.go +++ /dev/null @@ -1,188 +0,0 @@ -package tss - -import ( - "crypto/ecdsa" - "crypto/x509" - "errors" - "fmt" - "net" - "strconv" - - "github.com/certusone/wormhole/node/pkg/tss/internal" - ethcommon "github.com/ethereum/go-ethereum/common" - common "github.com/xlabs/tss-common" - "google.golang.org/protobuf/proto" -) - -type Identity struct { - Pid *common.PartyID `json:"Pid,inline"` // used for tss protocol. - KeyPEM PEM `json:"KeyPEM"` // the public key in PEM format. - Key *ecdsa.PublicKey `json:"-"` // ensuring this isn't stored in non-pem format. - CertPem PEM `json:"CertPem"` // the certificate in PEM format. - Cert *x509.Certificate `json:"-"` // ensuring this isn't stored in non-pem format. - - // the number representing the guardian when passing messages. - CommunicationIndex SenderIndex `json:"CommunicationIndex"` - // the hostname of the guardian, used to connect to it. - Hostname string `json:"Hostname"` - // the port the guardian is listening on. if 0 -> use the default port. - Port int `json:"Port,omitempty"` - // the combination of hostname and port. Used to establish a network connection. - networkname string `json:"-"` - - // TODO: is this field mutable? in the future, when this field is set via guardian communications, - // would it be set ONCE, or multiple times? (if once, we can use atomics to indicate whether it is set or not). - // otherwise, we'll need a lock. - VAAv1PubKey *ethcommon.Address `json:"VAAv1PubKey,omitempty"` // mapping between VaaV1 and PID (used in TSS) -} - -func (id *Identity) Copy() *Identity { - keypem := make([]byte, len(id.KeyPEM)) - copy(keypem, id.KeyPEM) - - certPem := make([]byte, len(id.CertPem)) - copy(certPem, id.CertPem) - - c, k, _ := extractCertAndKeyFromPem(certPem) - cpy := &Identity{ - Pid: id.getPidCopy(), - KeyPEM: keypem, - CertPem: certPem, - CommunicationIndex: id.CommunicationIndex, - Hostname: id.Hostname, - Port: id.Port, - Key: k, - Cert: c, - networkname: id.networkname, - } - - return cpy -} - -func (id *Identity) NetworkName() string { - if id.networkname != "" { - return id.networkname - } - - return id.portAndHostToNetName() -} - -func (id *Identity) portAndHostToNetName() string { - var port string - if id.Port <= 0 || id.Port > (1<<16) { - port = DefaultPort - } else { - port = strconv.Itoa(id.Port) - } - - return net.JoinHostPort(id.Hostname, port) -} - -func (id *Identity) getPidCopy() *common.PartyID { - // return a copy, tss-lib might modify this object. - return proto.CloneOf(id.Pid) -} - -type IdentitiesKeep struct { - // sorted by KeyPem. - Identities []*Identity - - // maps and slices to ensure quick lookups. - pemkeyToIndex map[string]int - vaav1PubToIdentity map[ethcommon.Address]int - partyidToIndex map[string]int - peerCerts []*x509.Certificate - partyIds []*common.PartyID -} - -var errUnknownPartyID = fmt.Errorf("unknown partyID") - -func (ids *IdentitiesKeep) fetchIdentityFromPartyID(senderPid *common.PartyID) (*Identity, error) { - pos, ok := ids.partyidToIndex[string(senderPid.GetID())] - if !ok { - return nil, errUnknownPartyID - } - - return ids.fetchIdentityFromIndex(SenderIndex(pos)) -} - -var errUnknownPubkey = fmt.Errorf("unknown public key") - -func (ids *IdentitiesKeep) fetchIdentityFromKeyPEM(pk PEM) (*Identity, error) { - pos, ok := ids.pemkeyToIndex[string(pk)] - if !ok { - return nil, errUnknownPubkey - } - - return ids.fetchIdentityFromIndex(SenderIndex(pos)) -} - -var ( - errNilCert = errors.New("nil certificate") - errUnsupportedKeyType = errors.New("unsupported public key type") -) - -// FetchIdentity implements ReliableTSS. -func (ids *IdentitiesKeep) FetchIdentity(cert *x509.Certificate) (*Identity, error) { - if cert == nil { - return nil, errNilCert - } - - var id *Identity - switch key := cert.PublicKey.(type) { - case *ecdsa.PublicKey: - publicKeyPem, err := internal.PublicKeyToPem(key) - if err != nil { - return nil, err - } - - id, err = ids.fetchIdentityFromKeyPEM(publicKeyPem) - if err != nil { - return nil, fmt.Errorf("error fetching identity from public key: %w", err) - } - case []byte: - var err error - id, err = ids.fetchIdentityFromKeyPEM(key) - if err != nil { - return nil, fmt.Errorf("error fetching identity from public key bytes: %w", err) - } - default: - return nil, errUnsupportedKeyType - } - - return id, nil -} - -func (ids *IdentitiesKeep) contains(senderId SenderIndex) bool { - if senderId < 0 || int(senderId) >= len(ids.Identities) { - return false - } - - return true -} - -func (ids *IdentitiesKeep) fetchIdentityFromIndex(senderId SenderIndex) (*Identity, error) { - if !ids.contains(senderId) { - return nil, ErrUnkownSender - } - - return ids.Identities[senderId], nil -} - -func (ids *IdentitiesKeep) fetchIdentityFromVaav1Pubkey(pubkey ethcommon.Address) (*Identity, error) { - index, ok := ids.vaav1PubToIdentity[pubkey] - if !ok { - return nil, fmt.Errorf("unknown vaav1 pubkey %s", pubkey.Hex()) - } - - return ids.fetchIdentityFromIndex(SenderIndex(index)) - -} - -func (ids *IdentitiesKeep) GetPeers() []*x509.Certificate { - return ids.peerCerts -} - -func (ids *IdentitiesKeep) GetPartyIDs() []*common.PartyID { - return ids.partyIds -} diff --git a/node/pkg/tss/implementation.go b/node/pkg/tss/implementation.go deleted file mode 100644 index 3b8307cee3..0000000000 --- a/node/pkg/tss/implementation.go +++ /dev/null @@ -1,1039 +0,0 @@ -package tss - -import ( - "bytes" - "context" - "crypto/ecdsa" - "crypto/rand" - "crypto/tls" - "fmt" - - "sync" - "sync/atomic" - "time" - - whcommon "github.com/certusone/wormhole/node/pkg/common" - tsscommv1 "github.com/certusone/wormhole/node/pkg/proto/tsscomm/v1" - "github.com/certusone/wormhole/node/pkg/supervisor" - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/wormhole-foundation/wormhole/sdk/vaa" - frosteth "github.com/xlabs/multi-party-sig/pkg/eth" - "github.com/xlabs/multi-party-sig/pkg/math/curve" - "github.com/xlabs/multi-party-sig/protocols/frost" - common "github.com/xlabs/tss-common" - "github.com/xlabs/tss-lib/v2/party" - "go.uber.org/zap" -) - -type uuid digest // distinguishing between types to avoid confusion. - -type fpCommunicationChannels party.OutputChannels - -// Engine is the implementation of reliableTSS, it is a wrapper for the -// tss-lib fullParty and adds hash-broadcast logic -// to the message sending and receiving. -type Engine struct { - ctx context.Context - - logger *zap.Logger - GuardianStorage - - fpParams *party.Parameters - fp party.FullParty - - fpCommChans fpCommunicationChannels - sigOutChan chan *common.SignatureData // actual sig output. - messageOutChan chan Sendable - - started atomic.Uint32 - msgSerialNumber uint64 - - // used to perform hash-broadcast: - mtx *sync.Mutex - received map[uuid]*broadcaststate - - sigCounter activeSigCounter - - // informs a central tracker of the guardian's actions. - ftCommandChan chan ftCommand - - SignatureMetrics sync.Map - - gst *whcommon.GuardianSetState -} - -type PEM []byte - -// Contains the TSS related configurations. -type Configurations struct { - maxSimultaneousSignatures int - // MaxSignerTTL is the maximum time a signature is allowed to be active. - // used to release resources. - MaxSignerTTL time.Duration - - ChainsWithNoSelfReport []uint16 - - // LeaderIdentity is used by the TSS engine protocol to determine who is responsible for telling - // the other guardians about a new VAAv1. - LeaderIdentity PEM // The public key of the leader in PEM format. -} - -// GuardianStorage is a struct that holds the data needed for a guardian to participate in the TSS protocol -// including its signing key, and the shared symmetric keys with other guardians. -// should be loaded from a file. -type GuardianStorage struct { - Configurations - - Self *Identity - IdentitiesKeep `json:"IdentitiesKeep,inline"` - - // should be a certificate generated with SecretKey - TlsX509 PEM - PrivateKey PEM - tlsCert *tls.Certificate - signingKey *ecdsa.PrivateKey // should be the unmarshalled value of PriavteKey. - - // Assumes threshold = 2f+1, where f is the maximal expected number of faulty nodes. - Threshold int - - // all secret keys should be generated with specific value. - TSSSecrets []byte - frostconf *frost.Config - - LoadDistributionKey []byte - - isleader bool -} - -// GuardianStorageFromFile loads a guardian storage from a file. -// If the storage file hadn't contained symetric keys, it'll compute them. -func NewGuardianStorageFromFile(storagePath string) (*GuardianStorage, error) { - var storage GuardianStorage - if err := storage.load(storagePath); err != nil { - return nil, err - } - - return &storage, nil -} - -// ProducedSignature lets a listener receive the output signatures once they're ready. -func (t *Engine) ProducedSignature() <-chan *common.SignatureData { - return t.sigOutChan -} - -// ProducedOutputMessages ensures a listener can send the output messages to the network. -func (t *Engine) ProducedOutputMessages() <-chan Sendable { - return t.messageOutChan -} - -// GetCertificate implements ReliableTSS. -func (st *GuardianStorage) GetCertificate() *tls.Certificate { - return st.tlsCert -} - -var ( - errNilTssEngine = fmt.Errorf("tss engine is nil") - errTssEngineNotStarted = fmt.Errorf("tss engine hasn't started") -) - -// BeginAsyncThresholdSigningProtocol used to start the TSS protocol over a specific msg. - -func (t *Engine) BeginAsyncThresholdSigningProtocol(vaaDigest []byte, chainID vaa.ChainID, consistencyLvl uint8) error { - return t.beginTSSSign(vaaDigest, chainID, consistencyLvl, signingMeta{}) -} - -type signingMeta struct { - isFromVaav1 bool - verifiedVAAv1 *vaa.VAA -} - -func (t *Engine) beginTSSSign(vaaDigest []byte, chainID vaa.ChainID, consistencyLvl uint8, mt signingMeta) error { - if t == nil { - return errNilTssEngine - } - - if t.started.Load() != started { - return errTssEngineNotStarted - } - - if t.fp == nil { - return fmt.Errorf("tss engine is not set up correctly, use NewReliableTSS to create a new engine") - } - - if len(vaaDigest) != digestSize { - return fmt.Errorf("vaaDigest length is not 32 bytes") - } - - d := party.Digest{} - copy(d[:], vaaDigest) - - if err := t.prepareThenAnounceNewDigest(d, chainID, consistencyLvl, mt); err != nil { - return err - } - - sigPrepInfo, err := t.getSigPrepInfo(chainID, d) - if err != nil { - return err - } - - t.logger.Info("signature for VAA requested", - zap.String("digest", fmt.Sprintf("%x", vaaDigest)), - zap.String("chainID", chainID.String()), - zap.Uint8("consistency", consistencyLvl), - zap.Bool("isFromVaav1", mt.isFromVaav1), - zap.Int("numMatchingTrackIDS", len(sigPrepInfo.alreadyStartedSigningTrackingIDs)), - ) - - t.createSignatureMetrics(vaaDigest, chainID) - - sigTask := makeSigningRequest(d, t.getExcludedFromCommittee(mt), chainID) - - info, err := t.fp.GetSigningInfo(sigTask) - if err != nil { - return fmt.Errorf("couldnt generate signing task: %w", err) - } - - if sigPrepInfo.alreadyStartedSigningTrackingIDs[trackidStr(info.TrackingID.ToString())] { - return nil // skipping signing. - } - - // TODO: cosider not recomputing the info, and just used it from `t.fp.GetSigningInfo(sigTask)` - info, err = t.fp.AsyncRequestNewSignature(sigTask) - - if err != nil { - return err - } - - flds := []zap.Field{ - zap.String("trackingID", info.TrackingID.ToString()), - zap.String("ChainID", chainID.String()), - zap.Any("committee", t.getCommitteeNetworkNames(info.SigningCommittee)), - } - - t.logger.Info( - "guardian started signing protocol", - flds..., - ) - - scmd := signCommand{SigningInfo: info, passedToFP: true, signingMeta: mt, digestconsistancy: consistencyLvl} - if err := intoChannelOrDone[ftCommand](t.ctx, t.ftCommandChan, &scmd); err != nil { - t.logger.Error("couldn't inform the tracker of the signature start", - zap.Error(err), - zap.String("trackingID", info.TrackingID.ToString()), - ) - - return err - } - - return nil -} - -// getExcludedFromCommittee follows the Leader's recommendation for the committee -// by returning the list of guardians that should be excluded from the committee (as 'faulties' list). -func (t *Engine) getExcludedFromCommittee(mt signingMeta) []*common.PartyID { - if !mt.isFromVaav1 || mt.verifiedVAAv1 == nil { - return nil - } - - signersID, err := t.translateVaaV1Signers(mt.verifiedVAAv1) - if err != nil { - return nil - } - - if len(signersID) < t.GuardianStorage.Threshold { - return nil // not enough guardians to form a committee. - } - - // grab everyone that is not a signer in the VAAv1. - var excludedSigners []*common.PartyID - for _, id := range t.GuardianStorage.Identities { - if _, ok := signersID[id.CommunicationIndex]; !ok { - excludedSigners = append(excludedSigners, id.Pid) - } - } - - return excludedSigners -} - -func (t *Engine) getCommitteeNetworkNames(pids []*common.PartyID) []string { - ids := make([]string, 0, len(pids)) - for _, pid := range pids { - id, err := t.GuardianStorage.fetchIdentityFromPartyID(pid) - if err != nil { - t.logger.Warn("couldn't find identity for partyID", zap.Any("partyID", pid)) - - continue - } - - ids = append(ids, id.NetworkName()) - } - - return ids -} - -func (t *Engine) SetGuardianSetState(gss *whcommon.GuardianSetState) error { - if gss == nil { - return fmt.Errorf("guardian set state is nil") - } - - if t == nil { - return errNilTssEngine - } - - if t.started.Load() != notStarted { - return fmt.Errorf("tss engine has started, and cannot receive new guardian set state") - } - - t.gst = gss - - return nil -} - -func (t *Engine) getSigPrepInfo(chainID vaa.ChainID, d party.Digest) (sigPreparationInfo, error) { - cmd := prepareToSignCommand{ - ChainID: chainID, - Digest: d, - reply: make(chan sigPreparationInfo, 1), - } - - if err := intoChannelOrDone[ftCommand](t.ctx, t.ftCommandChan, &cmd); err != nil { - return sigPreparationInfo{}, fmt.Errorf("failed to request for inactive guardians: %w", err) - } - - // waiting for the reply. - sigPrepInfo, err := outOfChannelOrDone(t.ctx, cmd.reply) - if err != nil { - return sigPreparationInfo{}, fmt.Errorf("failed to get inactive guardians: %w", err) - } - - return sigPrepInfo, nil -} - -// prepareThenAnounceNewDigest updates the inner state of the engine before announcing to others about a new digest seen. -func (t *Engine) prepareThenAnounceNewDigest(d party.Digest, chainID vaa.ChainID, consistencyLvl uint8, mt signingMeta) error { - signinginfo, err := t.fp.GetSigningInfo(party.SigningTask{ - Digest: d, - Faulties: []*common.PartyID{}, // no faulties - AuxiliaryData: chainIDToBytes(chainID), - }) - - if err != nil { - return fmt.Errorf("couldnt generate signing task: %w", err) - } - - sgCmd := &signCommand{ - SigningInfo: signinginfo, - passedToFP: false, // set to true only after FP actually received the message. - digestconsistancy: consistencyLvl, - signingMeta: mt, - } - - if err := intoChannelOrDone[ftCommand](t.ctx, t.ftCommandChan, sgCmd); err != nil { - return fmt.Errorf("couldn't inform the tracker of the signature start: %w", err) - } - - return nil -} - -func makeSigningRequest(d party.Digest, faulties []*common.PartyID, chainID vaa.ChainID) party.SigningTask { - return party.SigningTask{ - Digest: d, - // indicating the reviving guardian will be given a chance to join the protocol. - Faulties: faulties, - AuxiliaryData: chainIDToBytes(chainID), - } -} -func NewKeyGenerator(storage *GuardianStorage) (KeyGenerator, error) { - relTSS, err := NewReliableTSS(storage) - if err != nil { - return nil, fmt.Errorf("failed to create reliable TSS: %w", err) - } - - engine, ok := relTSS.(*Engine) - if !ok { // shouldn't happen, but just in case. - return nil, fmt.Errorf("reliable TSS does not implement KeyGenerator interface") - } - - return engine, nil -} - -func NewReliableTSS(storage *GuardianStorage) (ReliableTSS, error) { - if storage == nil { - return nil, fmt.Errorf("the guardian's tss storage is nil") - } - - if storage.maxSimultaneousSignatures < 0 { - storage.maxSimultaneousSignatures = defaultMaxLiveSignatures - } - - if storage.MaxSignerTTL == 0 { - storage.MaxSignerTTL = defaultMaxSignerTTL - } - - if storage.maxSimultaneousSignatures == 0 { - storage.maxSimultaneousSignatures = defaultMaxLiveSignatures - } - - if bytes.Equal(storage.Self.CertPem, storage.LeaderIdentity) { - storage.isleader = true - } - - fpParams := &party.Parameters{ - FrostSecrets: storage.frostconf, - PartyIDs: storage.GetPartyIDs(), - Self: storage.Self.Pid, - - MaxSignerTTL: storage.MaxSignerTTL, - LoadDistributionSeed: storage.LoadDistributionKey, - } - - fp, err := party.NewFullParty(fpParams) - if err != nil { - return nil, err - } - - expectedMsgs := storage.maxSimultaneousSignatures * - (numBroadcastsPerSignature + numUnicastsRounds*storage.NumGuardians()) * 2 // times 2 to stay on the safe side. - t := &Engine{ - ctx: nil, - - logger: &zap.Logger{}, - GuardianStorage: *storage, - - fpParams: fpParams, - fp: fp, - fpCommChans: fpCommunicationChannels{ - OutChannel: make(chan common.ParsedMessage, expectedMsgs), - SignatureOutputChannel: make(chan *common.SignatureData, storage.maxSimultaneousSignatures), - ErrChannel: make(chan *common.Error, storage.maxSimultaneousSignatures), - WarningChannel: make(chan *party.Warning, storage.maxSimultaneousSignatures), - KeygenOutputChannel: make(chan *party.TSSSecrets, 1), // shouldn't output often. - }, - - sigOutChan: make(chan *common.SignatureData, storage.maxSimultaneousSignatures), - messageOutChan: make(chan Sendable, expectedMsgs), - - msgSerialNumber: 0, - mtx: &sync.Mutex{}, - received: map[uuid]*broadcaststate{}, - - started: atomic.Uint32{}, // default value is 0 - - sigCounter: newSigCounter(), - - ftCommandChan: make(chan ftCommand, expectedMsgs), - } - - return t, nil -} - -func (t *Engine) MaxTTL() time.Duration { - return t.GuardianStorage.maxSignerTTL() -} - -// Start starts the TSS engine, and listens for the outputs of the full party. -func (t *Engine) Start(ctx context.Context) error { - if t == nil { - return fmt.Errorf("tss engine is nil") - } - - if !t.started.CompareAndSwap(notStarted, started) { - return fmt.Errorf("tss engine has already started") - } - - t.ctx = ctx - t.logger = supervisor.Logger(ctx). - With(zap.String("hostname", t.GuardianStorage.Self.Hostname)). - Named("tss") - - if err := t.fp.Start(party.OutputChannels(t.fpCommChans)); err != nil { - t.started.Store(notStarted) - - return err - } - - // closing the t.fp.start inside th listener - go t.fpListener() - - go t.sigTracker() - - leaderIdentity, err := t.GuardianStorage.fetchIdentityFromKeyPEM(t.LeaderIdentity) - if err != nil { - return fmt.Errorf("leader identity not found in guardian storage: %w", err) - } - - t.logger.Info( - "tss engine started", - zap.Any("configs", t.GuardianStorage.Configurations), - zap.Bool("hasGuardianSet", t.gst != nil), - zap.String("leaderID", leaderIdentity.Hostname), - ) - - return nil -} - -func (t *Engine) GetPublicKey() (curve.Point, error) { - pk, err := t.fp.GetPublic() - if err != nil { - return nil, fmt.Errorf("failed to get public key from full party: %w", err) - } - - return pk, nil -} - -func (t *Engine) GetEthAddress() (ethcommon.Address, error) { - pubkey, err := t.fp.GetPublic() - if err != nil { - return ethcommon.Address{}, fmt.Errorf("failed to get public key from full party: %w", err) - } - - ethaddress := ethcommon.Address{} - - add, err := frosteth.PointToAddress(pubkey) - if err != nil { - t.logger.Error("failed to convert public key to Ethereum address", zap.Error(err)) - } - - copy(ethaddress[:], add[:]) - - return ethaddress, nil -} - -func (st *GuardianStorage) maxSignerTTL() time.Duration { - // SECURITY NOTE: when we clean the guardian map from received Echo's - // we must use TTL > FullParty.TTL to ensure guardians can't use - // the deletion time to perform equivication attacks (since a message - // has no record after it was deleted). - // *2 is to account for possible offset in the time of the guardian. - return st.MaxSignerTTL * 2 -} - -// fpListener serves as a listining loop for the full party outputs. -// ensures the FP isn't being blocked on writing to fpOutChan, and wraps the result into a gossip message. -// IMPORTANT: the fpListener should not wait on writing to other channels! -// if the channel is full, the message should be dropped. -func (t *Engine) fpListener() { - maxTTL := t.MaxTTL() - - cleanUpTicker := time.NewTicker(maxTTL) - - for { - select { - case <-t.ctx.Done(): - t.logger.Info( - "shutting down TSS Engine", - ) - - t.fp.Stop() - cleanUpTicker.Stop() - - return - case m := <-t.fpCommChans.OutChannel: - t.handleFpOutput(m) - - case err := <-t.fpCommChans.ErrChannel: - t.handleFpError(err) - - case warn := <-t.fpCommChans.WarningChannel: - t.handleFPWarning(warn) - - case sig := <-t.fpCommChans.SignatureOutputChannel: - t.handleFpSignature(sig) - - case <-cleanUpTicker.C: - t.cleanup(maxTTL) - } - } -} - -func (t *Engine) handleFPWarning(warn *party.Warning) { - if warn == nil || warn.Message == "" { - return - } - - flds := []zap.Field{} - - if warn.TrackingID != nil { - flds = append(flds, zap.String("trackingId", warn.TrackingID.ToString())) - } - - if warn.Protocol != "" { - flds = append(flds, zap.String("protocol", string(warn.Protocol))) - } - - if warn.SessionRound != 0 { - flds = append(flds, zap.Int("round", int(warn.SessionRound))) - } - - if id, err := t.GuardianStorage.fetchIdentityFromPartyID(warn.PossibleCulprit); err == nil { - flds = append(flds, zap.String("possibleCulprit", id.Hostname)) - } - - t.logger.Warn( - fmt.Sprintf("tss-lib.FullParty: %s", warn.Message), - flds..., - ) -} - -func (t *Engine) handleFpSignature(sig *common.SignatureData) { - if sig == nil { - return - } - - t.logger.Debug("signature complete. updating inner state and forwarding it", zap.String("trackingId", sig.TrackingId.ToString())) - - t.sigCounter.remove(sig.TrackingId) - - select { - case t.ftCommandChan <- &SigEndCommand{sig.TrackingId}: - default: - // This is a warning, since the ftTracker will eventually clean the sigState matching the trackingID. - t.logger.Warn( - "couldn't inform the tracker of the signature end", - zap.String("trackingId", sig.TrackingId.ToString()), - ) - } - - select { - case t.sigOutChan <- sig: - default: - // if the signature can't be delivered, we can't do much about it. - t.logger.Error( - "Couldn't deliver the signature, signature output channel buffer is full", - zap.String("trackingId", sig.TrackingId.ToString()), - ) - } - - t.sigMetricDone(sig.TrackingId, false) // false since there were no issues. -} - -func (t *Engine) handleFpError(err *common.Error) { - if err == nil { - return - } - - trackid := err.TrackingId() - if trackid == nil { - t.logger.Error("error (without trackingID) in signing protocol ", zap.Error(err.Cause())) - - return - } - - select { - case t.ftCommandChan <- &SigEndCommand{trackid}: - default: - t.logger.Error("couldn't inform the tracker of signature end due to error", - zap.Error(err), - zap.String("trackingId", trackid.ToString()), - ) - } - - // if someone sent a message that caused an error -> we don't - // accept an override to that message, therefore, we can remove it, since it won't change. - t.sigCounter.remove(trackid) - - logErr(t.logger, &logableError{ - fmt.Errorf("error in signing protocol: %w", err.Cause()), - trackid, - intToRound(err.Round()), - }) - - t.sigMetricDone(trackid, true) -} - -func (t *Engine) handleFpOutput(m common.Message) { - tssMsg, err := t.intoSendable(m) - if err == nil { - - select { - case t.messageOutChan <- tssMsg: - default: - t.logger.Error("couldn't output tss message, network output channel buffer is full", - zap.String("trackingId", m.WireMsg().GetTrackingID().ToString()), - ) - } - - return - } - - // else log error: - lgErr := logableError{ - fmt.Errorf("failed to convert tss message and send it to network: %w", err), - m.WireMsg().GetTrackingID(), - "", - } - - // The following should always pass, since FullParty outputs a - // common.ParsedMessage and a valid message with a specific round. - if parsed, ok := m.(common.ParsedMessage); ok { - if rnd, e := getRound(parsed); e == nil { - lgErr.round = rnd - } - } - - logErr(t.logger, lgErr) -} - -func (t *Engine) cleanup(maxTTL time.Duration) { - now := time.Now() - - keysToBeRemoved := make([]any, 0) - - t.SignatureMetrics.Range(func(k, v any) bool { - mt, ok := v.(*signatureMetadata) - if !ok { - keysToBeRemoved = append(keysToBeRemoved, k) - - return true - } - - tmp := now.Sub(mt.timeOfCreation) - if tmp > maxTTL { - keysToBeRemoved = append(keysToBeRemoved, k) - } - - return true - }) - - for _, k := range keysToBeRemoved { - t.SignatureMetrics.Delete(k) - } - - t.sigCounter.cleanSelf(maxTTL) - - t.mtx.Lock() - defer t.mtx.Unlock() - - for k, v := range t.received { - if now.Sub(v.timeReceived) > maxTTL { - delete(t.received, k) - } - } -} - -func (t *Engine) intoSendable(m common.Message) (Sendable, error) { - bts, routing, err := m.WireBytes() - if err != nil { - return nil, err - } - - content := &tsscommv1.SignedMessage_TssContent{ - TssContent: &tsscommv1.TssContent{ - Payload: bts, - MsgSerialNumber: atomic.AddUint64(&t.msgSerialNumber, 1), - }, - } - - var sendable Sendable - - if routing.IsBroadcast() { - msgToSend := &tsscommv1.SignedMessage{ - Content: content, - Sender: t.Self.CommunicationIndex.toProto(), - Signature: nil, - } - - tmp := serializeableMessage{&tssMessageWrapper{m}} - - if err := t.sign(tmp.getUUID(t.LoadDistributionKey), msgToSend); err != nil { - return nil, err - } - - sendable = newEcho(msgToSend, t.Identities) - } else { - recipient, err := t.GuardianStorage.fetchIdentityFromPartyID(routing.To) - if err != nil { - return nil, fmt.Errorf("intoSendable: couldn't fetch partyID: %w", err) - } - - sendable = &Unicast{ - Unicast: &tsscommv1.Unicast{ - Content: &tsscommv1.Unicast_Tss{ - Tss: content.TssContent, - }, - }, - Receipients: []*Identity{recipient}, - } - } - - return sendable, nil -} - -func (t *Engine) HandleIncomingTssMessage(msg Incoming) { - if t == nil { - return // TODO: Consider what to do. - } - - if t.started.Load() != started { - return // TODO: Consider what to do. - } - - if err := t.handleIncomingTssMessage(msg); err != nil { - logErr(t.logger, err) - } -} - -var ( - errNilIncoming = fmt.Errorf("received nil incoming message") - errNilSource = fmt.Errorf("no source in incoming message") - errNeitherBroadcastNorUnicast = fmt.Errorf("received incoming message which is neither broadcast nor unicast") -) - -func (t *Engine) handleIncomingTssMessage(msg Incoming) error { - if msg == nil { - return errNilIncoming - } - - if msg.GetSource() == nil { - return errNilSource - } - - if msg.IsUnicast() { - return t.handleUnicast(msg) - } else if !msg.IsBroadcast() { - return errNeitherBroadcastNorUnicast - } - - if err := t.handleBroadcast(msg); err != nil { - return err - } - - return nil -} - -func (t *Engine) sendEchoOut(parsed broadcastMessage, m Incoming) { - select { - case t.messageOutChan <- t.makeEcho(m, parsed): - default: - t.logger.Warn("couldn't echo the message, network output channel buffer is full") - } -} - -func (t *Engine) makeEcho(m Incoming, parsed broadcastMessage) *Echo { - e := m.toBroadcastMsg() - - uuid := parsed.getUUID(t.LoadDistributionKey) - contentDigest := hashSignedMessage(e.Message) - - content := &tsscommv1.SignedMessage{ - Sender: e.Message.Sender, - Signature: e.Message.Signature, - Content: &tsscommv1.SignedMessage_HashEcho{ - HashEcho: &tsscommv1.HashEcho{ - SessionUuid: uuid[:], - OriginalContetDigest: contentDigest[:], - }, - }, - } - ech := newEcho(content, t.GuardianStorage.Identities) - return ech -} - -func (t *Engine) handleBroadcast(m Incoming) error { - parsed, err := t.parseBroadcast(m) - if err != nil { - return err - } - - shouldEcho, deliverable, err := t.broadcastInspection(parsed, m) - if err != nil { - return err - } - - if shouldEcho { - t.sendEchoOut(parsed, m) - } - - if deliverable == nil { - return nil - } - - return deliverable.deliver(t) -} - -func (t *Engine) feedIncomingToFp(parsed common.ParsedMessage) error { - trackId := parsed.WireMsg().TrackingID - from := parsed.GetFrom() - - id, err := t.GuardianStorage.fetchIdentityFromPartyID(from) - if err != nil { - return fmt.Errorf("error feeding fullParty: %w", err) // shouldn't happen. - } - - maxLiveSignatures := t.GuardianStorage.maxSimultaneousSignatures - - if ok := t.sigCounter.add(trackId, from, maxLiveSignatures); !ok { - tooManySimulSigsErrCntr.Inc() - - return fmt.Errorf("guardian %v has reached the maximum number of simultaneous signatures", id.Hostname) - } - - if err := t.fp.Update(parsed); err != nil { - return fmt.Errorf("failed to update full party with incoming message: %w", err) - } - - return nil -} - -// handleUnicast is responsible to handle any incoming unicast messages. -func (t *Engine) handleUnicast(m Incoming) error { - unicast := m.toUnicast() - if err := validateUnicastCorrectForm(unicast); err != nil { - return err - } - - switch v := unicast.Content.(type) { - case *tsscommv1.Unicast_Vaav1: - if err := t.handleUnicastVaaV1(v); err != nil { - return fmt.Errorf("failed to handle unicast vaav1: %w", err) - } - case *tsscommv1.Unicast_Tss: - if err := t.handleUnicastTSS(v, m.GetSource()); err != nil { - return fmt.Errorf("failed to handle unicast tss message: %w", err) - } - default: - return fmt.Errorf("received unicast with unknown content type: %T", v) - } - - return nil -} - -// handleUnicastTSS is helper function. responsible for handling unicast.TSS messages. -func (t *Engine) handleUnicastTSS(v *tsscommv1.Unicast_Tss, src *Identity) error { - fpmsg, err := t.parseTssContent(v.Tss, src) - if err != nil { - err = fmt.Errorf("couldn't parse unicast_tss payload: %w", err) - if fpmsg != nil { - err = fpmsg.wrapError(err) - } - - return err - } - - if isBroadcastMsg(fpmsg) { - return fmt.Errorf("received broadcast type message in unicast: %v", fpmsg) - } - - err = t.validateUnicastDoesntExist(fpmsg) - if err == errUnicastAlreadyReceived { - return nil - } - if err != nil { - return fpmsg.wrapError(fmt.Errorf("failed to ensure no equivication present in unicast: %w, sender:%v", err, src.Hostname)) - } - - if err := t.feedIncomingToFp(fpmsg); err != nil { - return fpmsg.wrapError(fmt.Errorf("unicast failed to update the full party: %w", err)) - } - - return nil -} - -var errUnicastAlreadyReceived = fmt.Errorf("unicast already received") - -func (t *Engine) validateUnicastDoesntExist(parsed common.ParsedMessage) error { - tmp := serializeableMessage{&tssMessageWrapper{parsed}} - id := tmp.getUUID(t.LoadDistributionKey) - - bts, _, err := parsed.WireBytes() - if err != nil { - return fmt.Errorf("failed storing the unicast: %w", err) - } - - msgDigest := hash(bts) - - t.mtx.Lock() - defer t.mtx.Unlock() - - if stored, ok := t.received[id]; ok { - if stored.verifiedDigest == nil { - return fmt.Errorf("internal error. Unicast stored without verified hash") - } - - if *stored.verifiedDigest != msgDigest { - return fmt.Errorf("%w. (duration from prev unicast %v)", ErrEquivicatingGuardian, time.Since(stored.timeReceived)) - } - - return errUnicastAlreadyReceived - } - - t.received[id] = &broadcaststate{ - timeReceived: time.Now(), // used for GC. - verifiedDigest: &msgDigest, // used to ensure no equivocation. - votes: nil, // no votes should be stored for a unicast. - echoedAlready: true, // ensuring this never echoed since it is a unicast. - mtx: nil, // no need to lock this, just store it. - } - - return nil -} - -var ( - ErrUnkownEchoer = fmt.Errorf("echoer is not a known guardian") - ErrUnkownSender = fmt.Errorf("sender is not a known guardian") -) - -func (st *GuardianStorage) sign(uuid uuid, msg *tsscommv1.SignedMessage) error { - tmp := hashSignedMessage(msg) - digest := hash(append(uuid[:], tmp[:]...)) - - sig, err := st.signingKey.Sign(rand.Reader, digest[:], nil) - msg.Signature = sig - - return err -} - -var ErrInvalidSignature = fmt.Errorf("invalid signature") - -var errEmptySignature = fmt.Errorf("empty signature") - -func (st *GuardianStorage) verifySignedMessage(uid uuid, msg *tsscommv1.SignedMessage) error { - if msg == nil { - return fmt.Errorf("nil signed message") - } - - if msg.Signature == nil { - return errEmptySignature - } - - id, err := st.fetchIdentityFromIndex(SenderIndex(msg.Sender)) - if err != nil { - return err - } - - pk, ok := id.Cert.PublicKey.(*ecdsa.PublicKey) - if !ok { - return fmt.Errorf("certificated stored with non-ecdsa public key, guardian storage is corrupted") - } - - tmp := hashSignedMessage(msg) - digest := hash(append(uid[:], tmp[:]...)) - - isValid := ecdsa.VerifyASN1(pk, digest[:], msg.Signature) - - if !isValid { - return ErrInvalidSignature - } - - return nil -} - -func (t *Engine) StartDKG(task party.DkgTask) (chan *party.TSSSecrets, error) { - if t == nil { - return nil, fmt.Errorf("tss engine is nil") - } - - if t.started.Load() != started { - return nil, fmt.Errorf("tss engine hasn't started") - } - - if t.fp == nil { - return nil, fmt.Errorf("tss engine is not set up correctly, use NewReliableTSS to create a new engine") - } - - t.logger.Info("starting DKG") - - err := t.fp.StartDKG(task) - - return t.fpCommChans.KeygenOutputChannel, err -} diff --git a/node/pkg/tss/implementation_test.go b/node/pkg/tss/implementation_test.go deleted file mode 100644 index bccd1542db..0000000000 --- a/node/pkg/tss/implementation_test.go +++ /dev/null @@ -1,2122 +0,0 @@ -package tss - -import ( - "bytes" - "context" - crand "crypto/rand" - "crypto/sha512" - "crypto/x509" - "crypto/x509/pkix" - "errors" - "fmt" - "math/big" - "math/rand" - "net" - "sync" - "testing" - "time" - - whcommon "github.com/certusone/wormhole/node/pkg/common" - "github.com/certusone/wormhole/node/pkg/guardiansigner" - "github.com/certusone/wormhole/node/pkg/internal/testutils" - tsscommv1 "github.com/certusone/wormhole/node/pkg/proto/tsscomm/v1" - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - dto "github.com/prometheus/client_model/go" - "github.com/stretchr/testify/assert" - "github.com/wormhole-foundation/wormhole/sdk/vaa" - "github.com/xlabs/multi-party-sig/pkg/round" - "github.com/xlabs/multi-party-sig/protocols/frost" - "github.com/xlabs/multi-party-sig/protocols/frost/sign" - common "github.com/xlabs/tss-common" - "github.com/xlabs/tss-lib/v2/party" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - "go.uber.org/zap/zaptest/observer" - "google.golang.org/protobuf/proto" -) - -var ( - unicastRounds = []signingRound{} - broadcastRounds = []signingRound{ - round2Message, - round3Message, - } - - allRounds = append(unicastRounds, broadcastRounds...) - reportableConsistancyLevel = uint8(1) // TODO - nonReportableConsistancyLevel = instantConsistencyLevel // TODO -) - -func parsedIntoEcho(a *assert.Assertions, t *Engine, parsed common.ParsedMessage) *IncomingMessage { - payload, _, err := parsed.WireBytes() - a.NoError(err) - - msg := &tsscommv1.Echo{ - Message: &tsscommv1.SignedMessage{ - Content: &tsscommv1.SignedMessage_TssContent{ - TssContent: &tsscommv1.TssContent{Payload: payload}, - }, - Sender: uint32(t.Self.CommunicationIndex), - Signature: nil, - }, - } - - tmp := serializeableMessage{&tssMessageWrapper{parsed}} - - a.NoError(t.sign(tmp.getUUID(t.LoadDistributionKey), msg.Message)) - - return &IncomingMessage{ - Source: t.Self, - Content: &tsscommv1.PropagatedMessage{ - Message: &tsscommv1.PropagatedMessage_Echo{ - Echo: msg, - }, - }, - } -} - -func (i *IncomingMessage) setSource(id *Identity) { - i.Source = id -} - -func TestBroadcast(t *testing.T) { - - // The tests here rely on n=5, threshold=2, meaning 3 guardians are needed to sign (f<=1). - t.Run("forLeaderCreatingMessage", func(t *testing.T) { - a := assert.New(t) - // f = 1, n = 5 - engines := load5GuardiansSetupForBroadcastChecks(a) - receiver := engines[4] - - e1 := engines[0] - // make parsedMessage, and insert into e1 - // then add another one for the same round. - for j, rnd := range allRounds { - parsed1 := generateFakeMessageWithRandomContent(e1.Self.Pid, receiver.Self.Pid, rnd, party.Digest{byte(j)}) - - echo := parsedIntoEcho(a, e1, parsed1) - - shouldBroadcast, shouldDeliver, err := receiver.broadcastInspection(&deliverableMessage{&parsedTssContent{parsed1, ""}}, echo) - a.NoError(err) - a.True(shouldBroadcast) - a.Nil(shouldDeliver) - } - }) - - t.Run("forLeaderNotReBroadcasting", func(t *testing.T) { - a := assert.New(t) - // f = 1, n = 5 - engines := load5GuardiansSetupForBroadcastChecks(a) - - e1 := engines[0] - receiver := e1 - // make parsedMessage, and insert into e1 - // then add another one for the same round. - for j, rnd := range allRounds { - parsed1 := generateFakeMessageWithRandomContent(e1.Self.Pid, receiver.Self.Pid, rnd, party.Digest{byte(j)}) - - echo := parsedIntoEcho(a, e1, parsed1) - - shouldBroadcast, shouldDeliver, err := receiver.broadcastInspection(&deliverableMessage{&parsedTssContent{parsed1, ""}}, echo) - a.NoError(err) - a.False(shouldBroadcast) - a.Nil(shouldDeliver) - } - }) - - t.Run("OnlyOnce", func(t *testing.T) { - a := assert.New(t) - // f = 1, n = 5 - engines := load5GuardiansSetupForBroadcastChecks(a) - receiver := engines[4] - - e1 := engines[0] - // make parsedMessage, and insert into e1 - // then add another one for the same round. - for j, rnd := range allRounds { - parsed1 := generateFakeMessageWithRandomContent(e1.Self.Pid, receiver.Self.Pid, rnd, party.Digest{byte(j)}) - - echo := parsedIntoEcho(a, e1, parsed1) - - shouldBroadcast, shouldDeliver, err := receiver.broadcastInspection(&deliverableMessage{&parsedTssContent{parsed1, ""}}, echo) - a.NoError(err) - a.True(shouldBroadcast) - a.Nil(shouldDeliver) - - shouldBroadcast, shouldDeliver, err = receiver.broadcastInspection(&deliverableMessage{&parsedTssContent{parsed1, ""}}, echo) - a.NoError(err) - a.False(shouldBroadcast) - a.Nil(shouldDeliver) - - shouldBroadcast, shouldDeliver, err = receiver.broadcastInspection(&deliverableMessage{&parsedTssContent{parsed1, ""}}, echo) - a.NoError(err) - a.False(shouldBroadcast) - a.Nil(shouldDeliver) - } - }) - - t.Run("waitForActualValueFromLeader", func(t *testing.T) { - a := assert.New(t) - engines := load5GuardiansSetupForBroadcastChecks(a) - e1, e2, e3 := engines[0], engines[1], engines[2] - - receiver := engines[4] - // two different signers on an echo, meaning it will receive from two players. - // since f=1 and we have f+1 echos: it should broadcast at the end of this test. - for j, rnd := range allRounds { - parsed1 := generateFakeMessageWithRandomContent(e1.Self.Pid, receiver.Self.Pid, rnd, party.Digest{byte(j)}) - - originalValue := parsedIntoEcho(a, e1, parsed1) - - echo := makeHashEcho(e1, parsed1, originalValue) - - parsed := &parsedHashEcho{ - HashEcho: echo.toBroadcastMsg().Message.GetHashEcho(), - } - - echo.setSource(e2.Self) - - shouldBroadcast, deliverable, err := receiver.broadcastInspection(parsed, echo) - a.NoError(err) - a.False(shouldBroadcast) - a.Nil(deliverable) - - echo.setSource(e3.Self) - - shouldBroadcast, deliverable, err = receiver.broadcastInspection(parsed, echo) - a.NoError(err) - a.False(shouldBroadcast) // should broadcast only for leader. - a.Nil(deliverable) - - echo.setSource(e1.Self) - - shouldBroadcast, deliverable, err = receiver.broadcastInspection(parsed, echo) - a.NoError(err) - a.False(shouldBroadcast) // should not broadcast if it hadn't seen the actual value from the leader! - a.Nil(deliverable) - - shouldBroadcast, deliverable, err = receiver.broadcastInspection(&deliverableMessage{&parsedTssContent{parsed1, ""}}, originalValue) - a.NoError(err) - a.True(shouldBroadcast) // should echo when seeing the actual value from the leader. - a.NotNil(deliverable) - } - }) -} - -func load5GuardiansSetupForBroadcastChecks(a *assert.Assertions) []*Engine { - engines, err := loadGuardians(5, "tss5") // f=1, n=5. - a.NoError(err) - - for _, v := range engines { - v.GuardianStorage.Threshold = 2 // meaning 3 guardians are needed to sign. - } - - return engines -} - -func makeHashEcho(e *Engine, parsed common.ParsedMessage, in *IncomingMessage) *IncomingMessage { - echocpy := proto.Clone(in.toBroadcastMsg()).(*tsscommv1.Echo) - - outgoing := &IncomingMessage{ - Source: in.Source, - Content: &tsscommv1.PropagatedMessage{ - Message: &tsscommv1.PropagatedMessage_Echo{ - Echo: echocpy, - }, - }} - - tmp := serializeableMessage{&tssMessageWrapper{parsed}} - - uid := tmp.getUUID(e.LoadDistributionKey) - dgst := hashSignedMessage(echocpy.Message) - - hshEcho := &tsscommv1.HashEcho{ - SessionUuid: uid[:], - OriginalContetDigest: dgst[:], - } - - outgoing.toBroadcastMsg().Message.Content = &tsscommv1.SignedMessage_HashEcho{HashEcho: hshEcho} - return outgoing - -} -func TestDeliver(t *testing.T) { - t.Run("After2fPlus1Messages", func(t *testing.T) { - a := assert.New(t) - engines := load5GuardiansSetupForBroadcastChecks(a) - e1, e2, e3 := engines[0], engines[1], engines[2] - - receiver := engines[4] - // two different signers on an echo, meaning it will receive from two players. - // since f=1 and we have f+1 echos: it should broadcast at the end of this test. - for j, rnd := range allRounds { - parsed1 := generateFakeMessageWithRandomContent(e1.Self.Pid, receiver.Self.Pid, rnd, party.Digest{byte(j)}) - - echo := parsedIntoEcho(a, e1, parsed1) - hshEcho := makeHashEcho(e1, parsed1, echo) - hshEcho.setSource(e2.Self) - - prsedHashEcho := &parsedHashEcho{hshEcho.toBroadcastMsg().Message.GetHashEcho()} - shouldBroadcast, deliverable, err := receiver.broadcastInspection(prsedHashEcho, hshEcho) - - a.NoError(err) - a.False(shouldBroadcast) - a.Nil(deliverable) - - hshEcho.setSource(e3.Self) - - shouldBroadcast, deliverable, err = receiver.broadcastInspection(prsedHashEcho, hshEcho) - a.NoError(err) - a.False(shouldBroadcast) // haven't seen the actual value from the leader yet. - a.Nil(deliverable) - - echo.setSource(e1.Self) - - shouldBroadcast, deliverable, err = receiver.broadcastInspection(&deliverableMessage{&parsedTssContent{parsed1, ""}}, echo) - a.NoError(err) - a.True(shouldBroadcast) - a.NotNil(deliverable) - } - }) - - t.Run("doesn'tDeliverTwice", func(t *testing.T) { - a := assert.New(t) - engines := load5GuardiansSetupForBroadcastChecks(a) - e1, e2, e3, e4 := engines[0], engines[1], engines[2], engines[3] - - receiver := engines[4] - // two different signers on an echo, meaning it will receive from two players. - // since f=1 and we have f+1 echos: it should broadcast at the end of this test. - for j, rnd := range allRounds { - parsed1 := generateFakeMessageWithRandomContent(e1.Self.Pid, receiver.Self.Pid, rnd, party.Digest{byte(j)}) - echo := parsedIntoEcho(a, e1, parsed1) - hashecho := makeHashEcho(e1, parsed1, echo) - hashecho.setSource(e2.Self) - - prsedHashEcho := &parsedHashEcho{hashecho.toBroadcastMsg().Message.GetHashEcho()} - shouldBroadcast, deliverable, err := receiver.broadcastInspection(prsedHashEcho, hashecho) - a.NoError(err) - a.False(shouldBroadcast) - a.Nil(deliverable) - - hashecho.setSource(e3.Self) - - shouldBroadcast, deliverable, err = receiver.broadcastInspection(prsedHashEcho, hashecho) - a.NoError(err) - a.False(shouldBroadcast) - a.Nil(deliverable) - - echo.setSource(e1.Self) - - shouldBroadcast, deliverable, err = receiver.broadcastInspection(&deliverableMessage{&parsedTssContent{parsed1, ""}}, echo) - a.NoError(err) - a.True(shouldBroadcast) - a.NotNil(deliverable) - - // twice in a row - shouldBroadcast, deliverable, err = receiver.broadcastInspection(&deliverableMessage{&parsedTssContent{parsed1, ""}}, echo) - a.NoError(err) - a.False(shouldBroadcast) - a.Nil(deliverable) - - // new hash echo, shouldn't deliver again too. - hashecho.setSource(e4.Self) - - shouldBroadcast, deliverable, err = receiver.broadcastInspection(prsedHashEcho, hashecho) - a.NoError(err) - a.False(shouldBroadcast) - a.Nil(deliverable) - } - }) -} - -func TestUuidNotAffectedByMessageContentChange(t *testing.T) { - a := assert.New(t) - engines := load5GuardiansSetupForBroadcastChecks(a) - e1 := engines[0] - for i, rnd := range allRounds { - trackingId := party.Digest{byte(i)} - - // each message is generated with some random content inside. - parsed1 := generateFakeParsedMessageWithRandomContent(e1.Self.Pid, e1.Self.Pid, rnd, trackingId) - parsed2 := generateFakeParsedMessageWithRandomContent(e1.Self.Pid, e1.Self.Pid, rnd, trackingId) - - uid1 := parsed1.getUUID(e1.LoadDistributionKey) - - uid2 := parsed2.getUUID(e1.LoadDistributionKey) - - a.Equal(uid1, uid2) - } -} - -func TestEquivocation(t *testing.T) { - t.Run("inBroadcastLogic", func(t *testing.T) { - a := assert.New(t) - engines := load5GuardiansSetupForBroadcastChecks(a) - e1, e2 := engines[0], engines[1] - - receiver := engines[4] - for i, rndType := range allRounds { - - trackingId := party.Digest{byte(i)} - - parsed1 := generateFakeMessageWithRandomContent(e1.Self.Pid, receiver.Self.Pid, rndType, trackingId) - - shouldBroadcast, deliverable, err := receiver.broadcastInspection(&deliverableMessage{&parsedTssContent{parsed1, ""}}, parsedIntoEcho(a, e2, parsed1)) - a.NoError(err) - a.True(shouldBroadcast) //should broadcast since e2 is the source of this message. - a.Nil(deliverable) - - parsed2 := generateFakeMessageWithRandomContent(e1.Self.Pid, e2.Self.Pid, rndType, trackingId) - - shouldBroadcast, deliverable, err = receiver.broadcastInspection(&deliverableMessage{&parsedTssContent{parsed2, ""}}, parsedIntoEcho(a, e2, parsed2)) - a.ErrorContains(err, "equivication") - a.False(shouldBroadcast) - a.Nil(deliverable) - - equvicatingEchoerMessage := parsedIntoEcho(a, e2, parsed1) - equvicatingEchoerMessage. - Content. - GetEcho(). - Message. - Content.(*tsscommv1.SignedMessage_TssContent). - TssContent. - Payload[0] += 1 - // now echoer is equivicating (change content, but of some seen message): - _, _, err = receiver.broadcastInspection(&deliverableMessage{&parsedTssContent{parsed1, ""}}, equvicatingEchoerMessage) - a.ErrorContains(err, e2.Self.Hostname) - } - }) - - t.Run("inUnicast", func(t *testing.T) { - a := assert.New(t) - engines := load5GuardiansSetupForBroadcastChecks(a) - e1, e2 := engines[0], engines[1] - - receiver := engines[4] - supctx := testutils.MakeSupervisorContext(context.Background()) - ctx, cncl := context.WithCancel(supctx) - defer cncl() - - e1.Start(ctx) - e2.Start(ctx) - - for i, rndType := range unicastRounds { - - trackingId := party.Digest{byte(i)} - - parsed1 := generateFakeMessageWithRandomContent(e1.Self.Pid, receiver.Self.Pid, rndType, trackingId) - parsed2 := generateFakeMessageWithRandomContent(e1.Self.Pid, receiver.Self.Pid, rndType, trackingId) - - bts, _, err := parsed1.WireBytes() - a.NoError(err) - - msg := &IncomingMessage{ - Content: &tsscommv1.PropagatedMessage{ - Message: &tsscommv1.PropagatedMessage_Unicast{ - Unicast: &tsscommv1.Unicast{ - Content: &tsscommv1.Unicast_Tss{ - Tss: &tsscommv1.TssContent{ - Payload: bts, - MsgSerialNumber: 0, - }, - }, - }, - }, - }, - } - - msg.setSource(e1.Self) - - receiver.handleUnicast(msg) - - bts, _, err = parsed2.WireBytes() - a.NoError(err) - - msg.Content.Message.(*tsscommv1.PropagatedMessage_Unicast). - Unicast.Content.(*tsscommv1.Unicast_Tss).Tss.Payload = bts - a.ErrorIs(receiver.handleUnicast(msg), ErrEquivicatingGuardian) - } - }) -} - -func TestBadInputs(t *testing.T) { - a := assert.New(t) - engines := load5GuardiansSetupForBroadcastChecks(a) - e1, e2 := engines[0], engines[1] - - supctx := testutils.MakeSupervisorContext(context.Background()) - ctx, cancel := context.WithTimeout(supctx, time.Minute*1) - defer cancel() - - e1.Start(ctx) // so it has a logger. - - t.Run("signature", func(t *testing.T) { - for j, rnd := range allRounds { - parsed1 := generateFakeMessageWithRandomContent(e1.Self.Pid, e1.Self.Pid, rnd, party.Digest{byte(j)}) - echo := parsedIntoEcho(a, e1, parsed1) - - echo.setSource(e1.Self) - - echo.toBroadcastMsg().Message.Signature[0] += 1 - _, _, err := e1.broadcastInspection(&deliverableMessage{&parsedTssContent{parsed1, ""}}, echo) - a.ErrorIs(err, ErrInvalidSignature) - - echo.setSource(e1.Self) - err = e1.handleIncomingTssMessage(echo) - a.ErrorIs(err, ErrInvalidSignature) - e1.HandleIncomingTssMessage(echo) // to ensure we go through some code path, nothing to check really. - } - }) - - t.Run("incoming message", func(t *testing.T) { - var tmp *Engine = nil - // these tests ensure we don't panic on bad inputs. - // Shouldn't fail or panic. - tmp.HandleIncomingTssMessage(nil) - e1.HandleIncomingTssMessage(nil) - e2.HandleIncomingTssMessage(nil) // e2 hadn't started. - - err := tmp.handleIncomingTssMessage(nil) - a.ErrorIs(err, errNilIncoming) - - err = e1.handleIncomingTssMessage(&IncomingMessage{}) - a.ErrorIs(err, errNilSource) - - err = e1.handleIncomingTssMessage(&IncomingMessage{Source: e2.Self}) - a.ErrorIs(err, errNeitherBroadcastNorUnicast) - - err = e1.handleIncomingTssMessage(&IncomingMessage{ - Source: e2.Self, - Content: &tsscommv1.PropagatedMessage{}}) - a.ErrorIs(err, errNeitherBroadcastNorUnicast) - - err = e1.handleIncomingTssMessage(&IncomingMessage{ - Source: e2.Self, - Content: &tsscommv1.PropagatedMessage{ - Message: &tsscommv1.PropagatedMessage_Echo{}, - }, - }) - a.ErrorIs(err, ErrBroadcastIsNil) - - err = e1.handleIncomingTssMessage(&IncomingMessage{ - Source: e2.Self, - Content: &tsscommv1.PropagatedMessage{ - Message: &tsscommv1.PropagatedMessage_Echo{Echo: &tsscommv1.Echo{}}, - }, - }) - a.ErrorIs(err, ErrSignedMessageIsNil) - - e2id, err := e2.fetchIdentityFromPartyID(e2.Self.Pid) - a.NoError(err) - - err = e1.handleIncomingTssMessage(&IncomingMessage{Source: e2.Self, Content: &tsscommv1.PropagatedMessage{ - Message: &tsscommv1.PropagatedMessage_Echo{Echo: &tsscommv1.Echo{ - Message: &tsscommv1.SignedMessage{ - Sender: uint32(e2id.CommunicationIndex), - }, - }}}, - }) - a.ErrorIs(err, ErrNoContent) - - err = e1.handleIncomingTssMessage(&IncomingMessage{Source: e2.Self, Content: &tsscommv1.PropagatedMessage{ - Message: &tsscommv1.PropagatedMessage_Echo{Echo: &tsscommv1.Echo{ - Message: &tsscommv1.SignedMessage{ - Content: &tsscommv1.SignedMessage_TssContent{ - TssContent: &tsscommv1.TssContent{}, - }, - Sender: uint32(e2id.CommunicationIndex), - Signature: []byte{1, 2, 3}, - }, - }}}, - }) - a.ErrorIs(err, ErrNilPayload) - - err = e1.handleIncomingTssMessage(&IncomingMessage{Source: e2.Self, Content: &tsscommv1.PropagatedMessage{ - Message: &tsscommv1.PropagatedMessage_Echo{Echo: &tsscommv1.Echo{ - Message: &tsscommv1.SignedMessage{ - Content: &tsscommv1.SignedMessage_TssContent{ - TssContent: &tsscommv1.TssContent{ - Payload: []byte{1, 2, 3}, - }, - }, - Sender: uint32(e2id.CommunicationIndex), - }, - }}}, - }) - a.ErrorIs(err, errEmptySignature) - - err = e1.handleIncomingTssMessage(&IncomingMessage{Source: e2.Self, Content: &tsscommv1.PropagatedMessage{ - Message: &tsscommv1.PropagatedMessage_Echo{Echo: &tsscommv1.Echo{ - Message: &tsscommv1.SignedMessage{ - Content: &tsscommv1.SignedMessage_TssContent{ - TssContent: &tsscommv1.TssContent{ - Payload: []byte{1, 2, 3}, - }, - }, - Sender: uint32(e2id.CommunicationIndex), - Signature: []byte{1, 2, 3}, - }, - }}}, - }) - a.ErrorContains(err, "cannot parse") - }) - - t.Run("Begin signing", func(t *testing.T) { - var tmp *Engine = nil - engines2 := load5GuardiansSetupForBroadcastChecks(a) - - a.ErrorIs(tmp.BeginAsyncThresholdSigningProtocol(nil, 0, reportableConsistancyLevel), errNilTssEngine) - a.ErrorIs(e2.BeginAsyncThresholdSigningProtocol(nil, 0, reportableConsistancyLevel), errTssEngineNotStarted) - - tmp = engines2[1] - tmp.started.Store(started) - - a.ErrorContains(e1.BeginAsyncThresholdSigningProtocol(make([]byte, 12), 0, reportableConsistancyLevel), "length is not 32 bytes") - - tmp.fp = nil - a.ErrorContains(tmp.BeginAsyncThresholdSigningProtocol(nil, 0, reportableConsistancyLevel), "not set up correctly") - }) - - t.Run("fetch certificate", func(t *testing.T) { - _, err := e1.fetchIdentityFromIndex(SenderIndex(e1.GuardianStorage.NumGuardians() + 1)) - a.ErrorIs(err, ErrUnkownSender) - }) - - t.Run("handle incoming VAAs", func(t *testing.T) { - a := assert.New(t) - - v, gs := genVaaAndGuardianSet(a) - - gst := whcommon.NewGuardianSetState(nil) - gst.Set(gs) - - engines := load5GuardiansSetupForBroadcastChecks(a) - engine := engines[0] // Not starting engine so it doesn't run BeginTSSSign - - engine.SetGuardianSetState(gst) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - ctx = testutils.MakeSupervisorContext(ctx) - - engine.Start(ctx) - - // bad verfication run - v.Version = 2 - v.Nonce = 0 - - bts, err := v.Marshal() - a.NoError(err) - - engine.LeaderIdentity = engine.Self.KeyPEM - - t.Run("Bad Version", func(t *testing.T) { - err = engine.handleUnicastVaaV1(&tsscommv1.Unicast_Vaav1{ - Vaav1: &tsscommv1.VaaV1Info{ - Marshaled: bts, - }, - }) - - a.ErrorContains(err, errNotVaaV1.Error()) - }) - - v.Version = vaa.VaaVersion1 - bts, err = v.Marshal() - a.NoError(err) - - t.Run("Bad Signature", func(t *testing.T) { - err = engine.handleUnicastVaaV1(&tsscommv1.Unicast_Vaav1{ - Vaav1: &tsscommv1.VaaV1Info{ - Marshaled: bts, - }, - }) - - a.ErrorContains(err, "signature") - }) - - t.Run("Bad Marshal", func(t *testing.T) { - err = engine.handleUnicastVaaV1(&tsscommv1.Unicast_Vaav1{ - Vaav1: &tsscommv1.VaaV1Info{ - Marshaled: []byte("BadMarshal"), - }, - }) - - a.ErrorContains(err, "unmarshal") - }) - - t.Run("nil VAA", func(t *testing.T) { - err = engine.handleUnicastVaaV1(nil) - - a.ErrorContains(err, "nil") - }) - - t.Run("no guardian set state", func(t *testing.T) { - engine.gst = nil - - err = engine.handleUnicastVaaV1(&tsscommv1.Unicast_Vaav1{ - Vaav1: &tsscommv1.VaaV1Info{ - Marshaled: bts, - }, - }) - - a.ErrorContains(err, "guardianSet") - }) - }) - - t.Run("witness Vaas", func(t *testing.T) { - a := assert.New(t) - - v, gs := genVaaAndGuardianSet(a) - - gst := whcommon.NewGuardianSetState(nil) - gst.Set(gs) - - engines := load5GuardiansSetupForBroadcastChecks(a) - engine := engines[0] // Not starting engine so it doesn't run BeginTSSSign - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - - ctx = testutils.MakeSupervisorContext(ctx) - - a.ErrorContains(engine.WitnessNewVaa(v), errTssEngineNotStarted.Error()) - - engine.Start(ctx) - - engine.isleader = true - a.ErrorContains(engine.WitnessNewVaa(v), errNilGuardianSetState.Error()) - engine.gst = gst - - a.NoError(engine.WitnessNewVaa(v)) - - a.ErrorContains(engine.WitnessNewVaa(nil), "nil") - a.NoError(engine.WitnessNewVaa(v)) - - engine.messageOutChan = nil - a.NoError(engine.WitnessNewVaa(v)) //shouldn't output error but log. - - v.Version += 1 - a.NoError(engine.WitnessNewVaa(v)) - - engine = nil - a.ErrorContains(engine.WitnessNewVaa(v), errNilTssEngine.Error()) - }) -} - -func createX509Cert(dnsName string) *x509.Certificate { - // using random serial number - var serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128) - - serialNumber, err := crand.Int(crand.Reader, serialNumberLimit) - if err != nil { - panic(err) - } - - tmpl := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{Organization: []string{"tsscomm"}}, - SignatureAlgorithm: x509.ECDSAWithSHA256, - NotBefore: time.Now(), - NotAfter: time.Now().Add(time.Hour * 24 * 366 * 40), // valid for > 40 years used for tests... - BasicConstraintsValid: true, - - DNSNames: []string{"localhost", dnsName}, - IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1)}, - } - return &tmpl -} - -func TestFetchPartyId(t *testing.T) { - a := assert.New(t) - engines, err := loadGuardians(5, "tss5") - a.NoError(err) - - e1 := engines[0] - id, err := e1.FetchIdentity(e1.Self.Cert) - a.NoError(err) - a.True(e1.Self.Pid.Equals(id.Pid)) - - crt := createX509Cert("localhost") - _, err = e1.FetchIdentity(crt) - a.ErrorContains(err, "unsupported") // cert.PublicKey=nil - - crt.PublicKey = []byte{1, 2, 3} - _, err = e1.FetchIdentity(crt) - a.ErrorContains(err, "unknown") -} - -func TestCleanup(t *testing.T) { - a := assert.New(t) - engines := load5GuardiansSetupForBroadcastChecks(a) - e1 := engines[0] - - uuid1 := uuid{1} - e1.received[uuid1] = &broadcaststate{ - timeReceived: time.Now().Add(time.Minute * 10 * (-1)), - } - - uuid2 := uuid{2} - e1.received[uuid2] = &broadcaststate{ - timeReceived: time.Now(), - } - - e1.cleanup(time.Minute * 5) // if more than 5 minutes passed -> delete - a.Len(e1.received, 1) - _, ok := e1.received[uuid{1}] - a.False(ok) - - _, ok = e1.received[uuid{2}] - a.True(ok) -} - -type badtssMessage struct { -} - -func (b *badtssMessage) ValidateBasic() bool { return true } -func (b *badtssMessage) GetRound() int { return 2 } -func (b *badtssMessage) Content() common.MessageContent { return nil } -func (b *badtssMessage) GetFrom() *common.PartyID { panic("unimplemented") } -func (b *badtssMessage) GetTo() *common.PartyID { panic("unimplemented") } -func (b *badtssMessage) IsBroadcast() bool { panic("unimplemented") } -func (b *badtssMessage) IsToOldAndNewCommittees() bool { panic("unimplemented") } -func (b *badtssMessage) IsToOldCommittee() bool { panic("unimplemented") } -func (b *badtssMessage) String() string { panic("unimplemented") } -func (b *badtssMessage) Type() string { panic("unimplemented") } -func (b *badtssMessage) WireMsg() *common.MessageWrapper { - return &common.MessageWrapper{ - TrackingID: nil, - } -} -func (b *badtssMessage) WireBytes() ([]byte, *common.MessageRouting, error) { - return nil, nil, errors.New("bad message") -} -func (b *badtssMessage) GetProtocol() common.ProtocolType { - return common.ProtocolFROSTSign -} - -func TestRouteCheck(t *testing.T) { - // this test is a bit of a hack. - // To ensure we don't panic on bad inputs. - a := assert.New(t) - engines := load5GuardiansSetupForBroadcastChecks(a) - e1 := engines[0] - - supctx := testutils.MakeSupervisorContext(context.Background()) - ctx, cancel := context.WithTimeout(supctx, time.Second*5) - defer cancel() - - e1.Start(ctx) - e1.fpCommChans.OutChannel <- &badtssMessage{} - e1.fpCommChans.ErrChannel <- common.NewTrackableError(errors.New("test"), "test", -1, nil, &common.TrackingID{}) - e1.fpCommChans.ErrChannel <- nil - - time.Sleep(time.Millisecond * 200) -} - -func TestDefaultSameLeader(t *testing.T) { - a := assert.New(t) - - engines := load5GuardiansSetupForBroadcastChecks(a) - - leader := engines[0].LeaderIdentity - a.NotNil(leader) - - for _, e := range engines { - a.Equal(e.LeaderIdentity, leader) - - if bytes.Equal(e.Self.KeyPEM, leader) { - a.True(e.isleader) - } else { - a.False(e.isleader) - } - } -} - -func TestNoFaultsFlow(t *testing.T) { - // checking metrics first since this is a bit flakey. - t.Run("with correct metrics", func(t *testing.T) { - sigProducedCntr.Reset() - a := assert.New(t) - engines, err := loadGuardians(5, "tss5") - a.NoError(err) - - dgst := party.Digest{1, 2, 3, 4, 5, 6, 7, 8, 9} - - supctx := testutils.MakeSupervisorContext(context.Background()) - ctx, cancel := context.WithTimeout(supctx, time.Second*20) - defer cancel() - - fmt.Println("starting engines.") - for _, engine := range engines { - a.NoError(engine.Start(ctx)) - } - - fmt.Println("msgHandler settup:") - dnchn := msgHandler(ctx, engines, 1) - - fmt.Println("engines started, requesting sigs") - - m := dto.Metric{} - - cID := vaa.ChainID(1) - // all engines are started, now we can begin the protocol. - for _, engine := range engines { - tmp := make([]byte, 32) - copy(tmp, dgst[:]) - engine.BeginAsyncThresholdSigningProtocol(tmp, cID, reportableConsistancyLevel) - } - - if ctxExpiredFirst(ctx, dnchn) { - a.FailNow("context expired") - } - - time.Sleep(time.Millisecond * 500) // ensuring all other engines have finished and not just one of them. - - sigProducedCntr.WithLabelValues(cID.String()).Write(&m) - a.Equal(engines[0].Threshold+1, int(m.Counter.GetValue())) - }) - - // Setting up all engines (not just 5), each with a different guardian storage. - // all will attempt to sign a single message, while outputing messages to each other, - // and reliably broadcasting them. - t.Run("Call multiple to sign the same digest", func(t *testing.T) { - a := assert.New(t) - engines, err := loadGuardians(5, "tss5") - a.NoError(err) - - dgst := party.Digest{1, 2, 3, 4, 5, 6, 7, 8, 9} - - supctx := testutils.MakeSupervisorContext(context.Background()) - ctx, cancel := context.WithTimeout(supctx, time.Second*10) - defer cancel() - - for _, engine := range engines { - a.NoError(engine.Start(ctx)) - } - - dnchn := msgHandler(ctx, engines, 1) - - cID := vaa.ChainID(1) - - // demand signing multiple times. - for range 10 { - for _, engine := range engines { - tmp := make([]byte, 32) - copy(tmp, dgst[:]) - engine.BeginAsyncThresholdSigningProtocol(tmp, cID, reportableConsistancyLevel) - } - fmt.Println() - } - - time.Sleep(time.Millisecond * 500) - if ctxExpiredFirst(ctx, dnchn) { - a.FailNow("context expired") - } - }) - - t.Run("19 signers", func(t *testing.T) { - t.SkipNow() // No tss19 engines available at the moment. - a := assert.New(t) - engines, err := loadGuardians(19, "tss19") - a.NoError(err) - - dgst := party.Digest{1, 2, 3, 4, 5, 6, 7, 8, 9} - - supctx := testutils.MakeSupervisorContext(context.Background()) - ctx, cancel := context.WithTimeout(supctx, time.Minute*1) - defer cancel() - - for _, engine := range engines { - a.NoError(engine.Start(ctx)) - } - - dnchn := msgHandler(ctx, engines, 1) - - cID := vaa.ChainID(1) - - for _, engine := range engines { - tmp := make([]byte, 32) - copy(tmp, dgst[:]) - engine.BeginAsyncThresholdSigningProtocol(tmp, cID, reportableConsistancyLevel) - } - - time.Sleep(time.Millisecond * 500) - if ctxExpiredFirst(ctx, dnchn) { - a.FailNow("context expired") - } - }) - - t.Run("with 5 sigs", func(t *testing.T) { - a := assert.New(t) - engines, err := loadGuardians(5, "tss5") - a.NoError(err) - - digests := make([]party.Digest, 5) - for i := 0; i < 5; i++ { - digests[i] = party.Digest{byte(i)} - } - - supctx := testutils.MakeSupervisorContext(context.Background()) - ctx, cancel := context.WithTimeout(supctx, time.Minute*1) - defer cancel() - - fmt.Println("starting engines.") - for _, engine := range engines { - a.NoError(engine.Start(ctx)) - } - - fmt.Println("msgHandler settup:") - dnchn := msgHandler(ctx, engines, len(digests)) - - fmt.Println("engines started, requesting sigs") - - // all engines are started, now we can begin the protocol. - for _, d := range digests { - - for _, engine := range engines { - tmp := make([]byte, 32) - copy(tmp, d[:]) - - engine.BeginAsyncThresholdSigningProtocol(tmp, 1, reportableConsistancyLevel) - } - } - - if ctxExpiredFirst(ctx, dnchn) { - a.FailNow("context expired") - } - }) - - t.Run("with nonreportable consistency level", func(t *testing.T) { - // test will check thatno FT is triggered when the consistency level is non-reportable. - // does so by starting signing for 2 out of 3 guardians and then wait for timeout. - a := assert.New(t) - engines, err := loadGuardians(5, "tss5") - a.NoError(err) - - dgst := party.Digest{1, 2, 3, 4, 5, 6, 7, 8, 9} - - supctx := testutils.MakeSupervisorContext(context.Background()) - ctx, cancel := context.WithTimeout(supctx, time.Second*10) - defer cancel() - - for _, engine := range engines { - a.NoError(engine.Start(ctx)) - } - - dnchn := msgHandler(ctx, engines, 1) - - cID := vaa.ChainID(1) - - e := getSigningGuardian(a, engines, party.SigningTask{ - Digest: dgst, - Faulties: []*common.PartyID{}, - AuxiliaryData: chainIDToBytes(cID), - }) - - for _, engine := range engines { - if e.Self.Pid.Equals(engine.Self.Pid) { - continue - } - - tmp := make([]byte, 32) - copy(tmp, dgst[:]) - engine.BeginAsyncThresholdSigningProtocol(tmp, cID, nonReportableConsistancyLevel) - } - - if !ctxExpiredFirst(ctx, dnchn) { - a.FailNow("signature shouldn't have been created") - } - }) - - t.Run("TSS sign after VAA seen by leader", func(t *testing.T) { - a := assert.New(t) - - nvaa, gs := genVaaAndGuardianSet(a) - - a.NoError(nvaa.Verify(gs.Keys)) - - gst := whcommon.NewGuardianSetState(nil) - gst.Set(gs) - - engines, err := loadGuardians(5, "tss5") - a.NoError(err) - - supctx := testutils.MakeSupervisorContext(context.Background()) - ctx, cancel := context.WithTimeout(supctx, time.Minute*30) - defer cancel() - - engines[0].isleader = true - for _, engine := range engines { - engine.LeaderIdentity = engines[0].Self.KeyPEM - engine.SetGuardianSetState(gst) - a.NoError(engine.Start(ctx)) - } - - dnchn := msgHandler(ctx, engines, 1) - - engines[0].WitnessNewVaa(nvaa) - if ctxExpiredFirst(ctx, dnchn) { - a.FailNow("context expired without signature") - } - }) - - t.Run("witness signature and use vaav1 mappings", func(t *testing.T) { - /* - This tests ensures that a specific committee signs the VAAv2 (everyone that signed the VAAv1). - */ - a := assert.New(t) - - nvaa, gs := genVaaAndGuardianSet(a) - - nvaa.Signatures = nvaa.Signatures[:4] - // ensuring valid vaa. - a.NoError(nvaa.Verify(gs.Keys)) - - gst := whcommon.NewGuardianSetState(nil) - gst.Set(gs) - - engines, err := loadGuardians(5, "tss5") - a.NoError(err) - - // set mappings (can be arbitrary in this unit test, since everyone is "online" and alive). - - for i := range engines { - engineIdentities := engines[i].GuardianStorage.Identities - for j := range engineIdentities { - id := engineIdentities[j] - - tmp := ethcommon.Address{} - copy(tmp[:], gs.Keys[j][:]) - - id.VAAv1PubKey = &tmp - engines[i].GuardianStorage.IdentitiesKeep.vaav1PubToIdentity[tmp] = int(id.CommunicationIndex) - } - } - - e := engines[0] // e IS LEADER. - e.isleader = true - - // Get who signed the VAAv1: - committeeHostnames := make(map[string]bool) // partyIDs - for _, s := range nvaa.Signatures { - id, err := e.GuardianStorage.fetchIdentityFromVaav1Pubkey(gs.Keys[s.Index]) - a.NoError(err) - - committeeHostnames[id.Hostname] = true - } - - supctx := testutils.MakeSupervisorContext(context.Background()) - ctx, cancel := context.WithTimeout(supctx, time.Minute*30) - defer cancel() - - for _, engine := range engines { - engine.LeaderIdentity = e.Self.KeyPEM - engine.SetGuardianSetState(gst) - a.NoError(engine.Start(ctx)) - } - - dnchn := msgHandler(ctx, engines, 1) - - e.WitnessNewVaa(nvaa) - - allMessages := <-dnchn - - // TEST: Check that every TSS-Content message was received from a committee member. - for _, m := range allMessages { - echo := m.toBroadcastMsg() - if echo == nil || echo.Message == nil || echo.Message.Content == nil { - continue - } - _, ok := echo.Message.Content.(*tsscommv1.SignedMessage_TssContent) - if !ok { - continue - } - - id, err := e.GuardianStorage.fetchIdentityFromIndex(SenderIndex(echo.Message.Sender)) - a.NoError(err) - - a.True(committeeHostnames[id.Hostname], "message from non-committee member: %s", id.Hostname) - } - }) -} - -// Creates a vaa with 2t+1 sigantures (not n-out-of-n signatures). -func genVaaAndGuardianSet(a *assert.Assertions) (*vaa.VAA, *whcommon.GuardianSet) { - gss := whcommon.NewGuardianSetState(nil) - _ = gss - - nvaa := &vaa.VAA{ - Version: vaa.VaaVersion1, - GuardianSetIndex: 0, - Signatures: nil, - Timestamp: time.Now(), - Nonce: 12345, - EmitterChain: vaa.ChainIDPythNet, - EmitterAddress: vaa.Address{1, 2, 3, 4, 54, 56, 67}, - Payload: []byte("hello world"), - Sequence: 5578, - ConsistencyLevel: pythnetFinalizedConsistencyLevel, - } - - addrss := []ethcommon.Address{} - sigs := []*vaa.Signature{} - for i := range 5 { - guardianSigner, err := guardiansigner.GenerateSignerWithPrivatekeyUnsafe(nil) - a.NoError(err) - - dgst := nvaa.SigningDigest() - - tmp, err := guardianSigner.Sign(context.Background(), dgst[:]) - a.NoError(err) - - sig := &vaa.Signature{Index: uint8(i)} - copy(sig.Signature[:], tmp) - - sigs = append(sigs, sig) - - addrss = append(addrss, crypto.PubkeyToAddress(guardianSigner.PublicKey(context.Background()))) - } - - gs := whcommon.NewGuardianSet(addrss, 0) - - nvaa.Signatures = sigs - return nvaa, gs -} - -func ctxExpiredFirst[T any](ctx context.Context, ch chan T) bool { - select { - case <-ctx.Done(): - return true - case <-ch: - return false - } -} - -func TestFT(t *testing.T) { - // t.Skip("Skipping these test until we decide about anouncing mechanism.") - - t.Run("single server crashes", func(t *testing.T) { - t.Skip("TODO: handle server crashes") - }) - - t.Run("server crashes during signing multiple digests", func(t *testing.T) { t.Skip("TODO: handle server crashes") }) - - t.Run("cant sign after f faults", func(t *testing.T) { t.Skip("TODO: handle server crashes") }) - - t.Run("metric cleanup", func(t *testing.T) { - // run for a few signatures, and ensure the metrics are cleaned up. - a := assert.New(t) - engines, err := loadGuardians(5, "tss5") - a.NoError(err) - - n := 2 - chainId := vaa.ChainID(1) - digests := make([]party.SigningTask, n) - for i := 0; i < n; i++ { - digests[i] = party.SigningTask{ - Digest: [32]byte{byte(i + 1)}, - Faulties: nil, - AuxiliaryData: chainIDToBytes(chainId), - } - } - - supctx := testutils.MakeSupervisorContext(context.Background()) - ctx, cancel := context.WithTimeout(supctx, time.Minute*4) - defer cancel() - - fmt.Println("starting engines.") - for _, engine := range engines { - engine.Configurations.MaxSignerTTL = time.Second * 4 - a.NoError(engine.Start(ctx)) - } - - e := getSigningGuardian(a, engines, digests...) - a.NotNil(e) - - fmt.Println("msgHandler settup:") - dnchn := msgHandler(ctx, engines, len(digests)) - - fmt.Println("engines started, requesting sigs") - - for _, d := range digests { - d := d - - for _, engine := range engines { - engine.BeginAsyncThresholdSigningProtocol(d.Digest[:], chainId, reportableConsistancyLevel) - } - } - - timer := time.After(engines[0].maxSignerTTL() * 4) - - if ctxExpiredFirst(ctx, dnchn) { - a.FailNow("context expired") - } - - <-timer - - for _, e := range engines { - e.SignatureMetrics.Range(func(k, v interface{}) bool { - fmt.Println(k, v) - a.Fail("metrics not cleaned up") - return false - }) - } - }) - - t.Run("Two quorums only one guardian in conjunction", func(t *testing.T) { - t.Skip("TODO: Make one of the signers of VAAv1 send the VAAv1 (similar to leader mechanism), so the others will also sign.") - - // This test simulates an error we've seen while testing with real data: - // 3 servers manage to generate VAA but not VAAv2 (TSS signatuer). - // That is, 3 servers saw the same digest, but only one of them was part of the tss-committee. - // As a result, the VAA was generated, but the VAAv2 was not (since the others in the committee didn't f+1 messages that started signing). - a := assert.New(t) - - supctx := testutils.MakeSupervisorContext(context.Background()) - ctx, cancel := context.WithTimeout(supctx, time.Minute) - defer cancel() - - cID := vaa.ChainID(1) - tsk := party.SigningTask{ - Digest: party.Digest{1, 2, 3, 4, 5, 6, 7, 8, 9}, - Faulties: []*common.PartyID{}, - AuxiliaryData: chainIDToBytes(cID), - } - - engines, err := loadGuardians(5, "tss5") - a.NoError(err) - - fmt.Println("starting engines.") - for _, engine := range engines { - a.NoError(engine.Start(ctx)) - } - - signers := getSigningGuardians(a, engines, tsk) - a.Len(signers, 3) - - fmt.Println("msgHandler settup:") - dnchn := msgHandler(ctx, engines, 1) - - nonSigners := make([]*Engine, 0, 2) - for _, engine := range engines { - if !contains(signers, engine) { - nonSigners = append(nonSigners, engine) - } - } - - // starting 3 signers where two aren't in the committee and one is. - for _, engine := range append(nonSigners, signers[0]) { - tmp := make([]byte, 32) - copy(tmp, tsk.Digest[:]) - - engine.BeginAsyncThresholdSigningProtocol(tmp, cID, reportableConsistancyLevel) - } - - if ctxExpiredFirst(ctx, dnchn) { - a.FailNow("context expired") - } - }) -} - -func TestMessagesWithBadRounds(t *testing.T) { - a := assert.New(t) - gs := load5GuardiansSetupForBroadcastChecks(a) - e1, e2 := gs[0], gs[1] - from := e1.Self - to := e2.Self - - t.Run("Unicast", func(t *testing.T) { - msgDigest := party.Digest{1} - for _, rnd := range broadcastRounds { - parsed := generateFakeMessageWithRandomContent(from.Pid, to.Pid, rnd, msgDigest) - bts, _, err := parsed.WireBytes() - a.NoError(err) - - m := &IncomingMessage{ - Source: from, - Content: &tsscommv1.PropagatedMessage{Message: &tsscommv1.PropagatedMessage_Unicast{ - Unicast: &tsscommv1.Unicast{ - Content: &tsscommv1.Unicast_Tss{ - Tss: &tsscommv1.TssContent{Payload: bts}, - }, - }, - }}, - } - - err = e2.handleUnicast(m) - a.ErrorContains(err, "received broadcast type message in unicast") - } - }) - - t.Run("Echo", func(t *testing.T) { - t.Skip("TODO: Right now there are no 'bad' rounds for echoes (since we've switched to frost), ecdsa might have those. so we might need to include a mechanism to review protocol type in each message.") - - msgDigest := party.Digest{2} - for _, rnd := range unicastRounds { - parsed := generateFakeMessageWithRandomContent(from.Pid, to.Pid, rnd, msgDigest) - bts, _, err := parsed.WireBytes() - a.NoError(err) - - m := &IncomingMessage{ - Source: from, - Content: &tsscommv1.PropagatedMessage{Message: &tsscommv1.PropagatedMessage_Echo{ - Echo: &tsscommv1.Echo{ - Message: &tsscommv1.SignedMessage{ - Content: &tsscommv1.SignedMessage_TssContent{ - TssContent: &tsscommv1.TssContent{Payload: bts}, - }, - Sender: uint32(from.CommunicationIndex), - Signature: nil, - }, - }, - }}, - } - a.NoError(e1.sign(uuid{}, m.Content.GetEcho().Message)) - - err = e2.handleBroadcast(m) - // a.ErrorIs(err, errBadRoundsInBroadcast) - } - }) -} - -func generateFakeParsedMessageWithRandomContent(from, to *common.PartyID, rnd signingRound, digest party.Digest) broadcastMessage { - fake := generateFakeMessageWithRandomContent(from, to, rnd, digest) - return &deliverableMessage{&parsedTssContent{fake, ""}} -} - -// if to == nil it's a broadcast message. -func generateFakeMessageWithRandomContent(from, to *common.PartyID, rnd signingRound, digest party.Digest) common.ParsedMessage { - partiesState := make([]byte, maxParties) - for i := 0; i < maxParties; i++ { - partiesState[i] = 255 - } - - trackingId := &common.TrackingID{ - Digest: digest[:], - PartiesState: partiesState, - AuxiliaryData: []byte{}, - } - - rndmBigNumber := &big.Int{} - buf := make([]byte, 16) - rand.Read(buf) - rndmBigNumber.SetBytes(buf) - - var ( - meta = common.MessageRouting{From: from, To: nil} // broadcast message since `To` is nil. - content common.MessageContent - ) - - switch rnd { - case round2Message: - content = &sign.Broadcast2{ - Di: rndmBigNumber.Bytes(), - Ei: rndmBigNumber.Bytes(), - } - case round3Message: - if to == nil { - panic("not a broadcast message") - } - meta = common.MessageRouting{From: from, To: to} // unicast message since `To` isn't nil. - - content = &sign.Broadcast3{ - Zi: rndmBigNumber.Bytes(), - } - default: - panic("unknown round") - } - - return common.NewMessage(meta, content, common.NewMessageWrapper(meta, content, trackingId)) -} - -func loadMockGuardianStorage(gstorageIndex int, from string) *GuardianStorage { - path, err := testutils.GetMockGuardianTssStorage(gstorageIndex, from) - if err != nil { - panic(err) - } - - st, err := NewGuardianStorageFromFile(path) - if err != nil { - panic(err) - } - return st -} - -func loadGuardians(numParticipants int, from string) ([]*Engine, error) { - engines := make([]*Engine, numParticipants) - - for i := 0; i < numParticipants; i++ { - e, err := NewReliableTSS(loadMockGuardianStorage(i, from)) - if err != nil { - return nil, err - } - en, ok := e.(*Engine) - if !ok { - return nil, errors.New("not an engine") - } - engines[i] = en - } - - return engines, nil -} - -type msgg struct { - Sender *Identity - Sendable -} - -// its channel returns an array of ALL messages it received. -func msgHandler(ctx context.Context, engines []*Engine, numDiffSigsExpected int) chan []*IncomingMessage { - messageBucket := make([]*IncomingMessage, 0, 10000) - signalDone := make(chan []*IncomingMessage, 1) - once := sync.Once{} - - nmsigs := map[string]struct{}{} - lck := sync.Mutex{} - - go func() { - wg := sync.WaitGroup{} - wg.Add(len(engines) * 2) - - chns := make(map[string]chan msgg, len(engines)) - for _, en := range engines { - chns[en.Self.Pid.GetID()] = make(chan msgg, 10000) - } - - for _, e := range engines { - engine := e - - // need a separate goroutine for handling engine output and engine input. - // simulating network stream incoming and network stream outgoing. - - // incoming - go func() { - defer wg.Done() - for { - select { - case <-ctx.Done(): - return - - case msg := <-chns[engine.Self.Pid.GetID()]: - in := &IncomingMessage{ - Source: msg.Sender, - Content: msg.Sendable.GetNetworkMessage(), - } - - engine.HandleIncomingTssMessage(in) - - lck.Lock() // used across multiple goroutines: must lock. - messageBucket = append(messageBucket, in) - lck.Unlock() - } - } - }() - - // Listener, responsible to receive output of engine, and direct it to the other engines. - go func() { - defer wg.Done() - for { - select { - case <-ctx.Done(): - return - - case m := <-engine.ProducedOutputMessages(): - if m.IsBroadcast() { - broadcast(chns, engine, m) - continue - } - unicast(m, chns, engine) - case sig := <-engine.ProducedSignature(): - sg, err := frost.Secp256k1SignatureTranslate(sig) - if err != nil { - panic("failed to translate signature:" + err.Error()) - } - - pk, err := engine.GetPublicKey() - if err != nil { - panic("failed to get public key:" + err.Error()) - } - - if err := sg.Verify(pk, sig.M); err != nil { - panic("failed to verify signature:" + err.Error()) - } - - lck.Lock() - nmsigs[sig.TrackingId.ToString()] = struct{}{} - ln := len(nmsigs) - lck.Unlock() - - fmt.Println("received signature", ln) - if ln < numDiffSigsExpected { - continue - } - - fmt.Printf("/////////\nreceived all signatures (%v)\n/////////\n", numDiffSigsExpected) - once.Do(func() { - lck.Lock() - messageSlice := messageBucket - lck.Unlock() - - signalDone <- messageSlice - close(signalDone) - }) - } - } - }() - } - - wg.Wait() - }() - - return signalDone -} - -func unicast(m Sendable, chns map[string]chan msgg, engine *Engine) { - pids := m.GetDestinations() - for _, id := range pids { - feedChn := chns[id.Pid.GetID()] - feedChn <- msgg{ - Sender: engine.Self, - Sendable: m.cloneSelf(), - } - } -} - -func broadcast(chns map[string]chan msgg, engine *Engine, m Sendable) { - for _, feedChn := range chns { - feedChn <- msgg{ - Sender: engine.Self, - Sendable: m.cloneSelf(), - } - } -} - -// strictly for the tests. -func (c *activeSigCounter) digestToGuardiansLen() int { - c.mtx.RLock() - defer c.mtx.RUnlock() - - return len(c.digestToGuardians) -} - -// Used to receive all messages for some engine, then feed them all at once, and collect the result. -// on error returns err. -// simulates echoes for each message too! -type echoFeed struct { - eng *Engine - peers []*Engine - messages []IncomingMessage -} - -func (b *echoFeed) addMessage(src *Engine, m Sendable) { - inc := IncomingMessage{ - Source: src.Self, - Content: m.GetNetworkMessage(), - } - - b.messages = append(b.messages, inc) -} - -func (b *echoFeed) feedWithEchoes() error { - defer func() { - b.messages = nil // clear messages after feeding. - }() - - if b.messages == nil { - return nil - } - - for _, msg := range b.messages { - if err := b.eng.handleIncomingTssMessage(&msg); err != nil { - return err - } - - echo := b.genEcho(msg) - // for each message: create fictional echo, making the guardian think that all other guardians have echoed it. - for _, v := range b.peers { - Incoming := &IncomingMessage{ - Source: v.Self, - Content: echo.GetNetworkMessage(), - } - - if err := b.eng.handleIncomingTssMessage(Incoming); err != nil { - return err - } - } - } - - return nil -} - -func (b *echoFeed) genEcho(msg IncomingMessage) *Echo { - // src := msg.GetSource() - // sig := msg.Content.GetEcho().Message.Signature - - parsed, err := b.eng.parseBroadcast(&msg) - if err != nil { - panic(err) // shouldn't happen in the test. - } - - return b.eng.makeEcho(&msg, parsed) -} - -func TestSigCounter(t *testing.T) { - a := assert.New(t) - - supctx := testutils.MakeSupervisorContext(context.Background()) - - t.Run("MaxCountBlockAdditionalUpdates", func(t *testing.T) { - ctx, cancel := context.WithTimeout(supctx, time.Minute*1) - defer cancel() - - // t.Skip("TODO: implement this test, fails since we've moved to broadcast only messages!") - - cID := vaa.ChainID(0) - tsks := []party.SigningTask{ - party.SigningTask{Digest: party.Digest{1}, Faulties: []*common.PartyID{}, AuxiliaryData: chainIDToBytes(cID)}, - party.SigningTask{Digest: party.Digest{2}, Faulties: []*common.PartyID{}, AuxiliaryData: chainIDToBytes(cID)}, - } - engines := load5GuardiansSetupForBroadcastChecks(a) - e1 := getSigningGuardian(a, engines, tsks...) - - e1.maxSimultaneousSignatures = 1 - feeder := &echoFeed{ - eng: e1, - peers: engines, - } - - signersTask0 := getSigningGuardians(a, engines, tsks[0]) - signersTask1 := getSigningGuardians(a, engines, tsks[1]) - - for taskNum, committee := range [][]*Engine{signersTask0, signersTask1} { - for _, e := range committee { - e.Start(ctx) - - msg := beginSigningAndGrabMessage(e, tsks[taskNum].Digest[:], cID) - feeder.addMessage(e, msg) - err := feeder.feedWithEchoes() - if err != nil { - a.ErrorContains(err, "maximum number of simultaneous") - - return - } - } - } - - t.FailNow() // expected feeding to fail due to maxSimultaneousSignatures. - }) - - t.Run("ErrorReduceCount", func(t *testing.T) { - ctx, cancel := context.WithTimeout(supctx, time.Minute*1) - defer cancel() - - // Tests might fail due to change of the GuardianStorage files - cID := vaa.ChainID(0) - tsks := []party.SigningTask{ - party.SigningTask{Digest: party.Digest{1}, Faulties: []*common.PartyID{}, AuxiliaryData: chainIDToBytes(cID)}, - } - engines := load5GuardiansSetupForBroadcastChecks(a) - e1 := getSigningGuardian(a, engines, tsks...) - e1.maxSimultaneousSignatures = 1 - - e1.Start(ctx) - - msg := beginSigningAndGrabMessage(e1, tsks[0].Digest[:], cID) - - feeder := &echoFeed{ - eng: e1, - peers: engines, - } - - feeder.addMessage(e1, msg) - a.NoError(feeder.feedWithEchoes()) - - incoming := &IncomingMessage{ - Source: e1.Self, - Content: msg.GetNetworkMessage(), - } - - parsed, err := e1.parseTssContent(incoming.toBroadcastMsg().Message.GetTssContent(), incoming.GetSource()) - a.NoError(err) - - tid := parsed.getTrackingID() - // test: - a.Equal(e1.sigCounter.digestToGuardiansLen(), 1) - select { - case e1.fpCommChans.ErrChannel <- common.NewTrackableError(fmt.Errorf("dummyerr"), "de", -1, e1.Self.Pid, tid): - case <-time.After(time.Second * 1): - t.FailNow() - return - } - time.Sleep(time.Millisecond * 500) - - a.Equal(e1.sigCounter.digestToGuardiansLen(), 0) - }) - - t.Run("sigDoneReduceCount", func(t *testing.T) { - ctx, cancel := context.WithTimeout(supctx, time.Minute*1) - defer cancel() - - // Tests might fail due to change of the GuardianStorage files - cID := vaa.ChainID(0) - tsks := []party.SigningTask{ - party.SigningTask{Digest: party.Digest{1}, Faulties: []*common.PartyID{}, AuxiliaryData: chainIDToBytes(cID)}, - } - engines := load5GuardiansSetupForBroadcastChecks(a) - e1 := getSigningGuardian(a, engines, tsks...) - e1.maxSimultaneousSignatures = 1 - - e1.Start(ctx) - - msg := beginSigningAndGrabMessage(e1, tsks[0].Digest[:], cID) - - feeder := &echoFeed{ - eng: e1, - peers: engines, - } - - feeder.addMessage(e1, msg) - a.NoError(feeder.feedWithEchoes()) - - incoming := &IncomingMessage{ - Source: e1.Self, - Content: msg.GetNetworkMessage(), - } - - parsed, err := e1.parseTssContent(incoming.toBroadcastMsg().Message.GetTssContent(), incoming.GetSource()) - a.NoError(err) - - // test: - a.Equal(e1.sigCounter.digestToGuardiansLen(), 1) - e1.fpCommChans.SignatureOutputChannel <- &common.SignatureData{ - Signature: []byte{}, - SignatureRecovery: []byte{}, - R: []byte{}, - S: []byte{}, - M: []byte{}, - TrackingId: parsed.getTrackingID(), - } - <-e1.sigOutChan - time.Sleep(time.Second * 1) - a.Equal(e1.sigCounter.digestToGuardiansLen(), 0) - }) - - t.Run("CanHaveSimulSigners", func(t *testing.T) { - ctx, cancel := context.WithTimeout(supctx, time.Minute*1) - defer cancel() - - // t.Skip("TODO: implement this test, fails since we've moved to broadcast only messages!") - - cID := vaa.ChainID(0) - tsks := []party.SigningTask{ - party.SigningTask{Digest: party.Digest{1}, Faulties: []*common.PartyID{}, AuxiliaryData: chainIDToBytes(cID)}, - party.SigningTask{Digest: party.Digest{2}, Faulties: []*common.PartyID{}, AuxiliaryData: chainIDToBytes(cID)}, - } - engines := load5GuardiansSetupForBroadcastChecks(a) - e1 := getSigningGuardian(a, engines, tsks...) - - e1.maxSimultaneousSignatures = 2 - feeder := &echoFeed{ - eng: e1, - peers: engines, - } - - signersTask0 := getSigningGuardians(a, engines, tsks[0]) - signersTask1 := getSigningGuardians(a, engines, tsks[1]) - - for taskNum, committee := range [][]*Engine{signersTask0, signersTask1} { - for _, e := range committee { - e.Start(ctx) - - msg := beginSigningAndGrabMessage(e, tsks[taskNum].Digest[:], cID) - feeder.addMessage(e, msg) - err := feeder.feedWithEchoes() - a.NoError(err) - } - } - }) -} - -func getSigningGuardian(a *assert.Assertions, engines []*Engine, tsks ...party.SigningTask) *Engine { - return getSigningGuardians(a, engines, tsks...)[0] -} - -func getSigningGuardians(a *assert.Assertions, engines []*Engine, tsks ...party.SigningTask) []*Engine { - a.GreaterOrEqual(len(tsks), 1) // at least one - - guardians := make([]*Engine, 0, len(engines)) -mainloop: - for _, e := range engines { - - for _, tsk := range tsks { - info1, err := e.fp.GetSigningInfo(tsk) - a.NoError(err) - - if !info1.IsSigner { - continue mainloop - } - } - - guardians = append(guardians, e) - } - - return guardians -} - -func beginSigningAndGrabMessage(e1 *Engine, dgst []byte, cid vaa.ChainID) Sendable { - go e1.BeginAsyncThresholdSigningProtocol(dgst, cid, reportableConsistancyLevel) - - var msg Sendable - for { // cleaning the channel, and taking one of the messages. - select { - case tmp := <-e1.ProducedOutputMessages(): - msg = tmp - parsed, err := e1.parseBroadcast(&IncomingMessage{ - Source: e1.Self, - Content: tmp.GetNetworkMessage(), - }) - if err != nil { - panic("failed to parse broadcast message: " + err.Error()) - } - - if _, ok := parsed.(*deliverableMessage); !ok { - continue - } - - return msg - - case <-time.After(time.Second * 5): - // This means the signer wasn't one of the signing committees. (did the Guardian storage change?) - // if it did, just make sure this engine is expected to sign, else use the right engine in the test. - panic("timeout!") - } - } -} - -func contains(lst []*Engine, e *Engine) bool { - for _, l := range lst { - if l.Self.Pid.Equals(e.Self.Pid) { - return true - } - } - - return false -} - -func TestTrackingIDSizeIsOkay(t *testing.T) { - dgst := party.Digest{1, 2, 3, 4, 5, 6, 7, 8, 9} - tid := common.TrackingID{ - Digest: dgst[:], - PartiesState: make([]byte, (maxParties+7)/8), - AuxiliaryData: chainIDToBytes(vaa.ChainID(5)), - } - - tidstr := tid.ToString() - assert.Equal(t, trackingIDHexStrSize, len(tidstr)) -} - -func TestDKG(t *testing.T) { - a := assert.New(t) - - engines, err := loadGuardians(5, "tss5") - a.NoError(err) - - for _, e := range engines { // Checks things work when no frost config is set. - e.GuardianStorage.frostconf = nil - } - - supctx := testutils.MakeSupervisorContext(context.Background()) - ctx, cancel := context.WithTimeout(supctx, time.Minute*1) - defer cancel() - - for _, engine := range engines { - a.NoError(engine.Start(ctx)) - } - - _ = msgHandler(ctx, engines, 1) - - promises := make([]chan *party.TSSSecrets, len(engines)) - for _, engine := range engines { - chn, err := engine.StartDKG(party.DkgTask{ - Threshold: 3, - Seed: party.Digest{}, - }) - a.NoError(err) - - promises[engine.Self.CommunicationIndex] = chn - } - fmt.Println("dkg started, waiting for configs...") - - res := make([]*frost.Config, len(engines)) - for _, v := range engines { - select { - case <-ctx.Done(): - a.FailNow("context expired before DKG finished") - case cfg := <-promises[v.Self.CommunicationIndex]: - fmt.Println("received config for", v.Self.CommunicationIndex) - res[v.Self.CommunicationIndex] = cfg.Config - } - } - - fmt.Println("DKG finished, configs:") - - fmt.Println("") -} - -func TestHandleFPWarning_IntegrationStyle_UsesEngineBootstrap(t *testing.T) { - // Create engines & give them their normal logger via your loader. - engines, err := loadGuardians(5, "tss5") - if err != nil { - t.Fatalf("loadGuardians failed: %v", err) - } - - supctx := testutils.MakeSupervisorContext(context.Background()) - ctx, cancel := context.WithTimeout(supctx, time.Minute) - defer cancel() - - for _, e := range engines { - if err := e.Start(ctx); err != nil { - t.Fatalf("engine.Start failed: %v", err) - } - } - - // swap in an observer logger so we can assert on logs. - e := engines[0] - core, logs := observer.New(zapcore.WarnLevel) - e.logger = zap.New(core) // replace the engine's logger with observed one - - // 1) nil warning => no logs - e.handleFPWarning(nil) - if logs.Len() != 0 { - t.Fatalf("engine expected 0 logs for nil warning, got %d", logs.Len()) - } - - // 2) empty message => no logs - e.handleFPWarning(&party.Warning{Message: ""}) - if logs.Len() != 0 { - t.Fatalf("engine expected 0 logs for empty message, got %d", logs.Len()) - } - - // 3) message only => single warn, no structured fields - msgOnly := &party.Warning{Message: "just a note"} - e.handleFPWarning(msgOnly) - if logs.Len() != 1 { - t.Fatalf("engine expected 1 log after message-only warn, got %d", logs.Len()) - } - entry := logs.All()[0] - wantMsg := fmt.Sprintf("tss-lib.FullParty: %s", msgOnly.Message) - if entry.Message != wantMsg { - t.Fatalf("engine got log message %q, want %q", entry.Message, wantMsg) - } - if len(entry.Context) != 0 { - t.Fatalf("engine expected no fields for message-only warn, got %v", entry.Context) - } - - // 4) protocol + round + nil culprit => fields for protocol, round; no possibleCulprit - core, logs = observer.New(zapcore.WarnLevel) - e.logger = zap.New(core) - - dgst := sha512.Sum512_256([]byte("123")) - tid := &common.TrackingID{ - Digest: dgst[:], - PartiesState: nil, - AuxiliaryData: []byte{dgst[0]}, - } - w := &party.Warning{ - Message: "something happened", - Protocol: common.ProtocolType("frost"), - SessionRound: round.Number(7), - TrackingID: tid, - // PossibleCulprit is nil on purpose → fetch should fail/skip, so field omitted - } - e.handleFPWarning(w) - - if logs.Len() != 1 { - t.Fatalf("engine expected 1 log, got %d", logs.Len()) - } - entry = logs.All()[0] - if entry.Level != zapcore.WarnLevel { - t.Fatalf("engine expected warn level, got %v", entry.Level) - } - got := observerToMap(entry) - - if got["protocol"] != "frost" { - t.Fatalf("engine expected protocol field mismatch: got %q, want %q", got["protocol"], "frost") - } - if got["round"] != "7" { - t.Fatalf("engine expected round field mismatch: got %q, want %q", got["round"], "7") - } - if _, ok := got["possibleCulprit"]; ok { - t.Fatalf("engine expected possibleCulprit to be absent when lookup fails or culprit is nil") - } - if got["trackingId"] != w.TrackingID.ToString() { - t.Fatalf("engine expected trackingId mismatch: got %q, want %q", got["trackingId"], "trk-123") - } - - // last but not least, adding a valid culprit: - core, logs = observer.New(zapcore.WarnLevel) - e.logger = zap.New(core) - w.PossibleCulprit = e.Self.Pid - e.handleFPWarning(w) - if logs.Len() != 1 { - t.Fatalf("engine expected 1 log, got %d", logs.Len()) - } - got = observerToMap(logs.All()[0]) - if got["possibleCulprit"] != e.Self.Hostname { - t.Fatalf("engine expected possibleCulprit field mismatch: got %q, want %q", got["possibleCulprit"], e.Self.Hostname) - } - // if got["trackingId"] != "trk-123" { - // t.Fatalf("engine %d: trackingId mismatch: got %q, want %q", i, got["trackingId"], "trk-123") - // } -} - -func observerToMap(entry observer.LoggedEntry) map[string]string { - got := map[string]string{} - for _, f := range entry.Context { - switch f.Type { - case zapcore.StringType: - got[f.Key] = f.String - case zapcore.Int64Type: // zap.Int encodes as int64 - got[f.Key] = fmt.Sprintf("%d", f.Integer) - } - } - return got -} - -func TestHandleIncomingTssMessage_NilHashEcho(t *testing.T) { - a := assert.New(t) - engines := load5GuardiansSetupForBroadcastChecks(a) - e1 := engines[0] - receiver := engines[4] - - // Start the receiver engine - supctx := testutils.MakeSupervisorContext(context.Background()) - ctx, cancel := context.WithCancel(supctx) - defer cancel() - a.NoError(receiver.Start(ctx)) - - // Create a valid SignedMessage, but with Content as a HashEcho with nil value - signedMsg := &tsscommv1.SignedMessage{ - Sender: uint32(e1.Self.CommunicationIndex), - Signature: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, // dummy signature - Content: &tsscommv1.SignedMessage_HashEcho{HashEcho: nil}, // <-- nil HashEcho - } - - // Wrap in Echo and PropagatedMessage - echo := &tsscommv1.Echo{ - Message: signedMsg, - } - incoming := &IncomingMessage{ - Source: e1.Self, - Content: &tsscommv1.PropagatedMessage{ - Message: &tsscommv1.PropagatedMessage_Echo{ - Echo: echo, - }, - }, - } - - // Results in a panic, because the HashEcho is nil. - _ = receiver.handleIncomingTssMessage(incoming) -} diff --git a/node/pkg/tss/internal/certs.go b/node/pkg/tss/internal/certs.go deleted file mode 100644 index 6ba12bd244..0000000000 --- a/node/pkg/tss/internal/certs.go +++ /dev/null @@ -1,127 +0,0 @@ -package internal - -import ( - "crypto/ecdsa" - "crypto/rand" - "crypto/x509" - "encoding/pem" - "errors" - "fmt" - "log" -) - -// CreateCert invokes x509.CreateCertificate and returns it in the x509.Certificate format -func CreateCert(template, parent *x509.Certificate, pub *ecdsa.PublicKey, parentPriv *ecdsa.PrivateKey) ( - cert *x509.Certificate, certPEM []byte, err error) { - - certDER, err := x509.CreateCertificate(rand.Reader, template, parent, pub, parentPriv) - if err != nil { - return - } - // parse the resulting certificate so we can use it again - cert, err = x509.ParseCertificate(certDER) - if err != nil { - return - } - // PEM encode the certificate (this is a standard TLS encoding) - b := pem.Block{Type: "CERTIFICATE", Bytes: certDER} - certPEM = pem.EncodeToMemory(&b) - return -} - -// NewTLSCredentials creates a self signed certificate -func NewTLSCredentials(secretKey *ecdsa.PrivateKey, template *x509.Certificate) *x509.Certificate { - // this cert will be the CA that we will use to sign the server cert - template.IsCA = true - // describe what the certificate will be used for - template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature - template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth} - - rootCert, _, err := CreateCert(template, template, &secretKey.PublicKey, secretKey) - if err != nil { - log.Fatalf("error creating cert: %v", err) - } - - return rootCert -} - -func PrivateKeyToPem(pkey *ecdsa.PrivateKey) []byte { - keyBytes, err := x509.MarshalPKCS8PrivateKey(pkey) - if err != nil { - log.Fatal("shouldn't happen") - } - - return pem.EncodeToMemory(&pem.Block{ - Type: "PRIVATE KEY", Bytes: keyBytes, - }) -} - -func PublicKeyToPem(pkey *ecdsa.PublicKey) ([]byte, error) { - keyBytes, err := x509.MarshalPKIXPublicKey(pkey) - if err != nil { - return nil, fmt.Errorf("failed to marshal public key: %w", err) - } - - return pem.EncodeToMemory(&pem.Block{ - Type: "PUBLIC KEY", Bytes: keyBytes, - }), nil -} - -func PemToPublicKey(pemBytes []byte) (*ecdsa.PublicKey, error) { - block, _ := pem.Decode(pemBytes) - if block == nil { - return nil, errors.New("failed to decode PEM block containing the public key") - } - if block.Type != "PUBLIC KEY" { - return nil, errors.New("PEM block is not a public key") - } - - k, err := x509.ParsePKIXPublicKey(block.Bytes) - if err != nil { - return nil, fmt.Errorf("failed to parse public key: %w", err) - } - - publicKey, ok := k.(*ecdsa.PublicKey) - if !ok { - return nil, errors.New("parsed public key is not an ECDSA key") - } - - return publicKey, nil -} -func PemToPrivateKey(pemBytes []byte) (*ecdsa.PrivateKey, error) { - block, _ := pem.Decode(pemBytes) - if block == nil { - return nil, errors.New("failed to decode PEM block containing the private key") - } - if block.Type != "PRIVATE KEY" { - return nil, errors.New("PEM block is not a private key") - } - - k, err := x509.ParsePKCS8PrivateKey(block.Bytes) - if err != nil { - return nil, fmt.Errorf("failed to parse private key: %v", err) - } - - privateKey, ok := k.(*ecdsa.PrivateKey) - if !ok { - return nil, errors.New("parsed private key is not an ECDSA key") - } - - return privateKey, nil -} - -func CertToPem(cert *x509.Certificate) []byte { - return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) -} - -func PemToCert(pemBytes []byte) (*x509.Certificate, error) { - block, _ := pem.Decode(pemBytes) - if block == nil { - return nil, errors.New("failed to decode PEM block containing the certificate") - } - if block.Type != "CERTIFICATE" { - return nil, errors.New("PEM block is not a certificate") - } - - return x509.ParseCertificate(block.Bytes) -} diff --git a/node/pkg/tss/internal/cmd/README.md b/node/pkg/tss/internal/cmd/README.md deleted file mode 100644 index e1a0529d9f..0000000000 --- a/node/pkg/tss/internal/cmd/README.md +++ /dev/null @@ -1,59 +0,0 @@ -Please read the entire document before running the protocol. - -# Running Distributed Key Generation - -The following DKG binary runs a distributed-key-generation protocol to -generate secrets for guardians to use by the threshold signing scheme (TSS). - -``` -go build -o=./server ./dkg -./server -cnfg= -``` - -As implied in the command above, the binary expects a config file (similar to the config file in `examples/dkg_config_example.json`). -The config file contains a few key fields: - -``` -"NumParticipants": int, -"WantedThreshold": int, -"Self" : Identifier -"SelfSecret" : PEM encoding of a secret key (as a byte array). -"Peers" : array of `Identifier` -``` - - -Where `NumParticipants` is the number of guardians in the system, -`WantedThreshold` is the wanted threshold (For instance, `NumParticipants=19` and `WantedThreshold=13`). -`Self` Descries to the binary who it is when running. -`Peers` Describe all possible participants (including `Self`). - - -#### *Notice:* The DKG.config should contain secret keys. *DO NOT share it with anyone*. - -The DKG protocol is used to generate secrets to TSS, -and it assumes a public key infrastructure (PKI). -PKI means that each participant of the DKG protocol know the public key of all of its peers. -These public keys are x509 certificates (and stored inside `Peers[i].TlsX509`), -and are used later by the TSS to establish TLS channels between the participants. -As a result, the x509 certificate provided by you should be self-signed root-level certificates. -In addition, you should provide put the secret key you've used to sign your certificate in the field `SelfSecret`. - -When creating the X509 certificates, be aware that the DNS name you set -in the certificate will be used as the hostname of -servers participating in the TSS protocol. -As a result, please refrain from using hostnames that are -unreachable. - -## Upon Completion -Once you run the protocol, it will generate for each guardian a `secrets.json` fil upon lkg completion. -This `secrets.json` file contains everything the Guardian process needs to run VAAv2. -Keep this file secret. - -You can see an example of the output in `secrets_example.json`. - -# Examples - -We’ve included several example configuration files in the examples folder: -- `secrets_example.json`: An example output file generated by the protocol. -- `dkg_config_example.json`: A sample configuration file expected by the DKG program at runtime. -- `multi_process_config_example/`: A directory containing five configuration files, each intended for a separate process on the same machine. To start the DKG protocol, launch five instances of the program,each using a different config file from this folder (Notice that only one output is expected in this case, since each program overwrites with its own output the same file). diff --git a/node/pkg/tss/internal/cmd/common.go b/node/pkg/tss/internal/cmd/common.go deleted file mode 100644 index a5d75c45c2..0000000000 --- a/node/pkg/tss/internal/cmd/common.go +++ /dev/null @@ -1,117 +0,0 @@ -package cmd - -import ( - "crypto/ecdsa" - "crypto/sha512" - "encoding/hex" - "fmt" - - engine "github.com/certusone/wormhole/node/pkg/tss" - "github.com/certusone/wormhole/node/pkg/tss/internal" - common "github.com/xlabs/tss-common" -) - -type Identifier struct { - Hostname string - TlsX509 engine.PEM // PEM Encoded (see certs.go). Note, you must have the private key of this cert later. - Port int // if one needs different ports, tell it here. -} - -type SetupConfigs struct { - NumParticipants int - WantedThreshold int // should be non inclusive. That is, if you have n=19,f=6, then threshold=12 (13 guardians needed to sign). - - Self Identifier - SelfSecret engine.PEM // PEM Encoded (see certs.go). Note, you must have the private key of this cert later. - StorageLocation string // The folder where secrets.json file will be saved. - - Peers []Identifier - Secrets []engine.PEM - SaveLocation []string -} - -func (cnfg *SetupConfigs) Validate() error { - if cnfg.NumParticipants < 1 { - return fmt.Errorf("number of participants should be at least 1") - } - - if len(cnfg.Peers) != cnfg.NumParticipants { - return fmt.Errorf("number of guardian identifiers should be equal to number of participants") - } - - if cnfg.WantedThreshold >= cnfg.NumParticipants-1 { - return fmt.Errorf("threshold should be less than number of participants") - } - - return nil -} - -func (cnfg *SetupConfigs) IntoMaps() (keyToEngineIdentity map[string]*engine.Identity, keyToID map[string]*Identifier, err error) { - keyToEngineIdentity = make(map[string]*engine.Identity, cnfg.NumParticipants) - - keyToID = map[string]*Identifier{} - for i, peer := range cnfg.Peers { - crt, err := internal.PemToCert(peer.TlsX509) - if err != nil { - return nil, nil, err - } - - if len(crt.DNSNames) == 0 { - return nil, nil, fmt.Errorf("expected DNS names in the cert") - } - - pk, ok := crt.PublicKey.(*ecdsa.PublicKey) // TODO - if !ok { - return nil, nil, fmt.Errorf("expected ecdsa public key in certs") - } - - bts, err := internal.PublicKeyToPem(pk) - if err != nil { - return nil, nil, err - } - - pidbytes := sha512.Sum512_256(bts) - // convert the byte array to a string representation for use as the party ID - pid := hex.EncodeToString(pidbytes[:]) - - keyToEngineIdentity[string(bts)] = &engine.Identity{ - Pid: &common.PartyID{ - ID: string(pid), - }, - KeyPEM: bts, - CertPem: peer.TlsX509, - Cert: nil, // not stored, since we have the certPem. - CommunicationIndex: 0, // unknown until all identites are sorted according to their Party ids. - Hostname: peer.Hostname, - Port: peer.Port, - Key: nil, // Filled by the guardian storage on boot. - VAAv1PubKey: nil, // not used in dkg, so nil. - } - - keyToID[string(bts)] = &cnfg.Peers[i] - } - - return keyToEngineIdentity, keyToID, nil -} - -// sorts the identities based on the partyID key (as tss-lib expects the parties to be sorted). -// then sets the communication index according to the sorted order. -func SortIdentities(unsortedIdentities map[string]*engine.Identity) []*engine.Identity { - pids := make([]*common.PartyID, 0, len(unsortedIdentities)) - pidsToCertKey := make(map[string]string, len(unsortedIdentities)) - for _, p := range unsortedIdentities { - pids = append(pids, p.Pid) - pidsToCertKey[string(p.Pid.GetID())] = string(p.KeyPEM) - } - - sortedPids := common.SortPartyIDs(pids) - - sortedIDS := make([]*engine.Identity, len(sortedPids)) - for i, pid := range sortedPids { - key := pidsToCertKey[string(pid.GetID())] - sortedIDS[i] = unsortedIdentities[key] - sortedIDS[i].CommunicationIndex = engine.SenderIndex(i) - } - - return sortedIDS -} diff --git a/node/pkg/tss/internal/cmd/dkg/server.go b/node/pkg/tss/internal/cmd/dkg/server.go deleted file mode 100644 index c4c47a1b62..0000000000 --- a/node/pkg/tss/internal/cmd/dkg/server.go +++ /dev/null @@ -1,232 +0,0 @@ -package main - -import ( - "bytes" - "context" - "crypto/sha256" - "encoding/gob" - "encoding/hex" - "encoding/json" - "flag" - "os" - "path" - "strconv" - "time" - - "github.com/certusone/wormhole/node/pkg/internal/testutils" - "github.com/certusone/wormhole/node/pkg/supervisor" - engine "github.com/certusone/wormhole/node/pkg/tss" - "github.com/certusone/wormhole/node/pkg/tss/comm" - "github.com/certusone/wormhole/node/pkg/tss/internal/cmd" - "github.com/xlabs/multi-party-sig/protocols/frost/sign" - "github.com/xlabs/tss-lib/v2/party" - "go.uber.org/zap" -) - -var cnfgPath = flag.String("cnfg", "", "path to config file in json format used to run the protocol") - -var logger *zap.Logger - -func main() { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - ctx = testutils.MakeSupervisorContext(ctx) - logger = supervisor.Logger(ctx) - - logger.Info("Loading KeyGenerator and GuardianStorage for DKG...") - cnfgs := loadConfigsFromFlags() - - keygen, gst := keygeneratorSetup(cnfgs) - - keygen.Start(ctx) - - logger.Info("Setting up server...") - srvr := createServer(keygen) - go func() { - if err := srvr.Run(ctx); err != nil { - logger.Fatal("Server stopped", zap.Error(err)) - - cancel() // stop the context if the server fails to run - } - }() - - time.Sleep(2 * time.Second) // wait for the server to start - logger.Info("DKG server is running, waiting for connections...") - if err := srvr.WaitForConnections(ctx); err != nil { - logger.Error("Failed to wait for connections", zap.Error(err)) - } - - time.Sleep(time.Second * 3) - - logger.Info("Connections established, starting DKG...") - - run(ctx, keygen, gst, cnfgs) -} - -func run(ctx context.Context, keygen engine.KeyGenerator, gst *engine.GuardianStorage, cnfgs *cmd.SetupConfigs) { - for i := range 10 { // The loop should converge after 2~3 iterations. - logger.Info("Starting new DKG session", zap.Int("session", i)) - - resChn, err := keygen.StartDKG(party.DkgTask{ - Threshold: gst.Threshold, - Seed: sha256.Sum256([]byte("dkg seed:" + strconv.Itoa(i))), - }) - - if err != nil { - logger.Fatal("failed to start DKG", zap.Error(err)) - } - - var tssConfigs *party.TSSSecrets - select { - case tssConfigs = <-resChn: - case <-ctx.Done(): - logger.Error("context expired before DKG finished", zap.Error(ctx.Err())) - - return - case <-time.After(time.Second * 20): - logger.Info("No progress. Retrying...", zap.Int("session", i)) - - continue - } - - crv := tssConfigs.PublicKey.Curve() - - lg := logger.With(zap.String("TrackingID", tssConfigs.TrackingID.ToString())) - - lg.Info("completed a DKG session") - - pkMarshal, err := crv.MarshalPoint(tssConfigs.PublicKey.Clone()) - if err != nil { - lg.Fatal("failed to marshal public key", zap.Error(err)) - } - - lg.Info("verifying resulting PK is valid for TSS usage", - zap.String("pk", hex.EncodeToString(pkMarshal)), - ) - - lg.Info("verifying randomly chosen PK is valid for smart-contract usage") - if !sign.PublicKeyValidForContract(tssConfigs.PublicKey) { - continue - } - - buff := bytes.NewBuffer(nil) - enc := gob.NewEncoder(buff) - - if err := enc.Encode(tssConfigs); err != nil { - lg.Fatal("failed to marshal frost configuration", zap.Error(err)) - } - - gst.TSSSecrets = buff.Bytes() - if err := gst.SetInnerFields(); err != nil { - lg.Fatal("failed to set inner fields of the GuardianStorage", zap.Error(err)) - } - - lg.Info("GuardianStorage updated with TSS secrets. Storing result into file", zap.Int("guardianIndex", i)) - - toStore, err := json.MarshalIndent(gst, "", " ") - if err != nil { - lg.Fatal("failed to marshal GuardianStorage", zap.Error(err)) - } - - // create path of dirs using cnfgs.StorageLocation: - if err := os.MkdirAll(cnfgs.StorageLocation, 0700); err != nil { - lg.Fatal("failed to create storage directory", zap.Error(err)) - } - - fname := path.Join(cnfgs.StorageLocation, "secrets.json") - - lg.Info("Writing GuardianStorage to file", zap.String("file", fname)) - if err := os.WriteFile(fname, toStore, 0600); err != nil { - lg.Fatal("failed to write GuardianStorage to file", zap.Error(err)) - } - - lg.Info("GuardianStorage successfully written to file", zap.String("file", fname)) - - return - } - - logger.Fatal("failed to complete DKG after 10 attempts, please check the logs for more details") -} - -func createServer(keygen engine.KeyGenerator) comm.DirectLink { - srvr, err := comm.NewServer(logger, keygen) - if err != nil { - logger.Fatal("failed to create a new server", zap.Error(err)) - } - return srvr -} - -func keygeneratorSetup(cnfgs *cmd.SetupConfigs) (engine.KeyGenerator, *engine.GuardianStorage) { - engineIds, _, err := cnfgs.IntoMaps() - if err != nil { - logger.Fatal("failed to convert configs engine's Identities", zap.Error(err)) - } - - sortedIds := cmd.SortIdentities(engineIds) - var self *engine.Identity - // find self: - for _, id := range sortedIds { - if bytes.Equal(id.CertPem, cnfgs.Self.TlsX509) { - // found self, set the communication index. - - self = id - break - } - } - - if self == nil { - logger.Fatal("failed to find self in the sorted identities, please check the config file") - } - - // Then Create a GuardianStorage. - gst := &engine.GuardianStorage{ - Configurations: engine.Configurations{}, - Self: self, - IdentitiesKeep: engine.IdentitiesKeep{ - Identities: sortedIds, - }, - - TlsX509: cnfgs.Self.TlsX509, - PrivateKey: cnfgs.SelfSecret, - Threshold: cnfgs.WantedThreshold - 1, - TSSSecrets: nil, // Hopefully, will be set at the end of this process. - LoadDistributionKey: []byte{}, // don't need this for DKG - } - if err := gst.SetInnerFields(); err != nil { - logger.Fatal("failed to set inner fields of the GuardianStorage", zap.Error(err)) - } - - // Then Create a KeyGenerator. - keygen, err := engine.NewKeyGenerator(gst) - if err != nil { - logger.Fatal("failed to create a KeyGenerator", zap.Error(err)) - } - - return keygen, gst -} - -func loadConfigsFromFlags() *cmd.SetupConfigs { - flag.Parse() - - if *cnfgPath == "" { - flag.PrintDefaults() - - logger.Fatal("config path is empty, please provide a valid path to a config file") - } - - logger.Info("Loading config file", zap.String("path", *cnfgPath)) - - f, err := os.ReadFile(*cnfgPath) - if err != nil { - logger.Fatal("failed to read file, err: ", zap.Error(err)) - } - - cnfg := &cmd.SetupConfigs{} - - if err = json.Unmarshal(f, cnfg); err != nil { - logger.Fatal("failed to unmarshal config file", zap.Error(err)) - } - - return cnfg -} diff --git a/node/pkg/tss/internal/cmd/examples/dkg_config_example.json b/node/pkg/tss/internal/cmd/examples/dkg_config_example.json deleted file mode 100644 index d8c025c0d6..0000000000 --- a/node/pkg/tss/internal/cmd/examples/dkg_config_example.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "NumParticipants": 5, - "WantedThreshold": 3, - "Self": { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRWnh6R3lNY2F3eUNyaUZ5R0htblJDREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmhBQm5LaG4waFc0cEp6ZlVuaXQ1UFJYZ1h4aUtSeC80WDFHRnArOTcybmtoVVFmQ3ZDZFp0U3Zlb0lmaHNOVGMKc21EREd6VDY1azlHc1F3K290U3ZXS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZNV3hmQ2xBOUFwUCtwb2ZVK1FBbUlLOFcxYytNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRaWFYzTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFENWhKbDU0QkJYRlp1NldKd0dFZi9Qd3czZXYvYTJoUFNYCngvN245MzVRUFFJZ05aV1dKQk9hQXJtU0xQbVdRNE1xQWRlbHo5ZEhtSjFWS3hxOWE5SFZFaGM9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8804 - }, - "SelfSecret": "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ0lkTWxpZkROT1hiaDdwT0QKc21OeTFkSlVUQ3BURnhTdFVpcnp5amQrdGNlaFJBTkNBQVNFQUdjcUdmU0ZiaWtuTjlTZUszazlGZUJmR0lwSApIL2hmVVlXbjczdmFlU0ZSQjhLOEoxbTFLOTZnaCtHdzFOeXlZTU1iTlBybVQwYXhERDZpMUs5WQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==", - "StorageLocation": ".", - "Peers": [ - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVgyZ0F3SUJBZ0lSQU1uSDFSenlZd1R5L3g0TWNsR1RncDB3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJLM0x5akJZSUo5OHlzMEtqU0RQMXBnV3ZMbWhJWm0xbkp2aW1CMnUxenlYWGR3NUxaZGx0N1JMNVpjYjlLVmkKRUo5eFJObTZwcERWQW5heEt0VjBlVmVqZ2JFd2dhNHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCVEljOUU0aTJTRlRMZnEwbGxpaUhoc09kS3JnREJOQmdOVkhSRUVSakJFZ2pGMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV0Z6YVdFdE1ESXVaMk53TG5SbGMzUnVaWFF1ZUd4aFluTXVlSGw2Z2dsc2IyTmhiR2h2YzNTSApCSDhBQUFFd0NnWUlLb1pJemowRUF3SURTQUF3UlFJZ05DR09rRlI2WTRlR2NiNE1FcnRuMnVoc29CaXFSdko0CkRiaTBrNUVxbkI4Q0lRQ2NLOVUySHZwUG4vbFc5UDhLcHFpUjg2cW9oN3Z3MWZKc3EzUmIrNjVjUVE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "Port": 8800 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxRENDQVh1Z0F3SUJBZ0lRUnRrMVFBOXdaUXl3WlNTaGRhZmswVEFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCm9GZnBhQlFlVmdwcWJ3SWRKa0RnNS9OMVJlQ3ZrYjNZSk1xeWU3elBmUUQ2TThWMFpQb0JmV0MvSWlXb2xReTgKaDN0YVVmQXVGbHFGaUkzMXF5dlRHS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZBL2lZTjh0MUlBS3hSRGN3WE5PaUU2ZTU1ZVNNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRkWE4zTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUNid3I5eVRObTlTOHRYUlMwVnNvWGpyanljTXhCRUhzejYrCjlFOGV4SWI2QWlCK3NGbFc3Y05zUWVpcjVsMzc2bUtYU0R4RWNWRlJmNTBuYVB4TS9UY21YUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8801 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRQ2FsMHBQeUM2SHJDQW8vM3pobHEwREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmlOZHZkdU5QL1JPK1hPRGlpdXlNRUEzcG5ZcmxkVDNTR2ozR1ZCL2lHNy9BS0QyNlkybCtNQlZzcWlNNERvRFgKSHFXTVB5ZUsrQ1BEQ0xYWGJBMUlyYU9Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZGREQ0UWtmUkd1dFJQcldheDBpQkVXclVLcFVNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRkWE5sTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSUZ0Mmk3SldkSkNzL3ZNemtoc2dOK1RVMVErREZ4UWhmVzR4CkNGRkdET2o2QWlFQWhyZmJ0b0JTOFRRWWwyeXRmS1VMNDlJRlptOFVOWFZuSHY0b0tWU1dwYlk9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8802 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVh5Z0F3SUJBZ0lSQVBOUzYvdGNzdE9lZERlb2FpNWV0SDR3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJIeUl5NDBOcHRZZHR5c3h6bFdHNUpoakRIcUs0NmxHUWw2YXFyRzlrSlFodlFSeUg3M2d0Tzl1Mmd0enEySHoKZ2ZhUlhCRzBkdVZuTUZ1dkp2aHB1b2lqZ2JBd2dhMHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCUXRHaUlSQ212aFErdlZKOFNjUVlxM1FhSFp3akJNQmdOVkhSRUVSVEJEZ2pCMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV1YxWXkwd01pNW5ZM0F1ZEdWemRHNWxkQzU0YkdGaWN5NTRlWHFDQ1d4dlkyRnNhRzl6ZEljRQpmd0FBQVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQThCWjZ3UjB2K01UQUJDNXNDWk5oZGQzK3NuQmFMWUhqCjNVd3Q1eGZpK0h3Q0lRQ29qcnBSd0tEMXRIVXNCM3NLN3dpSjBjcGlEdVV2K0YvSHc1QWNFWml6OHc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "Port": 8803 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRWnh6R3lNY2F3eUNyaUZ5R0htblJDREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmhBQm5LaG4waFc0cEp6ZlVuaXQ1UFJYZ1h4aUtSeC80WDFHRnArOTcybmtoVVFmQ3ZDZFp0U3Zlb0lmaHNOVGMKc21EREd6VDY1azlHc1F3K290U3ZXS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZNV3hmQ2xBOUFwUCtwb2ZVK1FBbUlLOFcxYytNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRaWFYzTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFENWhKbDU0QkJYRlp1NldKd0dFZi9Qd3czZXYvYTJoUFNYCngvN245MzVRUFFJZ05aV1dKQk9hQXJtU0xQbVdRNE1xQWRlbHo5ZEhtSjFWS3hxOWE5SFZFaGM9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8804 - } - ] -} \ No newline at end of file diff --git a/node/pkg/tss/internal/cmd/examples/multi_process_config_example/1/dkg.json b/node/pkg/tss/internal/cmd/examples/multi_process_config_example/1/dkg.json deleted file mode 100644 index acceefc662..0000000000 --- a/node/pkg/tss/internal/cmd/examples/multi_process_config_example/1/dkg.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "NumParticipants": 5, - "WantedThreshold": 3, - "Self": { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVgyZ0F3SUJBZ0lSQU1uSDFSenlZd1R5L3g0TWNsR1RncDB3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJLM0x5akJZSUo5OHlzMEtqU0RQMXBnV3ZMbWhJWm0xbkp2aW1CMnUxenlYWGR3NUxaZGx0N1JMNVpjYjlLVmkKRUo5eFJObTZwcERWQW5heEt0VjBlVmVqZ2JFd2dhNHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCVEljOUU0aTJTRlRMZnEwbGxpaUhoc09kS3JnREJOQmdOVkhSRUVSakJFZ2pGMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV0Z6YVdFdE1ESXVaMk53TG5SbGMzUnVaWFF1ZUd4aFluTXVlSGw2Z2dsc2IyTmhiR2h2YzNTSApCSDhBQUFFd0NnWUlLb1pJemowRUF3SURTQUF3UlFJZ05DR09rRlI2WTRlR2NiNE1FcnRuMnVoc29CaXFSdko0CkRiaTBrNUVxbkI4Q0lRQ2NLOVUySHZwUG4vbFc5UDhLcHFpUjg2cW9oN3Z3MWZKc3EzUmIrNjVjUVE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "Port": 8800 - }, - "SelfSecret": "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ09NOTRDcUNmNWNwZGVKNmMKK1ZKQ21hb3M2cmdrR1RtODRoTnV1WHJpSVRLaFJBTkNBQVN0eThvd1dDQ2ZmTXJOQ28wZ3o5YVlGcnk1b1NHWgp0WnliNHBnZHJ0YzhsMTNjT1MyWFpiZTBTK1dYRy9TbFloQ2ZjVVRadXFhUTFRSjJzU3JWZEhsWAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==", - "StorageLocation": ".", - "Peers": [ - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVgyZ0F3SUJBZ0lSQU1uSDFSenlZd1R5L3g0TWNsR1RncDB3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJLM0x5akJZSUo5OHlzMEtqU0RQMXBnV3ZMbWhJWm0xbkp2aW1CMnUxenlYWGR3NUxaZGx0N1JMNVpjYjlLVmkKRUo5eFJObTZwcERWQW5heEt0VjBlVmVqZ2JFd2dhNHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCVEljOUU0aTJTRlRMZnEwbGxpaUhoc09kS3JnREJOQmdOVkhSRUVSakJFZ2pGMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV0Z6YVdFdE1ESXVaMk53TG5SbGMzUnVaWFF1ZUd4aFluTXVlSGw2Z2dsc2IyTmhiR2h2YzNTSApCSDhBQUFFd0NnWUlLb1pJemowRUF3SURTQUF3UlFJZ05DR09rRlI2WTRlR2NiNE1FcnRuMnVoc29CaXFSdko0CkRiaTBrNUVxbkI4Q0lRQ2NLOVUySHZwUG4vbFc5UDhLcHFpUjg2cW9oN3Z3MWZKc3EzUmIrNjVjUVE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "Port": 8800 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxRENDQVh1Z0F3SUJBZ0lRUnRrMVFBOXdaUXl3WlNTaGRhZmswVEFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCm9GZnBhQlFlVmdwcWJ3SWRKa0RnNS9OMVJlQ3ZrYjNZSk1xeWU3elBmUUQ2TThWMFpQb0JmV0MvSWlXb2xReTgKaDN0YVVmQXVGbHFGaUkzMXF5dlRHS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZBL2lZTjh0MUlBS3hSRGN3WE5PaUU2ZTU1ZVNNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRkWE4zTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUNid3I5eVRObTlTOHRYUlMwVnNvWGpyanljTXhCRUhzejYrCjlFOGV4SWI2QWlCK3NGbFc3Y05zUWVpcjVsMzc2bUtYU0R4RWNWRlJmNTBuYVB4TS9UY21YUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8801 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRQ2FsMHBQeUM2SHJDQW8vM3pobHEwREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmlOZHZkdU5QL1JPK1hPRGlpdXlNRUEzcG5ZcmxkVDNTR2ozR1ZCL2lHNy9BS0QyNlkybCtNQlZzcWlNNERvRFgKSHFXTVB5ZUsrQ1BEQ0xYWGJBMUlyYU9Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZGREQ0UWtmUkd1dFJQcldheDBpQkVXclVLcFVNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRkWE5sTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSUZ0Mmk3SldkSkNzL3ZNemtoc2dOK1RVMVErREZ4UWhmVzR4CkNGRkdET2o2QWlFQWhyZmJ0b0JTOFRRWWwyeXRmS1VMNDlJRlptOFVOWFZuSHY0b0tWU1dwYlk9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8802 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVh5Z0F3SUJBZ0lSQVBOUzYvdGNzdE9lZERlb2FpNWV0SDR3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJIeUl5NDBOcHRZZHR5c3h6bFdHNUpoakRIcUs0NmxHUWw2YXFyRzlrSlFodlFSeUg3M2d0Tzl1Mmd0enEySHoKZ2ZhUlhCRzBkdVZuTUZ1dkp2aHB1b2lqZ2JBd2dhMHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCUXRHaUlSQ212aFErdlZKOFNjUVlxM1FhSFp3akJNQmdOVkhSRUVSVEJEZ2pCMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV1YxWXkwd01pNW5ZM0F1ZEdWemRHNWxkQzU0YkdGaWN5NTRlWHFDQ1d4dlkyRnNhRzl6ZEljRQpmd0FBQVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQThCWjZ3UjB2K01UQUJDNXNDWk5oZGQzK3NuQmFMWUhqCjNVd3Q1eGZpK0h3Q0lRQ29qcnBSd0tEMXRIVXNCM3NLN3dpSjBjcGlEdVV2K0YvSHc1QWNFWml6OHc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "Port": 8803 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRWnh6R3lNY2F3eUNyaUZ5R0htblJDREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmhBQm5LaG4waFc0cEp6ZlVuaXQ1UFJYZ1h4aUtSeC80WDFHRnArOTcybmtoVVFmQ3ZDZFp0U3Zlb0lmaHNOVGMKc21EREd6VDY1azlHc1F3K290U3ZXS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZNV3hmQ2xBOUFwUCtwb2ZVK1FBbUlLOFcxYytNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRaWFYzTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFENWhKbDU0QkJYRlp1NldKd0dFZi9Qd3czZXYvYTJoUFNYCngvN245MzVRUFFJZ05aV1dKQk9hQXJtU0xQbVdRNE1xQWRlbHo5ZEhtSjFWS3hxOWE5SFZFaGM9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8804 - } - ] -} \ No newline at end of file diff --git a/node/pkg/tss/internal/cmd/examples/multi_process_config_example/2/dkg.json b/node/pkg/tss/internal/cmd/examples/multi_process_config_example/2/dkg.json deleted file mode 100644 index f4b6677bd9..0000000000 --- a/node/pkg/tss/internal/cmd/examples/multi_process_config_example/2/dkg.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "NumParticipants": 5, - "WantedThreshold": 3, - "Self": { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxRENDQVh1Z0F3SUJBZ0lRUnRrMVFBOXdaUXl3WlNTaGRhZmswVEFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCm9GZnBhQlFlVmdwcWJ3SWRKa0RnNS9OMVJlQ3ZrYjNZSk1xeWU3elBmUUQ2TThWMFpQb0JmV0MvSWlXb2xReTgKaDN0YVVmQXVGbHFGaUkzMXF5dlRHS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZBL2lZTjh0MUlBS3hSRGN3WE5PaUU2ZTU1ZVNNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRkWE4zTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUNid3I5eVRObTlTOHRYUlMwVnNvWGpyanljTXhCRUhzejYrCjlFOGV4SWI2QWlCK3NGbFc3Y05zUWVpcjVsMzc2bUtYU0R4RWNWRlJmNTBuYVB4TS9UY21YUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8801 - }, - "SelfSecret": "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ2E5cUs3SzZTODlxanpub2UKMkNxZzZENnBsUWlLS0VqT3J3L2xVTDJndFVDaFJBTkNBQVNnVitsb0ZCNVdDbXB2QWgwbVFPRG44M1ZGNEsrUgp2ZGdreXJKN3ZNOTlBUG96eFhSaytnRjlZTDhpSmFpVkRMeUhlMXBSOEM0V1dvV0lqZldySzlNWQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==", - "StorageLocation": ".", - "Peers": [ - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVgyZ0F3SUJBZ0lSQU1uSDFSenlZd1R5L3g0TWNsR1RncDB3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJLM0x5akJZSUo5OHlzMEtqU0RQMXBnV3ZMbWhJWm0xbkp2aW1CMnUxenlYWGR3NUxaZGx0N1JMNVpjYjlLVmkKRUo5eFJObTZwcERWQW5heEt0VjBlVmVqZ2JFd2dhNHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCVEljOUU0aTJTRlRMZnEwbGxpaUhoc09kS3JnREJOQmdOVkhSRUVSakJFZ2pGMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV0Z6YVdFdE1ESXVaMk53TG5SbGMzUnVaWFF1ZUd4aFluTXVlSGw2Z2dsc2IyTmhiR2h2YzNTSApCSDhBQUFFd0NnWUlLb1pJemowRUF3SURTQUF3UlFJZ05DR09rRlI2WTRlR2NiNE1FcnRuMnVoc29CaXFSdko0CkRiaTBrNUVxbkI4Q0lRQ2NLOVUySHZwUG4vbFc5UDhLcHFpUjg2cW9oN3Z3MWZKc3EzUmIrNjVjUVE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "Port": 8800 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxRENDQVh1Z0F3SUJBZ0lRUnRrMVFBOXdaUXl3WlNTaGRhZmswVEFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCm9GZnBhQlFlVmdwcWJ3SWRKa0RnNS9OMVJlQ3ZrYjNZSk1xeWU3elBmUUQ2TThWMFpQb0JmV0MvSWlXb2xReTgKaDN0YVVmQXVGbHFGaUkzMXF5dlRHS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZBL2lZTjh0MUlBS3hSRGN3WE5PaUU2ZTU1ZVNNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRkWE4zTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUNid3I5eVRObTlTOHRYUlMwVnNvWGpyanljTXhCRUhzejYrCjlFOGV4SWI2QWlCK3NGbFc3Y05zUWVpcjVsMzc2bUtYU0R4RWNWRlJmNTBuYVB4TS9UY21YUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8801 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRQ2FsMHBQeUM2SHJDQW8vM3pobHEwREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmlOZHZkdU5QL1JPK1hPRGlpdXlNRUEzcG5ZcmxkVDNTR2ozR1ZCL2lHNy9BS0QyNlkybCtNQlZzcWlNNERvRFgKSHFXTVB5ZUsrQ1BEQ0xYWGJBMUlyYU9Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZGREQ0UWtmUkd1dFJQcldheDBpQkVXclVLcFVNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRkWE5sTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSUZ0Mmk3SldkSkNzL3ZNemtoc2dOK1RVMVErREZ4UWhmVzR4CkNGRkdET2o2QWlFQWhyZmJ0b0JTOFRRWWwyeXRmS1VMNDlJRlptOFVOWFZuSHY0b0tWU1dwYlk9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8802 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVh5Z0F3SUJBZ0lSQVBOUzYvdGNzdE9lZERlb2FpNWV0SDR3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJIeUl5NDBOcHRZZHR5c3h6bFdHNUpoakRIcUs0NmxHUWw2YXFyRzlrSlFodlFSeUg3M2d0Tzl1Mmd0enEySHoKZ2ZhUlhCRzBkdVZuTUZ1dkp2aHB1b2lqZ2JBd2dhMHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCUXRHaUlSQ212aFErdlZKOFNjUVlxM1FhSFp3akJNQmdOVkhSRUVSVEJEZ2pCMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV1YxWXkwd01pNW5ZM0F1ZEdWemRHNWxkQzU0YkdGaWN5NTRlWHFDQ1d4dlkyRnNhRzl6ZEljRQpmd0FBQVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQThCWjZ3UjB2K01UQUJDNXNDWk5oZGQzK3NuQmFMWUhqCjNVd3Q1eGZpK0h3Q0lRQ29qcnBSd0tEMXRIVXNCM3NLN3dpSjBjcGlEdVV2K0YvSHc1QWNFWml6OHc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "Port": 8803 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRWnh6R3lNY2F3eUNyaUZ5R0htblJDREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmhBQm5LaG4waFc0cEp6ZlVuaXQ1UFJYZ1h4aUtSeC80WDFHRnArOTcybmtoVVFmQ3ZDZFp0U3Zlb0lmaHNOVGMKc21EREd6VDY1azlHc1F3K290U3ZXS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZNV3hmQ2xBOUFwUCtwb2ZVK1FBbUlLOFcxYytNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRaWFYzTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFENWhKbDU0QkJYRlp1NldKd0dFZi9Qd3czZXYvYTJoUFNYCngvN245MzVRUFFJZ05aV1dKQk9hQXJtU0xQbVdRNE1xQWRlbHo5ZEhtSjFWS3hxOWE5SFZFaGM9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8804 - } - ] -} \ No newline at end of file diff --git a/node/pkg/tss/internal/cmd/examples/multi_process_config_example/3/dkg.json b/node/pkg/tss/internal/cmd/examples/multi_process_config_example/3/dkg.json deleted file mode 100644 index a39da02e04..0000000000 --- a/node/pkg/tss/internal/cmd/examples/multi_process_config_example/3/dkg.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "NumParticipants": 5, - "WantedThreshold": 3, - "Self": { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRQ2FsMHBQeUM2SHJDQW8vM3pobHEwREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmlOZHZkdU5QL1JPK1hPRGlpdXlNRUEzcG5ZcmxkVDNTR2ozR1ZCL2lHNy9BS0QyNlkybCtNQlZzcWlNNERvRFgKSHFXTVB5ZUsrQ1BEQ0xYWGJBMUlyYU9Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZGREQ0UWtmUkd1dFJQcldheDBpQkVXclVLcFVNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRkWE5sTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSUZ0Mmk3SldkSkNzL3ZNemtoc2dOK1RVMVErREZ4UWhmVzR4CkNGRkdET2o2QWlFQWhyZmJ0b0JTOFRRWWwyeXRmS1VMNDlJRlptOFVOWFZuSHY0b0tWU1dwYlk9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8802 - }, - "SelfSecret": "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ3p3VFB2RElVWnkyOVk0dXIKTWxRcmZ3QkNvMzRqQm5VVE0zdFc0a2R6bE1taFJBTkNBQVNJMTI5MjQwLzlFNzVjNE9LSzdJd1FEZW1kaXVWMQpQZElhUGNaVUgrSWJ2OEFvUGJwamFYNHdGV3lxSXpnT2dOY2VwWXcvSjRyNEk4TUl0ZGRzRFVpdAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==", - "StorageLocation": ".", - "Peers": [ - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVgyZ0F3SUJBZ0lSQU1uSDFSenlZd1R5L3g0TWNsR1RncDB3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJLM0x5akJZSUo5OHlzMEtqU0RQMXBnV3ZMbWhJWm0xbkp2aW1CMnUxenlYWGR3NUxaZGx0N1JMNVpjYjlLVmkKRUo5eFJObTZwcERWQW5heEt0VjBlVmVqZ2JFd2dhNHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCVEljOUU0aTJTRlRMZnEwbGxpaUhoc09kS3JnREJOQmdOVkhSRUVSakJFZ2pGMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV0Z6YVdFdE1ESXVaMk53TG5SbGMzUnVaWFF1ZUd4aFluTXVlSGw2Z2dsc2IyTmhiR2h2YzNTSApCSDhBQUFFd0NnWUlLb1pJemowRUF3SURTQUF3UlFJZ05DR09rRlI2WTRlR2NiNE1FcnRuMnVoc29CaXFSdko0CkRiaTBrNUVxbkI4Q0lRQ2NLOVUySHZwUG4vbFc5UDhLcHFpUjg2cW9oN3Z3MWZKc3EzUmIrNjVjUVE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "Port": 8800 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxRENDQVh1Z0F3SUJBZ0lRUnRrMVFBOXdaUXl3WlNTaGRhZmswVEFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCm9GZnBhQlFlVmdwcWJ3SWRKa0RnNS9OMVJlQ3ZrYjNZSk1xeWU3elBmUUQ2TThWMFpQb0JmV0MvSWlXb2xReTgKaDN0YVVmQXVGbHFGaUkzMXF5dlRHS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZBL2lZTjh0MUlBS3hSRGN3WE5PaUU2ZTU1ZVNNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRkWE4zTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUNid3I5eVRObTlTOHRYUlMwVnNvWGpyanljTXhCRUhzejYrCjlFOGV4SWI2QWlCK3NGbFc3Y05zUWVpcjVsMzc2bUtYU0R4RWNWRlJmNTBuYVB4TS9UY21YUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8801 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRQ2FsMHBQeUM2SHJDQW8vM3pobHEwREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmlOZHZkdU5QL1JPK1hPRGlpdXlNRUEzcG5ZcmxkVDNTR2ozR1ZCL2lHNy9BS0QyNlkybCtNQlZzcWlNNERvRFgKSHFXTVB5ZUsrQ1BEQ0xYWGJBMUlyYU9Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZGREQ0UWtmUkd1dFJQcldheDBpQkVXclVLcFVNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRkWE5sTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSUZ0Mmk3SldkSkNzL3ZNemtoc2dOK1RVMVErREZ4UWhmVzR4CkNGRkdET2o2QWlFQWhyZmJ0b0JTOFRRWWwyeXRmS1VMNDlJRlptOFVOWFZuSHY0b0tWU1dwYlk9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8802 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVh5Z0F3SUJBZ0lSQVBOUzYvdGNzdE9lZERlb2FpNWV0SDR3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJIeUl5NDBOcHRZZHR5c3h6bFdHNUpoakRIcUs0NmxHUWw2YXFyRzlrSlFodlFSeUg3M2d0Tzl1Mmd0enEySHoKZ2ZhUlhCRzBkdVZuTUZ1dkp2aHB1b2lqZ2JBd2dhMHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCUXRHaUlSQ212aFErdlZKOFNjUVlxM1FhSFp3akJNQmdOVkhSRUVSVEJEZ2pCMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV1YxWXkwd01pNW5ZM0F1ZEdWemRHNWxkQzU0YkdGaWN5NTRlWHFDQ1d4dlkyRnNhRzl6ZEljRQpmd0FBQVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQThCWjZ3UjB2K01UQUJDNXNDWk5oZGQzK3NuQmFMWUhqCjNVd3Q1eGZpK0h3Q0lRQ29qcnBSd0tEMXRIVXNCM3NLN3dpSjBjcGlEdVV2K0YvSHc1QWNFWml6OHc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "Port": 8803 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRWnh6R3lNY2F3eUNyaUZ5R0htblJDREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmhBQm5LaG4waFc0cEp6ZlVuaXQ1UFJYZ1h4aUtSeC80WDFHRnArOTcybmtoVVFmQ3ZDZFp0U3Zlb0lmaHNOVGMKc21EREd6VDY1azlHc1F3K290U3ZXS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZNV3hmQ2xBOUFwUCtwb2ZVK1FBbUlLOFcxYytNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRaWFYzTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFENWhKbDU0QkJYRlp1NldKd0dFZi9Qd3czZXYvYTJoUFNYCngvN245MzVRUFFJZ05aV1dKQk9hQXJtU0xQbVdRNE1xQWRlbHo5ZEhtSjFWS3hxOWE5SFZFaGM9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8804 - } - ] -} \ No newline at end of file diff --git a/node/pkg/tss/internal/cmd/examples/multi_process_config_example/4/dkg.json b/node/pkg/tss/internal/cmd/examples/multi_process_config_example/4/dkg.json deleted file mode 100644 index 00bd0d5366..0000000000 --- a/node/pkg/tss/internal/cmd/examples/multi_process_config_example/4/dkg.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "NumParticipants": 5, - "WantedThreshold": 3, - "Self": { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVh5Z0F3SUJBZ0lSQVBOUzYvdGNzdE9lZERlb2FpNWV0SDR3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJIeUl5NDBOcHRZZHR5c3h6bFdHNUpoakRIcUs0NmxHUWw2YXFyRzlrSlFodlFSeUg3M2d0Tzl1Mmd0enEySHoKZ2ZhUlhCRzBkdVZuTUZ1dkp2aHB1b2lqZ2JBd2dhMHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCUXRHaUlSQ212aFErdlZKOFNjUVlxM1FhSFp3akJNQmdOVkhSRUVSVEJEZ2pCMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV1YxWXkwd01pNW5ZM0F1ZEdWemRHNWxkQzU0YkdGaWN5NTRlWHFDQ1d4dlkyRnNhRzl6ZEljRQpmd0FBQVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQThCWjZ3UjB2K01UQUJDNXNDWk5oZGQzK3NuQmFMWUhqCjNVd3Q1eGZpK0h3Q0lRQ29qcnBSd0tEMXRIVXNCM3NLN3dpSjBjcGlEdVV2K0YvSHc1QWNFWml6OHc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "Port": 8803 - }, - "SelfSecret": "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ1B0MVhUenU3QzR3QTg5Ri8KdjVVN0RTR0I1NHBnZUFVKzBpakordUh0aDJhaFJBTkNBQVI4aU11TkRhYldIYmNyTWM1Vmh1U1lZd3g2aXVPcApSa0plbXFxeHZaQ1VJYjBFY2grOTRMVHZidG9MYzZ0aDg0SDJrVndSdEhibFp6QmJyeWI0YWJxSQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==", - "StorageLocation": ".", - "Peers": [ - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVgyZ0F3SUJBZ0lSQU1uSDFSenlZd1R5L3g0TWNsR1RncDB3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJLM0x5akJZSUo5OHlzMEtqU0RQMXBnV3ZMbWhJWm0xbkp2aW1CMnUxenlYWGR3NUxaZGx0N1JMNVpjYjlLVmkKRUo5eFJObTZwcERWQW5heEt0VjBlVmVqZ2JFd2dhNHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCVEljOUU0aTJTRlRMZnEwbGxpaUhoc09kS3JnREJOQmdOVkhSRUVSakJFZ2pGMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV0Z6YVdFdE1ESXVaMk53TG5SbGMzUnVaWFF1ZUd4aFluTXVlSGw2Z2dsc2IyTmhiR2h2YzNTSApCSDhBQUFFd0NnWUlLb1pJemowRUF3SURTQUF3UlFJZ05DR09rRlI2WTRlR2NiNE1FcnRuMnVoc29CaXFSdko0CkRiaTBrNUVxbkI4Q0lRQ2NLOVUySHZwUG4vbFc5UDhLcHFpUjg2cW9oN3Z3MWZKc3EzUmIrNjVjUVE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "Port": 8800 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxRENDQVh1Z0F3SUJBZ0lRUnRrMVFBOXdaUXl3WlNTaGRhZmswVEFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCm9GZnBhQlFlVmdwcWJ3SWRKa0RnNS9OMVJlQ3ZrYjNZSk1xeWU3elBmUUQ2TThWMFpQb0JmV0MvSWlXb2xReTgKaDN0YVVmQXVGbHFGaUkzMXF5dlRHS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZBL2lZTjh0MUlBS3hSRGN3WE5PaUU2ZTU1ZVNNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRkWE4zTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUNid3I5eVRObTlTOHRYUlMwVnNvWGpyanljTXhCRUhzejYrCjlFOGV4SWI2QWlCK3NGbFc3Y05zUWVpcjVsMzc2bUtYU0R4RWNWRlJmNTBuYVB4TS9UY21YUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8801 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRQ2FsMHBQeUM2SHJDQW8vM3pobHEwREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmlOZHZkdU5QL1JPK1hPRGlpdXlNRUEzcG5ZcmxkVDNTR2ozR1ZCL2lHNy9BS0QyNlkybCtNQlZzcWlNNERvRFgKSHFXTVB5ZUsrQ1BEQ0xYWGJBMUlyYU9Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZGREQ0UWtmUkd1dFJQcldheDBpQkVXclVLcFVNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRkWE5sTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSUZ0Mmk3SldkSkNzL3ZNemtoc2dOK1RVMVErREZ4UWhmVzR4CkNGRkdET2o2QWlFQWhyZmJ0b0JTOFRRWWwyeXRmS1VMNDlJRlptOFVOWFZuSHY0b0tWU1dwYlk9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8802 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVh5Z0F3SUJBZ0lSQVBOUzYvdGNzdE9lZERlb2FpNWV0SDR3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJIeUl5NDBOcHRZZHR5c3h6bFdHNUpoakRIcUs0NmxHUWw2YXFyRzlrSlFodlFSeUg3M2d0Tzl1Mmd0enEySHoKZ2ZhUlhCRzBkdVZuTUZ1dkp2aHB1b2lqZ2JBd2dhMHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCUXRHaUlSQ212aFErdlZKOFNjUVlxM1FhSFp3akJNQmdOVkhSRUVSVEJEZ2pCMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV1YxWXkwd01pNW5ZM0F1ZEdWemRHNWxkQzU0YkdGaWN5NTRlWHFDQ1d4dlkyRnNhRzl6ZEljRQpmd0FBQVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQThCWjZ3UjB2K01UQUJDNXNDWk5oZGQzK3NuQmFMWUhqCjNVd3Q1eGZpK0h3Q0lRQ29qcnBSd0tEMXRIVXNCM3NLN3dpSjBjcGlEdVV2K0YvSHc1QWNFWml6OHc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "Port": 8803 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRWnh6R3lNY2F3eUNyaUZ5R0htblJDREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmhBQm5LaG4waFc0cEp6ZlVuaXQ1UFJYZ1h4aUtSeC80WDFHRnArOTcybmtoVVFmQ3ZDZFp0U3Zlb0lmaHNOVGMKc21EREd6VDY1azlHc1F3K290U3ZXS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZNV3hmQ2xBOUFwUCtwb2ZVK1FBbUlLOFcxYytNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRaWFYzTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFENWhKbDU0QkJYRlp1NldKd0dFZi9Qd3czZXYvYTJoUFNYCngvN245MzVRUFFJZ05aV1dKQk9hQXJtU0xQbVdRNE1xQWRlbHo5ZEhtSjFWS3hxOWE5SFZFaGM9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8804 - } - ], - "Secrets": [ - "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ09NOTRDcUNmNWNwZGVKNmMKK1ZKQ21hb3M2cmdrR1RtODRoTnV1WHJpSVRLaFJBTkNBQVN0eThvd1dDQ2ZmTXJOQ28wZ3o5YVlGcnk1b1NHWgp0WnliNHBnZHJ0YzhsMTNjT1MyWFpiZTBTK1dYRy9TbFloQ2ZjVVRadXFhUTFRSjJzU3JWZEhsWAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==", - "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ2E5cUs3SzZTODlxanpub2UKMkNxZzZENnBsUWlLS0VqT3J3L2xVTDJndFVDaFJBTkNBQVNnVitsb0ZCNVdDbXB2QWgwbVFPRG44M1ZGNEsrUgp2ZGdreXJKN3ZNOTlBUG96eFhSaytnRjlZTDhpSmFpVkRMeUhlMXBSOEM0V1dvV0lqZldySzlNWQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==", - "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ3p3VFB2RElVWnkyOVk0dXIKTWxRcmZ3QkNvMzRqQm5VVE0zdFc0a2R6bE1taFJBTkNBQVNJMTI5MjQwLzlFNzVjNE9LSzdJd1FEZW1kaXVWMQpQZElhUGNaVUgrSWJ2OEFvUGJwamFYNHdGV3lxSXpnT2dOY2VwWXcvSjRyNEk4TUl0ZGRzRFVpdAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==", - "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ1B0MVhUenU3QzR3QTg5Ri8KdjVVN0RTR0I1NHBnZUFVKzBpakordUh0aDJhaFJBTkNBQVI4aU11TkRhYldIYmNyTWM1Vmh1U1lZd3g2aXVPcApSa0plbXFxeHZaQ1VJYjBFY2grOTRMVHZidG9MYzZ0aDg0SDJrVndSdEhibFp6QmJyeWI0YWJxSQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==", - "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ0lkTWxpZkROT1hiaDdwT0QKc21OeTFkSlVUQ3BURnhTdFVpcnp5amQrdGNlaFJBTkNBQVNFQUdjcUdmU0ZiaWtuTjlTZUszazlGZUJmR0lwSApIL2hmVVlXbjczdmFlU0ZSQjhLOEoxbTFLOTZnaCtHdzFOeXlZTU1iTlBybVQwYXhERDZpMUs5WQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==" - ] -} \ No newline at end of file diff --git a/node/pkg/tss/internal/cmd/examples/multi_process_config_example/5/dkg.json b/node/pkg/tss/internal/cmd/examples/multi_process_config_example/5/dkg.json deleted file mode 100644 index d8c025c0d6..0000000000 --- a/node/pkg/tss/internal/cmd/examples/multi_process_config_example/5/dkg.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "NumParticipants": 5, - "WantedThreshold": 3, - "Self": { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRWnh6R3lNY2F3eUNyaUZ5R0htblJDREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmhBQm5LaG4waFc0cEp6ZlVuaXQ1UFJYZ1h4aUtSeC80WDFHRnArOTcybmtoVVFmQ3ZDZFp0U3Zlb0lmaHNOVGMKc21EREd6VDY1azlHc1F3K290U3ZXS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZNV3hmQ2xBOUFwUCtwb2ZVK1FBbUlLOFcxYytNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRaWFYzTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFENWhKbDU0QkJYRlp1NldKd0dFZi9Qd3czZXYvYTJoUFNYCngvN245MzVRUFFJZ05aV1dKQk9hQXJtU0xQbVdRNE1xQWRlbHo5ZEhtSjFWS3hxOWE5SFZFaGM9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8804 - }, - "SelfSecret": "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ0lkTWxpZkROT1hiaDdwT0QKc21OeTFkSlVUQ3BURnhTdFVpcnp5amQrdGNlaFJBTkNBQVNFQUdjcUdmU0ZiaWtuTjlTZUszazlGZUJmR0lwSApIL2hmVVlXbjczdmFlU0ZSQjhLOEoxbTFLOTZnaCtHdzFOeXlZTU1iTlBybVQwYXhERDZpMUs5WQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==", - "StorageLocation": ".", - "Peers": [ - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVgyZ0F3SUJBZ0lSQU1uSDFSenlZd1R5L3g0TWNsR1RncDB3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJLM0x5akJZSUo5OHlzMEtqU0RQMXBnV3ZMbWhJWm0xbkp2aW1CMnUxenlYWGR3NUxaZGx0N1JMNVpjYjlLVmkKRUo5eFJObTZwcERWQW5heEt0VjBlVmVqZ2JFd2dhNHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCVEljOUU0aTJTRlRMZnEwbGxpaUhoc09kS3JnREJOQmdOVkhSRUVSakJFZ2pGMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV0Z6YVdFdE1ESXVaMk53TG5SbGMzUnVaWFF1ZUd4aFluTXVlSGw2Z2dsc2IyTmhiR2h2YzNTSApCSDhBQUFFd0NnWUlLb1pJemowRUF3SURTQUF3UlFJZ05DR09rRlI2WTRlR2NiNE1FcnRuMnVoc29CaXFSdko0CkRiaTBrNUVxbkI4Q0lRQ2NLOVUySHZwUG4vbFc5UDhLcHFpUjg2cW9oN3Z3MWZKc3EzUmIrNjVjUVE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "Port": 8800 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxRENDQVh1Z0F3SUJBZ0lRUnRrMVFBOXdaUXl3WlNTaGRhZmswVEFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCm9GZnBhQlFlVmdwcWJ3SWRKa0RnNS9OMVJlQ3ZrYjNZSk1xeWU3elBmUUQ2TThWMFpQb0JmV0MvSWlXb2xReTgKaDN0YVVmQXVGbHFGaUkzMXF5dlRHS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZBL2lZTjh0MUlBS3hSRGN3WE5PaUU2ZTU1ZVNNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRkWE4zTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUNid3I5eVRObTlTOHRYUlMwVnNvWGpyanljTXhCRUhzejYrCjlFOGV4SWI2QWlCK3NGbFc3Y05zUWVpcjVsMzc2bUtYU0R4RWNWRlJmNTBuYVB4TS9UY21YUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8801 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRQ2FsMHBQeUM2SHJDQW8vM3pobHEwREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmlOZHZkdU5QL1JPK1hPRGlpdXlNRUEzcG5ZcmxkVDNTR2ozR1ZCL2lHNy9BS0QyNlkybCtNQlZzcWlNNERvRFgKSHFXTVB5ZUsrQ1BEQ0xYWGJBMUlyYU9Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZGREQ0UWtmUkd1dFJQcldheDBpQkVXclVLcFVNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRkWE5sTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSUZ0Mmk3SldkSkNzL3ZNemtoc2dOK1RVMVErREZ4UWhmVzR4CkNGRkdET2o2QWlFQWhyZmJ0b0JTOFRRWWwyeXRmS1VMNDlJRlptOFVOWFZuSHY0b0tWU1dwYlk9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8802 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVh5Z0F3SUJBZ0lSQVBOUzYvdGNzdE9lZERlb2FpNWV0SDR3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJIeUl5NDBOcHRZZHR5c3h6bFdHNUpoakRIcUs0NmxHUWw2YXFyRzlrSlFodlFSeUg3M2d0Tzl1Mmd0enEySHoKZ2ZhUlhCRzBkdVZuTUZ1dkp2aHB1b2lqZ2JBd2dhMHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCUXRHaUlSQ212aFErdlZKOFNjUVlxM1FhSFp3akJNQmdOVkhSRUVSVEJEZ2pCMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV1YxWXkwd01pNW5ZM0F1ZEdWemRHNWxkQzU0YkdGaWN5NTRlWHFDQ1d4dlkyRnNhRzl6ZEljRQpmd0FBQVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQThCWjZ3UjB2K01UQUJDNXNDWk5oZGQzK3NuQmFMWUhqCjNVd3Q1eGZpK0h3Q0lRQ29qcnBSd0tEMXRIVXNCM3NLN3dpSjBjcGlEdVV2K0YvSHc1QWNFWml6OHc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "Port": 8803 - }, - { - "Hostname": "localhost", - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRWnh6R3lNY2F3eUNyaUZ5R0htblJDREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmhBQm5LaG4waFc0cEp6ZlVuaXQ1UFJYZ1h4aUtSeC80WDFHRnArOTcybmtoVVFmQ3ZDZFp0U3Zlb0lmaHNOVGMKc21EREd6VDY1azlHc1F3K290U3ZXS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZNV3hmQ2xBOUFwUCtwb2ZVK1FBbUlLOFcxYytNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRaWFYzTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFENWhKbDU0QkJYRlp1NldKd0dFZi9Qd3czZXYvYTJoUFNYCngvN245MzVRUFFJZ05aV1dKQk9hQXJtU0xQbVdRNE1xQWRlbHo5ZEhtSjFWS3hxOWE5SFZFaGM9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "Port": 8804 - } - ] -} \ No newline at end of file diff --git a/node/pkg/tss/internal/cmd/examples/secrets_example.json b/node/pkg/tss/internal/cmd/examples/secrets_example.json deleted file mode 100755 index 0b7917dacc..0000000000 --- a/node/pkg/tss/internal/cmd/examples/secrets_example.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "MaxSignerTTL": 300000000000, - "ChainsWithNoSelfReport": null, - "LeaderIdentity": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFZklqTGpRMm0xaDIzS3pIT1ZZYmttR01NZW9yagpxVVpDWHBxcXNiMlFsQ0c5QkhJZnZlQzA3MjdhQzNPcllmT0I5cEZjRWJSMjVXY3dXNjhtK0dtNmlBPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==", - "Self": { - "Pid": { - "ID": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfIjLjQ2m1h23KzHOVYbkmGMMeorj\nqUZCXpqqsb2QlCG9BHIfveC0727aC3OrYfOB9pFcEbR25WcwW68m+Gm6iA==\n-----END PUBLIC KEY-----\n" - }, - "KeyPEM": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFZklqTGpRMm0xaDIzS3pIT1ZZYmttR01NZW9yagpxVVpDWHBxcXNiMlFsQ0c5QkhJZnZlQzA3MjdhQzNPcllmT0I5cEZjRWJSMjVXY3dXNjhtK0dtNmlBPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==", - "CertPem": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVh5Z0F3SUJBZ0lSQVBOUzYvdGNzdE9lZERlb2FpNWV0SDR3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJIeUl5NDBOcHRZZHR5c3h6bFdHNUpoakRIcUs0NmxHUWw2YXFyRzlrSlFodlFSeUg3M2d0Tzl1Mmd0enEySHoKZ2ZhUlhCRzBkdVZuTUZ1dkp2aHB1b2lqZ2JBd2dhMHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCUXRHaUlSQ212aFErdlZKOFNjUVlxM1FhSFp3akJNQmdOVkhSRUVSVEJEZ2pCMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV1YxWXkwd01pNW5ZM0F1ZEdWemRHNWxkQzU0YkdGaWN5NTRlWHFDQ1d4dlkyRnNhRzl6ZEljRQpmd0FBQVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQThCWjZ3UjB2K01UQUJDNXNDWk5oZGQzK3NuQmFMWUhqCjNVd3Q1eGZpK0h3Q0lRQ29qcnBSd0tEMXRIVXNCM3NLN3dpSjBjcGlEdVV2K0YvSHc1QWNFWml6OHc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "CommunicationIndex": 0, - "Hostname": "localhost", - "Port": 8803 - }, - "IdentitiesKeep": { - "Identities": [ - { - "Pid": { - "ID": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfIjLjQ2m1h23KzHOVYbkmGMMeorj\nqUZCXpqqsb2QlCG9BHIfveC0727aC3OrYfOB9pFcEbR25WcwW68m+Gm6iA==\n-----END PUBLIC KEY-----\n" - }, - "KeyPEM": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFZklqTGpRMm0xaDIzS3pIT1ZZYmttR01NZW9yagpxVVpDWHBxcXNiMlFsQ0c5QkhJZnZlQzA3MjdhQzNPcllmT0I5cEZjRWJSMjVXY3dXNjhtK0dtNmlBPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==", - "CertPem": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVh5Z0F3SUJBZ0lSQVBOUzYvdGNzdE9lZERlb2FpNWV0SDR3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJIeUl5NDBOcHRZZHR5c3h6bFdHNUpoakRIcUs0NmxHUWw2YXFyRzlrSlFodlFSeUg3M2d0Tzl1Mmd0enEySHoKZ2ZhUlhCRzBkdVZuTUZ1dkp2aHB1b2lqZ2JBd2dhMHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCUXRHaUlSQ212aFErdlZKOFNjUVlxM1FhSFp3akJNQmdOVkhSRUVSVEJEZ2pCMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV1YxWXkwd01pNW5ZM0F1ZEdWemRHNWxkQzU0YkdGaWN5NTRlWHFDQ1d4dlkyRnNhRzl6ZEljRQpmd0FBQVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQThCWjZ3UjB2K01UQUJDNXNDWk5oZGQzK3NuQmFMWUhqCjNVd3Q1eGZpK0h3Q0lRQ29qcnBSd0tEMXRIVXNCM3NLN3dpSjBjcGlEdVV2K0YvSHc1QWNFWml6OHc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "CommunicationIndex": 0, - "Hostname": "localhost", - "Port": 8803 - }, - { - "Pid": { - "ID": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhABnKhn0hW4pJzfUnit5PRXgXxiK\nRx/4X1GFp+972nkhUQfCvCdZtSveoIfhsNTcsmDDGzT65k9GsQw+otSvWA==\n-----END PUBLIC KEY-----\n" - }, - "KeyPEM": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFaEFCbktobjBoVzRwSnpmVW5pdDVQUlhnWHhpSwpSeC80WDFHRnArOTcybmtoVVFmQ3ZDZFp0U3Zlb0lmaHNOVGNzbURER3pUNjVrOUdzUXcrb3RTdldBPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==", - "CertPem": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRWnh6R3lNY2F3eUNyaUZ5R0htblJDREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmhBQm5LaG4waFc0cEp6ZlVuaXQ1UFJYZ1h4aUtSeC80WDFHRnArOTcybmtoVVFmQ3ZDZFp0U3Zlb0lmaHNOVGMKc21EREd6VDY1azlHc1F3K290U3ZXS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZNV3hmQ2xBOUFwUCtwb2ZVK1FBbUlLOFcxYytNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRaWFYzTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFENWhKbDU0QkJYRlp1NldKd0dFZi9Qd3czZXYvYTJoUFNYCngvN245MzVRUFFJZ05aV1dKQk9hQXJtU0xQbVdRNE1xQWRlbHo5ZEhtSjFWS3hxOWE5SFZFaGM9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "CommunicationIndex": 1, - "Hostname": "localhost", - "Port": 8804 - }, - { - "Pid": { - "ID": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiNdvduNP/RO+XODiiuyMEA3pnYrl\ndT3SGj3GVB/iG7/AKD26Y2l+MBVsqiM4DoDXHqWMPyeK+CPDCLXXbA1IrQ==\n-----END PUBLIC KEY-----\n" - }, - "KeyPEM": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFaU5kdmR1TlAvUk8rWE9EaWl1eU1FQTNwbllybApkVDNTR2ozR1ZCL2lHNy9BS0QyNlkybCtNQlZzcWlNNERvRFhIcVdNUHllSytDUERDTFhYYkExSXJRPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==", - "CertPem": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxVENDQVh1Z0F3SUJBZ0lRQ2FsMHBQeUM2SHJDQW8vM3pobHEwREFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmlOZHZkdU5QL1JPK1hPRGlpdXlNRUEzcG5ZcmxkVDNTR2ozR1ZCL2lHNy9BS0QyNlkybCtNQlZzcWlNNERvRFgKSHFXTVB5ZUsrQ1BEQ0xYWGJBMUlyYU9Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZGREQ0UWtmUkd1dFJQcldheDBpQkVXclVLcFVNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRkWE5sTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSUZ0Mmk3SldkSkNzL3ZNemtoc2dOK1RVMVErREZ4UWhmVzR4CkNGRkdET2o2QWlFQWhyZmJ0b0JTOFRRWWwyeXRmS1VMNDlJRlptOFVOWFZuSHY0b0tWU1dwYlk9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "CommunicationIndex": 2, - "Hostname": "localhost", - "Port": 8802 - }, - { - "Pid": { - "ID": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoFfpaBQeVgpqbwIdJkDg5/N1ReCv\nkb3YJMqye7zPfQD6M8V0ZPoBfWC/IiWolQy8h3taUfAuFlqFiI31qyvTGA==\n-----END PUBLIC KEY-----\n" - }, - "KeyPEM": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFb0ZmcGFCUWVWZ3BxYndJZEprRGc1L04xUmVDdgprYjNZSk1xeWU3elBmUUQ2TThWMFpQb0JmV0MvSWlXb2xReThoM3RhVWZBdUZscUZpSTMxcXl2VEdBPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==", - "CertPem": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxRENDQVh1Z0F3SUJBZ0lRUnRrMVFBOXdaUXl3WlNTaGRhZmswVEFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJMU1EY3dNVEV5TkRRME5Wb1lEekl3TmpVd056TXhNVEkwTkRRMQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCm9GZnBhQlFlVmdwcWJ3SWRKa0RnNS9OMVJlQ3ZrYjNZSk1xeWU3elBmUUQ2TThWMFpQb0JmV0MvSWlXb2xReTgKaDN0YVVmQXVGbHFGaUkzMXF5dlRHS09Cc0RDQnJUQU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFCkZBL2lZTjh0MUlBS3hSRGN3WE5PaUU2ZTU1ZVNNRXdHQTFVZEVRUkZNRU9DTUhRdFoyTndMWFJvY21WemFITnAKWjI1dVpYUXRkWE4zTFRBeUxtZGpjQzUwWlhOMGJtVjBMbmhzWVdKekxuaDVlb0lKYkc5allXeG9iM04waHdSLwpBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUNid3I5eVRObTlTOHRYUlMwVnNvWGpyanljTXhCRUhzejYrCjlFOGV4SWI2QWlCK3NGbFc3Y05zUWVpcjVsMzc2bUtYU0R4RWNWRlJmNTBuYVB4TS9UY21YUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "CommunicationIndex": 3, - "Hostname": "localhost", - "Port": 8801 - }, - { - "Pid": { - "ID": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAErcvKMFggn3zKzQqNIM/WmBa8uaEh\nmbWcm+KYHa7XPJdd3Dktl2W3tEvllxv0pWIQn3FE2bqmkNUCdrEq1XR5Vw==\n-----END PUBLIC KEY-----\n" - }, - "KeyPEM": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFcmN2S01GZ2duM3pLelFxTklNL1dtQmE4dWFFaAptYldjbStLWUhhN1hQSmRkM0RrdGwyVzN0RXZsbHh2MHBXSVFuM0ZFMmJxbWtOVUNkckVxMVhSNVZ3PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==", - "CertPem": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVgyZ0F3SUJBZ0lSQU1uSDFSenlZd1R5L3g0TWNsR1RncDB3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJLM0x5akJZSUo5OHlzMEtqU0RQMXBnV3ZMbWhJWm0xbkp2aW1CMnUxenlYWGR3NUxaZGx0N1JMNVpjYjlLVmkKRUo5eFJObTZwcERWQW5heEt0VjBlVmVqZ2JFd2dhNHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCVEljOUU0aTJTRlRMZnEwbGxpaUhoc09kS3JnREJOQmdOVkhSRUVSakJFZ2pGMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV0Z6YVdFdE1ESXVaMk53TG5SbGMzUnVaWFF1ZUd4aFluTXVlSGw2Z2dsc2IyTmhiR2h2YzNTSApCSDhBQUFFd0NnWUlLb1pJemowRUF3SURTQUF3UlFJZ05DR09rRlI2WTRlR2NiNE1FcnRuMnVoc29CaXFSdko0CkRiaTBrNUVxbkI4Q0lRQ2NLOVUySHZwUG4vbFc5UDhLcHFpUjg2cW9oN3Z3MWZKc3EzUmIrNjVjUVE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "CommunicationIndex": 4, - "Hostname": "localhost", - "Port": 8800 - } - ] - }, - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVh5Z0F3SUJBZ0lSQVBOUzYvdGNzdE9lZERlb2FpNWV0SDR3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5UQTNNREV4TWpRME5EVmFHQTh5TURZMU1EY3pNVEV5TkRRMApOVm93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJIeUl5NDBOcHRZZHR5c3h6bFdHNUpoakRIcUs0NmxHUWw2YXFyRzlrSlFodlFSeUg3M2d0Tzl1Mmd0enEySHoKZ2ZhUlhCRzBkdVZuTUZ1dkp2aHB1b2lqZ2JBd2dhMHdEZ1lEVlIwUEFRSC9CQVFEQWdLRU1CMEdBMVVkSlFRVwpNQlFHQ0NzR0FRVUZCd01CQmdnckJnRUZCUWNEQWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXCkJCUXRHaUlSQ212aFErdlZKOFNjUVlxM1FhSFp3akJNQmdOVkhSRUVSVEJEZ2pCMExXZGpjQzEwYUhKbGMyaHoKYVdkdWJtVjBMV1YxWXkwd01pNW5ZM0F1ZEdWemRHNWxkQzU0YkdGaWN5NTRlWHFDQ1d4dlkyRnNhRzl6ZEljRQpmd0FBQVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQThCWjZ3UjB2K01UQUJDNXNDWk5oZGQzK3NuQmFMWUhqCjNVd3Q1eGZpK0h3Q0lRQ29qcnBSd0tEMXRIVXNCM3NLN3dpSjBjcGlEdVV2K0YvSHc1QWNFWml6OHc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", - "PrivateKey": "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ1B0MVhUenU3QzR3QTg5Ri8KdjVVN0RTR0I1NHBnZUFVKzBpakordUh0aDJhaFJBTkNBQVI4aU11TkRhYldIYmNyTWM1Vmh1U1lZd3g2aXVPcApSa0plbXFxeHZaQ1VJYjBFY2grOTRMVHZidG9MYzZ0aDg0SDJrVndSdEhibFp6QmJyeWI0YWJxSQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==", - "Threshold": 2, - "TSSSecrets": "pmJJRHiyLS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFZklqTGpRMm0xaDIzS3pIT1ZZYmttR01NZW9yagpxVVpDWHBxcXNiMlFsQ0c5QkhJZnZlQzA3MjdhQzNPcllmT0I5cEZjRWJSMjVXY3dXNjhtK0dtNmlBPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCmlUaHJlc2hvbGQCbFByaXZhdGVTaGFyZVgg7t2M+2oKeN8UAHyLR435tis6bf8+K95KYlobMPRcAylpUHVibGljS2V5WCEDUVeg2WPTQ1P/eCOOYWcvZLMQ9Uhr6E1hBEDSzHirpBBoQ2hhaW5LZXn2clZlcmlmaWNhdGlvblNoYXJlc1kENKV4si0tLS0tQkVHSU4gUFVCTElDIEtFWS0tLS0tCk1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRW9GZnBhQlFlVmdwcWJ3SWRKa0RnNS9OMVJlQ3YKa2IzWUpNcXllN3pQZlFENk04VjBaUG9CZldDL0lpV29sUXk4aDN0YVVmQXVGbHFGaUkzMXF5dlRHQT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQpYIQI8uCfQW9y2009GFtFxceycp8d/YTj1l7t5dH0ujACqIniyLS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFcmN2S01GZ2duM3pLelFxTklNL1dtQmE4dWFFaAptYldjbStLWUhhN1hQSmRkM0RrdGwyVzN0RXZsbHh2MHBXSVFuM0ZFMmJxbWtOVUNkckVxMVhSNVZ3PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tClghA5t/UMBXkdqrynXTL5686qY8DEx+chIKg0Zql3hSmWbCeLItLS0tLUJFR0lOIFBVQkxJQyBLRVktLS0tLQpNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVmSWpMalEybTFoMjNLekhPVllia21HTU1lb3JqCnFVWkNYcHFxc2IyUWxDRzlCSElmdmVDMDcyN2FDM09yWWZPQjlwRmNFYlIyNVdjd1c2OG0rR202aUE9PQotLS0tLUVORCBQVUJMSUMgS0VZLS0tLS0KWCEDIP95YVDBnloXfRLkUWkImrXEYxTOYbKtoTbHkWb+JdF4si0tLS0tQkVHSU4gUFVCTElDIEtFWS0tLS0tCk1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRWhBQm5LaG4waFc0cEp6ZlVuaXQ1UFJYZ1h4aUsKUngvNFgxR0ZwKzk3Mm5raFVRZkN2Q2RadFN2ZW9JZmhzTlRjc21EREd6VDY1azlHc1F3K290U3ZXQT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQpYIQKY6hjV/OBSB9WFpUpJgyeOyJI3a+a2BZx55RenMj2+2XiyLS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFaU5kdmR1TlAvUk8rWE9EaWl1eU1FQTNwbllybApkVDNTR2ozR1ZCL2lHNy9BS0QyNlkybCtNQlZzcWlNNERvRFhIcVdNUHllSytDUERDTFhYYkExSXJRPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tClghAnLWjcTFacOpxEK1zzuOiqGRw1yf9Nl8E43qms5Ckq6P", - "LoadDistributionKey": "" -} \ No newline at end of file diff --git a/node/pkg/tss/internal/cmd/lkg/cnfg.example.json b/node/pkg/tss/internal/cmd/lkg/cnfg.example.json deleted file mode 100644 index d9941848f3..0000000000 --- a/node/pkg/tss/internal/cmd/lkg/cnfg.example.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "NumParticipants": 5, - "WantedThreshold": 3, - - "GuardianSpecifics": [ - { - "Identifier": { - "TlsX509":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJvakNDQVVlZ0F3SUJBZ0lRUXg2NUhRRjB0Q3I2VmxVWXo2VWEzekFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJME1Ea3lNakEzTVRjeU1sb1lEekl3TmpReE1ESXlNRGN4TnpJeQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCit5ZlV2ZVBvMG1zZGxLS0FwcDM4TzR4ckJzRGN2cEFRblhmSUFDQk1ycjIzU056QnhFWVYwOGZYZ1Z2dXVGSkQKRTRRc29CRzdSb2lBZ3ZGWk5Rb055cU45TUhzd0RnWURWUjBQQVFIL0JBUURBZ0tFTUIwR0ExVWRKUVFXTUJRRwpDQ3NHQVFVRkJ3TUJCZ2dyQmdFRkJRY0RBakFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQjBHQTFVZERnUVdCQlJMCnByNHdWZ0FEeVVNZXNiVVJFWWJ3N215bDB6QWFCZ05WSFJFRUV6QVJnZ2xzYjJOaGJHaHZjM1NIQkg4QUFBRXcKQ2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQVBVejY3Q3lKb0lLclNDSlJQcCtNQktFWkkvUTFtK3JlS0RqYzNBVQpXZkhBQWlFQWxIOXpJZjNoZ2hBS3dCcCt1MlB6L05TLzZYSm96UGQ5ZFpnR1dqeHdSM2c9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" - }, - "WhereToSaveSecrets": "./save0" - }, - { - "Identifier": { - "TlsX509":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJvVENDQVVlZ0F3SUJBZ0lRVkNTcndsY1hnWnJrTWRzbFYwamk1VEFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJME1Ea3lNakEzTVRjeU1sb1lEekl3TmpReE1ESXlNRGN4TnpJeQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCkhXd3B0YWk2WGRidW5JSmR2cUc2QTZMU3pTSFZRSkNoYWU3ZkNLNnR3ZlhvSFdMVE1jeklaa2RzMTZHZnh3Sy8KY1JFbUM4Y2JsYytsREFFcVIzR0UzcU45TUhzd0RnWURWUjBQQVFIL0JBUURBZ0tFTUIwR0ExVWRKUVFXTUJRRwpDQ3NHQVFVRkJ3TUJCZ2dyQmdFRkJRY0RBakFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQjBHQTFVZERnUVdCQlRMCmtJSU95R2p4bGNIaHRnbWFxNm9PZS92WmhEQWFCZ05WSFJFRUV6QVJnZ2xzYjJOaGJHaHZjM1NIQkg4QUFBRXcKQ2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnUkVUSEhyZXNhRndoOW1udm9UZWNlNGFoWGJBWnFnSmFwVWdDSTF0YgovTXNDSVFDTVM4M25hRGNTL2lNVkc2Y3pqT3JiQnkyT3ZITnc5YUhDSm04V29MUWhDZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" - }, - "WhereToSaveSecrets": "./save1" - }, - { - "Identifier": { - "TlsX509":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJvVENDQVVpZ0F3SUJBZ0lSQUlxZ2NqN29zeWg3Y0VZb1RtdTB4cTB3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5EQTVNakl3TnpFM01qSmFHQTh5TURZME1UQXlNakEzTVRjeQpNbG93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJFOFQvanhieEVHZHFDQnFDNUQ4RDBPa0NXeSthMHJqQVUxRHJaYmxyWVFlUktYbHdLWXdGa2pOVjZNVlBROUIKbjFtcG9hNitIMmhJRnZudnNEdjAyVFdqZlRCN01BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVQpCZ2dyQmdFRkJRY0RBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVCkpya1E4ZzA4WDVFcGI4T1JsalUyb2l0WTJMd3dHZ1lEVlIwUkJCTXdFWUlKYkc5allXeG9iM04waHdSL0FBQUIKTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUJ1VnA3VE9xZXBlOEpvSnZxQTk2bnFIYzVHME9ucHZQa0t6dzJucgo2d3ExQWlCSXlJaHlpL0xVK1RWUDd3Z2JJVXpjMlJXeXZpSklDL0h0YW1ua3FxYWQ2QT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" - }, - "WhereToSaveSecrets": "./save2" - - }, - { - "Identifier": { - "TlsX509":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJvakNDQVVpZ0F3SUJBZ0lSQUxieVduSHQ2aHpRaDZMb2Q5bzBQemt3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5EQTVNakl3TnpFM01qSmFHQTh5TURZME1UQXlNakEzTVRjeQpNbG93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJGTk9sRHkvRS8wOERtRHdzMVVwaVh5TGV3UkJBUEYvUXRQbm5GR0dvcS9aeFNtZDM2U08vNGs0TnRkWFFKdkEKS05SOVpZNklmclRYUDloNTZOWVlLSTJqZlRCN01BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVQpCZ2dyQmdFRkJRY0RBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVClF4R3ZBOHVNTjF5NUFRRGMweTc2QjMrQ256OHdHZ1lEVlIwUkJCTXdFWUlKYkc5allXeG9iM04waHdSL0FBQUIKTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSUdSUS9UdTRFQWtZRFhtWW5LeHRnR0JlQjhYT2ZIa2dOWGhGM1NXTApyRzQ2QWlFQXpHTzJlWklmenVlQytFQ05XWXBzdkFwK2F0emxXQlNkNmVUdDdKZGMwczQ9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" - }, - "WhereToSaveSecrets": "./save3" - }, - { - "Identifier": { - "TlsX509":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJvRENDQVVlZ0F3SUJBZ0lRSkVGQ2RBN3ZDZE5mbHExUzZQTmJzVEFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJME1Ea3lNakEzTVRjeU1sb1lEekl3TmpReE1ESXlNRGN4TnpJeQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmJENWdUSmtLayszT2FaRE5XQi9ORHFqeHZONmRET0lvL3lBMms0NkxKbXJSakVacEJDcDFhYVZpSzk5Sm1IWG0KOWIrOWpFWndDRy9NS2ZVaVJmMjBFS045TUhzd0RnWURWUjBQQVFIL0JBUURBZ0tFTUIwR0ExVWRKUVFXTUJRRwpDQ3NHQVFVRkJ3TUJCZ2dyQmdFRkJRY0RBakFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQjBHQTFVZERnUVdCQlRKCnRnMGZzeU45NmN3THhzdWZONjJRUnRZWHpqQWFCZ05WSFJFRUV6QVJnZ2xzYjJOaGJHaHZjM1NIQkg4QUFBRXcKQ2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnWlhqelBYeTRsaklsY0swMFFxQ3p3dXROUkN3Wk1kWXNPZ2Q0RHFhQQozK1lDSUdvL2I1ZkEzWmViYmJJclJic3YzWVpOLzlsTXJFbSt1M1JQYmJsSk96L1UKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=" - }, - "WhereToSaveSecrets": "./save4" - } - ] -} \ No newline at end of file diff --git a/node/pkg/tss/internal/cmd/lkg/local_key_generation.go b/node/pkg/tss/internal/cmd/lkg/local_key_generation.go deleted file mode 100644 index 76e4b4d08e..0000000000 --- a/node/pkg/tss/internal/cmd/lkg/local_key_generation.go +++ /dev/null @@ -1,238 +0,0 @@ -// This file runs Distrubted Key Generation (DKG) protocol in local setting. -// That is, a gorotuine orchestrator simulates the network communication between the parties -// by collecting the outputs of the parties and feeding these output messages to the correct parties (using the `Update` method). - -package main - -import ( - "bytes" - "crypto/rand" - "encoding/gob" - "encoding/json" - "flag" - "fmt" - "os" - "path" - - engine "github.com/certusone/wormhole/node/pkg/tss" - "github.com/certusone/wormhole/node/pkg/tss/internal/cmd" - "github.com/xlabs/multi-party-sig/pkg/math/curve" - "github.com/xlabs/multi-party-sig/pkg/math/polynomial" - "github.com/xlabs/multi-party-sig/pkg/math/sample" - "github.com/xlabs/multi-party-sig/pkg/party" - "github.com/xlabs/multi-party-sig/protocols/frost" - "github.com/xlabs/multi-party-sig/protocols/frost/sign" -) - -var cnfgPath = flag.String("cnfg", "", "path to config file in json format used to run the protocol") - -func main() { - flag.Parse() - - if *cnfgPath == "" { - flag.PrintDefaults() - - return - } - - f, err := os.ReadFile(*cnfgPath) - if err != nil { - fmt.Println("failed to read file, err: ", err) - - return - } - - cnfg := &cmd.SetupConfigs{} - err = json.Unmarshal(f, cnfg) - if err != nil { - fmt.Println("failed to unmarshal config, err: ", err) - - return - } - - Run(cnfg) -} - -type LKGConfig cmd.SetupConfigs -type Identifier = cmd.Identifier - -type dkgPlayer struct { - whereToStore string - - // the identity of the player, tied to the x509 cert and its private key. - self *engine.Identity - ids engine.IdentitiesKeep - - selfCert []byte - certKeyPEM []byte - - // same for all guardians // generated here. - loadDistributionKey []byte -} - -func Run(cnfg *cmd.SetupConfigs) { - if cnfg == nil { - panic("config is nil") - } - - // ensuring the threshold matches what the library expects. - // (if n=5,f=1 and we want 2f+1 committees, then WantedThreshold should be equal to 2) - cnfg.WantedThreshold -= 1 - - fmt.Println("Setting up player secrets. This might take a while...") - all, err := setupPlayers(cnfg) - if err != nil { - panic(err) - } - - fmt.Println("all players generated their secrets. Starting the protocol. This might take several minutes.") - simulateDKG(all, cnfg.WantedThreshold) -} - -// this function simulates the results of running a DKG protocol. -func simulateDKG(all []*dkgPlayer, threshold int) { - group := curve.Secp256k1{} - - publicKey, f := makefrostKey(group, threshold) - - privateShares := make(map[party.ID]curve.Scalar, len(all)) - for _, p := range all { - id := party.ID(p.self.Pid.GetID()) - - privateShares[id] = f.Evaluate(id.Scalar(group)) - } - - verificationShares := make(map[party.ID]curve.Point, len(all)) - - for _, p := range all { - id := party.ID(p.self.Pid.GetID()) - - point := privateShares[id].ActOnBase() - verificationShares[id] = point - } - - guardians := make([]*engine.GuardianStorage, len(all)) - for i, p := range all { - id := party.ID(p.self.Pid.GetID()) - - // can't be jsoned. - cnf := &frost.Config{ - ID: id, - Threshold: threshold, - PrivateShare: privateShares[id], - PublicKey: publicKey, - // ChainKey: d.loadDistributionKey, // needed iff we use BIP32 key derivation. - VerificationShares: party.NewPointMap(verificationShares), - } - - buff := bytes.NewBuffer(nil) - enc := gob.NewEncoder(buff) - - if err := enc.Encode(cnf); err != nil { - panic(fmt.Sprintf("failed to marshal frost config for guardian %d: %v", i, err)) - } - - guardians[i] = &engine.GuardianStorage{ - Configurations: engine.Configurations{}, - Self: p.self, - TlsX509: p.selfCert, - PrivateKey: p.certKeyPEM, - IdentitiesKeep: p.ids, - Threshold: threshold, - // SavedSecretParameters: , - LoadDistributionKey: p.loadDistributionKey, - TSSSecrets: buff.Bytes(), - } - } - - fmt.Println("All guardians have finished the protocol. Saving the secrets to disk.") - - for i, guardian := range guardians { - if guardian == nil { - panic(fmt.Sprintf("error in script. Guardian[%d] is nil", i)) - } - - bts, err := json.MarshalIndent(guardian, "", " ") - if err != nil { - panic("") - } - - if err := os.MkdirAll(all[i].whereToStore, 0700); err != nil { - panic("Failed to create directory: " + err.Error()) - } - - fname := path.Join(all[i].whereToStore, "secrets.json") - - if err := os.WriteFile(fname, bts, 0600); err != nil { - panic("Failed to write to disk: " + err.Error()) - } - } - -} - -func makefrostKey(group curve.Secp256k1, threshold int) (curve.Point, *polynomial.Polynomial) { - for range 128 { - secret := sample.Scalar(rand.Reader, group) - f := polynomial.NewPolynomial(group, threshold, secret) - publicKey := secret.ActOnBase() - if sign.PublicKeyValidForContract(publicKey) { - return publicKey, f - } - } - - panic("could not generate a valid frost key after 128 attempts") -} - -func setupPlayers(cnfg *cmd.SetupConfigs) ([]*dkgPlayer, error) { - if err := cnfg.Validate(); err != nil { - return nil, err - } - - loadBalancingKey := make([]byte, 32) - _, err := rand.Read(loadBalancingKey) - if err != nil { - return nil, err - } - - unsortedIdentities, mp, err := cnfg.IntoMaps() - if err != nil { - return nil, err - } - - sortedIDS := cmd.SortIdentities(unsortedIdentities) - - all := make([]*dkgPlayer, cnfg.NumParticipants) - for _, id := range sortedIDS { - index := 0 - for _, v := range cnfg.Peers { - if string(v.TlsX509) == string(id.CertPem) { - break - } - - index++ - } - - tmp := make([]byte, 32) - copy(tmp, loadBalancingKey) - - // peerContext := tss.NewPeerContext(sortedPids) - - gspecific := mp[string(id.KeyPEM)] - - p := &dkgPlayer{ - self: id, - whereToStore: cnfg.SaveLocation[index], - selfCert: gspecific.TlsX509, - certKeyPEM: cnfg.Secrets[index], - loadDistributionKey: tmp, - - ids: engine.IdentitiesKeep{ - Identities: sortedIDS, - }, - } - all[id.CommunicationIndex] = p - - } - - return all, nil -} diff --git a/node/pkg/tss/internal/cmd/scripts_test.go b/node/pkg/tss/internal/cmd/scripts_test.go deleted file mode 100644 index 9c87467db6..0000000000 --- a/node/pkg/tss/internal/cmd/scripts_test.go +++ /dev/null @@ -1,435 +0,0 @@ -package cmd - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/x509" - "crypto/x509/pkix" - "encoding/json" - "fmt" - "math/big" - "net" - "os" - "os/exec" - "path" - "path/filepath" - "runtime" - "strconv" - "strings" - "testing" - "time" - - engine "github.com/certusone/wormhole/node/pkg/tss" - "github.com/certusone/wormhole/node/pkg/tss/internal" -) - -// create these from scrath, then store it into a single file. -// json should be able to read it. - -var hostnames = []string{ - "t-gcp-threshsignnet-asia-01.gcp.testnet.xlabs.xyz", - // "t-gcp-threshsignnet-asia-02.gcp.testnet.xlabs.xyz", - // "t-gcp-threshsignnet-asia-03.gcp.testnet.xlabs.xyz", - // "t-gcp-threshsignnet-asia-04.gcp.testnet.xlabs.xyz", - - "t-gcp-threshsignnet-usw-01.gcp.testnet.xlabs.xyz", - // "t-gcp-threshsignnet-usw-02.gcp.testnet.xlabs.xyz", - // "t-gcp-threshsignnet-usw-03.gcp.testnet.xlabs.xyz", - // "t-gcp-threshsignnet-usw-04.gcp.testnet.xlabs.xyz", - - "t-gcp-threshsignnet-use-01.gcp.testnet.xlabs.xyz", - // "t-gcp-threshsignnet-use-02.gcp.testnet.xlabs.xyz", - // "t-gcp-threshsignnet-use-03.gcp.testnet.xlabs.xyz", - // "t-gcp-threshsignnet-use-04.gcp.testnet.xlabs.xyz", - - "t-gcp-threshsignnet-euc-01.gcp.testnet.xlabs.xyz", - // "t-gcp-threshsignnet-euc-02.gcp.testnet.xlabs.xyz", - // "t-gcp-threshsignnet-euc-03.gcp.testnet.xlabs.xyz", - // "t-gcp-threshsignnet-euc-04.gcp.testnet.xlabs.xyz", - - "t-gcp-threshsignnet-euw-01.gcp.testnet.xlabs.xyz", - // "t-gcp-threshsignnet-euw-02.gcp.testnet.xlabs.xyz", - // "t-gcp-threshsignnet-euw-03.gcp.testnet.xlabs.xyz", -} - -const saveFile = "./lkg/lkg.json" -const specificKeysFolder = "5-servers" - -var prepareForLocalDKG = false - -type dkgTest struct { - hostnames []string - saveFolder string - - forLocalDKG bool - storeIntoInternalTestData bool -} - -func getCurrentFilePath(t *testing.T) string { - _, filename, _, ok := runtime.Caller(0) - if !ok { - t.Fatal("Unable to get the current test file path") - } - - // The path returned by runtime.Caller might be relative. - // We use filepath.Abs to ensure we get the absolute path. - absPath, err := filepath.Abs(filename) - if err != nil { - t.Fatalf("Unable to get the absolute path: %v", err) - } - - // remove the filename to get the directory - return filepath.Dir(absPath) -} - -func TestMain(t *testing.T) { - t.Skip("skipping main test, use specific tests instead") - - tt := dkgTest{ - hostnames: hostnames, - saveFolder: path.Join("dkg"), - forLocalDKG: true, - } - t.Run("CreateDKGConfigs", tt.createDKGConfigs) - - tt = dkgTest{ - hostnames: hostnames, - // for ease of debug, not using full path. - saveFolder: path.Join(getCurrentFilePath(t), "dkg"), // workingdir - forLocalDKG: true, - storeIntoInternalTestData: true, - } - t.Run("RunDKG", tt.RunDKG) - - tt = dkgTest{ - hostnames: hostnames, - // for ease of debug, not using full path. - saveFolder: path.Join(getCurrentFilePath(t), "lkg"), // workingdir - forLocalDKG: false, - storeIntoInternalTestData: false, - } - t.Run("CreateLKGConfigs", tt.createLKGConfigs) - - // t.Run("scpSecretsToServers", sendToServers) -} - -func cleanResultFolder(t *testing.T, resultDir string) { - // make sure the directory exists and EMPTY - if err := os.MkdirAll(resultDir, 0755); err != nil { - t.Fatalf("failed to create directory: %v", err) - } - - // empty all files in the directory - items, err := os.ReadDir(resultDir) - if err != nil { - t.Fatalf("failed to read directory: %v", err) - } - fmt.Println("directory contents before cleanup:") - - for _, listing := range items { - name := listing.Name() - - if path.Ext(name) != ".json" || listing.IsDir() { - fmt.Println("skipping file:", name) - continue - } - - fmt.Println("removing file:", listing.Name()) - filePath := path.Join(resultDir, listing.Name()) - if err := os.Remove(filePath); err != nil { - t.Fatalf("failed to remove file %s: %v", filePath, err) - } - } -} - -// scp -i ~/.ssh/id_ed25519 asia-01/secrets.json jonathan@%v:~ -func sendToServers(t *testing.T) { - cnfg := loadConfigs(t) - - _, file, _, ok := runtime.Caller(0) - if !ok { - t.Fatal("could not grab file due to runtime.Caller(0) failure") - } - - errs := make(chan error, len(cnfg.Peers)) - - workdir := path.Join(path.Dir(file), "setkey", "keys", specificKeysFolder) - fmt.Println("sending files...") - for i := range cnfg.Peers { - go func(i int) { - saveLocation := cnfg.SaveLocation[i] - if saveLocation == "" { - errs <- fmt.Errorf("guardian %d has empty WhereToSaveSecrets", i) - - return - } - - localSecretsPath := path.Join(workdir, saveLocation, "secrets.json") - hostname := cnfg.Peers[i].Hostname - cmd := exec.Command("scp", "-i", "~/.ssh/id_ed25519", localSecretsPath, fmt.Sprintf("jonathan@%s:~", hostname)) - - // fmt.Println(cmd.String()) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - if err := cmd.Run(); err != nil { - errs <- fmt.Errorf("failed to run command: %v", err) - - return - } - fmt.Printf("sent %s to %s\n", localSecretsPath, hostname) - - errs <- nil - }(i) - // scp -i ~/.ssh/id_ed25519 jonathan@t-gcp-threshsignnet-asia-01.gcp.testnet.xlabs.xyz:~ - } - - timer := time.NewTimer(time.Second * 90) - defer timer.Stop() - - for i := 0; i < len(cnfg.Peers); i++ { - select { - case err := <-errs: - if err != nil { - t.Fatalf("failed to send file: %v", err) - - return - } - case <-timer.C: - t.Fatalf("timed out waiting for file transfer") - - return - } - } - - fmt.Println("done sending files") -} - -func loadConfigs(t *testing.T) SetupConfigs { - bts, err := os.ReadFile(saveFile) - if err != nil { - t.Fatalf("failed to read file: %v", err) - } - var cnfg SetupConfigs - if err := json.Unmarshal(bts, &cnfg); err != nil { - t.Fatalf("failed to unmarshal config: %v", err) - } - return cnfg -} - -func (d dkgTest) createLKGConfigs(t *testing.T) { - cnfg := d.createLKG(t) - - // store - bts, err := json.MarshalIndent(cnfg, "", " ") - if err != nil { - t.Fatalf("failed to marshal config: %v", err) - } - - if err := os.WriteFile(path.Join(d.saveFolder, "lkg.json"), bts, 0644); err != nil { - t.Fatalf("failed to write file: %v", err) - } -} - -func (d dkgTest) createDKGConfigs(t *testing.T) { - mainCnf := d.createLKG(t) - - for i := range d.hostnames { - output := fmt.Sprintf("guardian%d", i) - cnfg := SetupConfigs{ - NumParticipants: mainCnf.NumParticipants, - WantedThreshold: mainCnf.WantedThreshold, - Self: mainCnf.Peers[i], - SelfSecret: mainCnf.Secrets[i], - StorageLocation: output, - Peers: mainCnf.Peers, - } - - bts, err := json.MarshalIndent(cnfg, "", " ") - if err != nil { - t.Fatalf("failed to marshal config: %v", err) - } - - if err := os.MkdirAll(d.saveFolder, 0777); err != nil { - t.Fatalf("failed to create directory: %v", err) - } - - savepath := path.Join(d.saveFolder, strconv.Itoa(i)+".json") - if err := os.WriteFile(savepath, bts, 0644); err != nil { - t.Fatalf("failed to write file: %v", err) - } - } -} - -func (d dkgTest) createLKG(t *testing.T) SetupConfigs { - hostnames := d.hostnames - - mainCnf := SetupConfigs{ - NumParticipants: len(hostnames), - WantedThreshold: 2*(len(hostnames)/3) + 1, - Peers: make([]Identifier, len(hostnames)), - Secrets: make([]engine.PEM, len(hostnames)), - SaveLocation: make([]string, len(hostnames)), - } - - for i, hostname := range hostnames { - port := 8998 - if d.forLocalDKG { - port += i - hostname = "localhost" - mainCnf.SaveLocation[i] = fmt.Sprintf("guardian%d", i) - } else { - mainCnf.SaveLocation[i] = extractRegion(hostname) - } - sk, cert := createTLSCert(hostname) - mainCnf.Peers[i] = Identifier{ - Hostname: hostname, - TlsX509: cert, - Port: port, - } - - mainCnf.Secrets[i] = internal.PrivateKeyToPem(sk) - } - return mainCnf -} - -func (d dkgTest) RunDKG(t *testing.T) { - errs := make(chan error, len(d.hostnames)) - - initialWd, err := os.Getwd() - if err != nil { - t.Fatalf("failed to get working directory: %v", err) - } - defer func() { - if err := os.Chdir(initialWd); err != nil { - t.Fatalf("failed to restore working directory: %v", err) - } - }() - - if err := os.Chdir(d.saveFolder); err != nil { - t.Fatalf("failed to change working directory: %v", err) - } - - for i := range d.hostnames { - serverPath := "server.go" - configPath := strconv.Itoa(i) + ".json" - // call go run ./dkg --config= - - args := []string{ - "run", - serverPath, - "-cnfg", configPath, - } - - // in parallel - cmd := exec.Command("go", args...) - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - if err := cmd.Start(); err != nil { - t.Fatalf("failed to start command: %v", err) - } - - fmt.Println("running command:", cmd.String()) - - go func(cmd *exec.Cmd, index int) { - // Wait for command to finish and send error (if any) back to the main goroutine. - if err := cmd.Wait(); err != nil { - errs <- fmt.Errorf("command %d failed: %v", index, err) - return - } - errs <- nil - }(cmd, i) - } - - fmt.Println("all commands started, waiting for results...") - time.Sleep(time.Second) - // collect results and fail from the test goroutine if any command failed - for i := 0; i < len(d.hostnames); i++ { - if err := <-errs; err != nil { - t.Fatalf("failed running dkg: %v", err) - } - } - - if !d.storeIntoInternalTestData { - return - } - // store the guardians into internal/testutils/testdata/dkg5 - mainFolder := "tss5" - resultDir := path.Join("..", "..", "..", "..", "internal", "testutils", "testdata", mainFolder) - cleanResultFolder(t, resultDir) - - for i := range d.hostnames { - saveLocation := fmt.Sprintf("guardian%d", i) - _path := path.Join(d.saveFolder, saveLocation, "secrets.json") - - //read the file into a GuardianStorage struct - gst, err := engine.NewGuardianStorageFromFile(_path) - if err != nil { - t.Fatalf("failed to read guardian storage from file: %v", err) - } - - // store into result dir: - filename := fmt.Sprintf("guardian%d.json", i) - filepath := path.Join(resultDir, filename) - bts, err := json.MarshalIndent(gst, "", " ") - if err != nil { - t.Fatalf("failed to marshal guardian storage: %v", err) - } - if err := os.WriteFile(filepath, bts, 0644); err != nil { - t.Fatalf("failed to write guardian storage to file: %v", err) - } - } -} - -func extractRegion(domain string) string { - parts := strings.Split(domain, "-") - if len(parts) < 5 { - panic("unexpected format") - } - - regionPart := parts[3] // "euw" - rest := strings.Join(parts[4:], "-") // "01.gcp.testnet.xlabs.xyz" - dotParts := strings.Split(rest, ".") - if len(dotParts) == 0 { - panic("unexpected format after region") - } - - return fmt.Sprintf("%s-%s", regionPart, dotParts[0]) -} - -func createTLSCert(hostname string) (*ecdsa.PrivateKey, engine.PEM) { - cert := createX509Cert(hostname) - sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - panic(err) - } - - signedCert := internal.NewTLSCredentials(sk, cert) - return sk, internal.CertToPem(signedCert) - -} -func createX509Cert(hostname string) *x509.Certificate { - // using random serial number - var serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128) - - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - panic(err) - } - - tmpl := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{Organization: []string{"tsscomm"}}, - SignatureAlgorithm: x509.ECDSAWithSHA256, - NotBefore: time.Now(), - NotAfter: time.Now().Add(time.Hour * 24 * 366 * 40), // valid for > 40 years used for tests... - BasicConstraintsValid: true, - - DNSNames: []string{hostname, "localhost"}, - IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1)}, - } - return &tmpl -} diff --git a/node/pkg/tss/internal/cmd/setkey/key.example.pem b/node/pkg/tss/internal/cmd/setkey/key.example.pem deleted file mode 100644 index a337c37df5..0000000000 --- a/node/pkg/tss/internal/cmd/setkey/key.example.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGs/YPcShygXE0WC4 -oRSjU2Sd8P4NKl+6ieZcbI1rAGuhRANCAAT7J9S94+jSax2UooCmnfw7jGsGwNy+ -kBCdd8gAIEyuvbdI3MHERhXTx9eBW+64UkMThCygEbtGiICC8Vk1Cg3K ------END PRIVATE KEY----- - - - - diff --git a/node/pkg/tss/internal/cmd/setkey/lkg.example.json b/node/pkg/tss/internal/cmd/setkey/lkg.example.json deleted file mode 100755 index 6f46bca120..0000000000 --- a/node/pkg/tss/internal/cmd/setkey/lkg.example.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "MaxSignerTTL": 0, - "DelayGraceTime": 0, - "ChainsWithNoSelfReport": null, - "Self": { - "id": "0", - "key": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFK3lmVXZlUG8wbXNkbEtLQXBwMzhPNHhyQnNEYwp2cEFRblhmSUFDQk1ycjIzU056QnhFWVYwOGZYZ1Z2dXVGSkRFNFFzb0JHN1JvaUFndkZaTlFvTnlnPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==", - "index": 0 - }, - "TlsX509": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJvakNDQVVlZ0F3SUJBZ0lRUXg2NUhRRjB0Q3I2VmxVWXo2VWEzekFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJME1Ea3lNakEzTVRjeU1sb1lEekl3TmpReE1ESXlNRGN4TnpJeQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCit5ZlV2ZVBvMG1zZGxLS0FwcDM4TzR4ckJzRGN2cEFRblhmSUFDQk1ycjIzU056QnhFWVYwOGZYZ1Z2dXVGSkQKRTRRc29CRzdSb2lBZ3ZGWk5Rb055cU45TUhzd0RnWURWUjBQQVFIL0JBUURBZ0tFTUIwR0ExVWRKUVFXTUJRRwpDQ3NHQVFVRkJ3TUJCZ2dyQmdFRkJRY0RBakFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQjBHQTFVZERnUVdCQlJMCnByNHdWZ0FEeVVNZXNiVVJFWWJ3N215bDB6QWFCZ05WSFJFRUV6QVJnZ2xzYjJOaGJHaHZjM1NIQkg4QUFBRXcKQ2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQVBVejY3Q3lKb0lLclNDSlJQcCtNQktFWkkvUTFtK3JlS0RqYzNBVQpXZkhBQWlFQWxIOXpJZjNoZ2hBS3dCcCt1MlB6L05TLzZYSm96UGQ5ZFpnR1dqeHdSM2c9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "PrivateKey": null, - "Guardians": [ - { - "id": "0", - "key": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFK3lmVXZlUG8wbXNkbEtLQXBwMzhPNHhyQnNEYwp2cEFRblhmSUFDQk1ycjIzU056QnhFWVYwOGZYZ1Z2dXVGSkRFNFFzb0JHN1JvaUFndkZaTlFvTnlnPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==", - "index": 0 - }, - { - "id": "1", - "key": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFSFd3cHRhaTZYZGJ1bklKZHZxRzZBNkxTelNIVgpRSkNoYWU3ZkNLNnR3ZlhvSFdMVE1jeklaa2RzMTZHZnh3Sy9jUkVtQzhjYmxjK2xEQUVxUjNHRTNnPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==", - "index": 1 - }, - { - "id": "2", - "key": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFVHhQK1BGdkVRWjJvSUdvTGtQd1BRNlFKYkw1cgpTdU1CVFVPdGx1V3RoQjVFcGVYQXBqQVdTTTFYb3hVOUQwR2ZXYW1ocnI0ZmFFZ1crZSt3Ty9UWk5RPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==", - "index": 2 - }, - { - "id": "3", - "key": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFVTA2VVBMOFQvVHdPWVBDelZTbUpmSXQ3QkVFQQo4WDlDMCtlY1VZYWlyOW5GS1ozZnBJNy9pVGcyMTFkQW04QW8xSDFsam9oK3ROYy8ySG5vMWhnb2pRPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==", - "index": 3 - }, - { - "id": "4", - "key": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFYkQ1Z1RKa0trKzNPYVpETldCL05EcWp4dk42ZApET0lvL3lBMms0NkxKbXJSakVacEJDcDFhYVZpSzk5Sm1IWG05Yis5akVad0NHL01LZlVpUmYyMEVBPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==", - "index": 4 - } - ], - "GuardianCerts": [ - "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJvakNDQVVlZ0F3SUJBZ0lRUXg2NUhRRjB0Q3I2VmxVWXo2VWEzekFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJME1Ea3lNakEzTVRjeU1sb1lEekl3TmpReE1ESXlNRGN4TnpJeQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCit5ZlV2ZVBvMG1zZGxLS0FwcDM4TzR4ckJzRGN2cEFRblhmSUFDQk1ycjIzU056QnhFWVYwOGZYZ1Z2dXVGSkQKRTRRc29CRzdSb2lBZ3ZGWk5Rb055cU45TUhzd0RnWURWUjBQQVFIL0JBUURBZ0tFTUIwR0ExVWRKUVFXTUJRRwpDQ3NHQVFVRkJ3TUJCZ2dyQmdFRkJRY0RBakFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQjBHQTFVZERnUVdCQlJMCnByNHdWZ0FEeVVNZXNiVVJFWWJ3N215bDB6QWFCZ05WSFJFRUV6QVJnZ2xzYjJOaGJHaHZjM1NIQkg4QUFBRXcKQ2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQVBVejY3Q3lKb0lLclNDSlJQcCtNQktFWkkvUTFtK3JlS0RqYzNBVQpXZkhBQWlFQWxIOXpJZjNoZ2hBS3dCcCt1MlB6L05TLzZYSm96UGQ5ZFpnR1dqeHdSM2c9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJvVENDQVVlZ0F3SUJBZ0lRVkNTcndsY1hnWnJrTWRzbFYwamk1VEFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJME1Ea3lNakEzTVRjeU1sb1lEekl3TmpReE1ESXlNRGN4TnpJeQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCkhXd3B0YWk2WGRidW5JSmR2cUc2QTZMU3pTSFZRSkNoYWU3ZkNLNnR3ZlhvSFdMVE1jeklaa2RzMTZHZnh3Sy8KY1JFbUM4Y2JsYytsREFFcVIzR0UzcU45TUhzd0RnWURWUjBQQVFIL0JBUURBZ0tFTUIwR0ExVWRKUVFXTUJRRwpDQ3NHQVFVRkJ3TUJCZ2dyQmdFRkJRY0RBakFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQjBHQTFVZERnUVdCQlRMCmtJSU95R2p4bGNIaHRnbWFxNm9PZS92WmhEQWFCZ05WSFJFRUV6QVJnZ2xzYjJOaGJHaHZjM1NIQkg4QUFBRXcKQ2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnUkVUSEhyZXNhRndoOW1udm9UZWNlNGFoWGJBWnFnSmFwVWdDSTF0YgovTXNDSVFDTVM4M25hRGNTL2lNVkc2Y3pqT3JiQnkyT3ZITnc5YUhDSm04V29MUWhDZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJvVENDQVVpZ0F3SUJBZ0lSQUlxZ2NqN29zeWg3Y0VZb1RtdTB4cTB3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5EQTVNakl3TnpFM01qSmFHQTh5TURZME1UQXlNakEzTVRjeQpNbG93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJFOFQvanhieEVHZHFDQnFDNUQ4RDBPa0NXeSthMHJqQVUxRHJaYmxyWVFlUktYbHdLWXdGa2pOVjZNVlBROUIKbjFtcG9hNitIMmhJRnZudnNEdjAyVFdqZlRCN01BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVQpCZ2dyQmdFRkJRY0RBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVCkpya1E4ZzA4WDVFcGI4T1JsalUyb2l0WTJMd3dHZ1lEVlIwUkJCTXdFWUlKYkc5allXeG9iM04waHdSL0FBQUIKTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUJ1VnA3VE9xZXBlOEpvSnZxQTk2bnFIYzVHME9ucHZQa0t6dzJucgo2d3ExQWlCSXlJaHlpL0xVK1RWUDd3Z2JJVXpjMlJXeXZpSklDL0h0YW1ua3FxYWQ2QT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJvakNDQVVpZ0F3SUJBZ0lSQUxieVduSHQ2aHpRaDZMb2Q5bzBQemt3Q2dZSUtvWkl6ajBFQXdJd0VqRVEKTUE0R0ExVUVDaE1IZEhOelkyOXRiVEFnRncweU5EQTVNakl3TnpFM01qSmFHQTh5TURZME1UQXlNakEzTVRjeQpNbG93RWpFUU1BNEdBMVVFQ2hNSGRITnpZMjl0YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBCkJGTk9sRHkvRS8wOERtRHdzMVVwaVh5TGV3UkJBUEYvUXRQbm5GR0dvcS9aeFNtZDM2U08vNGs0TnRkWFFKdkEKS05SOVpZNklmclRYUDloNTZOWVlLSTJqZlRCN01BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVQpCZ2dyQmdFRkJRY0RBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVClF4R3ZBOHVNTjF5NUFRRGMweTc2QjMrQ256OHdHZ1lEVlIwUkJCTXdFWUlKYkc5allXeG9iM04waHdSL0FBQUIKTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSUdSUS9UdTRFQWtZRFhtWW5LeHRnR0JlQjhYT2ZIa2dOWGhGM1NXTApyRzQ2QWlFQXpHTzJlWklmenVlQytFQ05XWXBzdkFwK2F0emxXQlNkNmVUdDdKZGMwczQ9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJvRENDQVVlZ0F3SUJBZ0lRSkVGQ2RBN3ZDZE5mbHExUzZQTmJzVEFLQmdncWhrak9QUVFEQWpBU01SQXcKRGdZRFZRUUtFd2QwYzNOamIyMXRNQ0FYRFRJME1Ea3lNakEzTVRjeU1sb1lEekl3TmpReE1ESXlNRGN4TnpJeQpXakFTTVJBd0RnWURWUVFLRXdkMGMzTmpiMjF0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFCmJENWdUSmtLayszT2FaRE5XQi9ORHFqeHZONmRET0lvL3lBMms0NkxKbXJSakVacEJDcDFhYVZpSzk5Sm1IWG0KOWIrOWpFWndDRy9NS2ZVaVJmMjBFS045TUhzd0RnWURWUjBQQVFIL0JBUURBZ0tFTUIwR0ExVWRKUVFXTUJRRwpDQ3NHQVFVRkJ3TUJCZ2dyQmdFRkJRY0RBakFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQjBHQTFVZERnUVdCQlRKCnRnMGZzeU45NmN3THhzdWZONjJRUnRZWHpqQWFCZ05WSFJFRUV6QVJnZ2xzYjJOaGJHaHZjM1NIQkg4QUFBRXcKQ2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnWlhqelBYeTRsaklsY0swMFFxQ3p3dXROUkN3Wk1kWXNPZ2Q0RHFhQQozK1lDSUdvL2I1ZkEzWmViYmJJclJic3YzWVpOLzlsTXJFbSt1M1JQYmJsSk96L1UKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=" - ], - "Threshold": 2, - "SavedSecretParameters": { - "PaillierSK": { - "N": 28742372743403056606446119372474808413449427671068534972535037456917918264618776189041384821916763483527040599734050224364878628311591430017099199281757380786886595619434239515103945437824279005604694460768572095243383787826599929313843639044222831310637028069986274126797211309487511756328576086119228899073246792142393439718987992767449395625375612093809800566086421926293196039666085419517134761932363174673503603847067457296553729850089089754159590223002317510208127841446920329268690686068704436035232780301545837962441135296307644061706926471399773519934597701663247135126876761960948324517873662168260899797569, - "LambdaN": 14371186371701528303223059686237404206724713835534267486267518728458959132309388094520692410958381741763520299867025112182439314155795715008549599640878690393443297809717119757551972718912139502802347230384286047621691893913299964656921819522111415655318514034993137063398605654743755878164288043059614449536453756152280341586605078211551802428557633455838566884853112277229632222286238692772113663654988577798563456233505176911322304412293678344447784183272725009420248870533102362081159936343761230543197726916880328646146296384576964080782722224071366163570122439851258008989957709575471916377843760062396946956522, - "PhiN": 28742372743403056606446119372474808413449427671068534972535037456917918264618776189041384821916763483527040599734050224364878628311591430017099199281757380786886595619434239515103945437824279005604694460768572095243383787826599929313843639044222831310637028069986274126797211309487511756328576086119228899072907512304560683173210156423103604857115266911677133769706224554459264444572477385544227327309977155597126912467010353822644608824587356688895568366545450018840497741066204724162319872687522461086395453833760657292292592769153928161565444448142732327140244879702516017979915419150943832755687520124793893913044, - "P": 163696069443827813606180105112746710440121253715580006329289108251628733319889988237397522831877637473705753267564899703520296112333316692974296093428516655439886491123142538861786157297472413352699556905550324367590084339945343969596519607313091371554490056368939315703111318716221063305472889088437106965323, - "Q": 175583768388928732171656239233044057820223928417086790050908263582302861773718045735509911790508381602670938112492203770388824913168416372289725763028350835927743609257573066244584656083709561596137769562234856302558458187208371930544962415943949821239862765591791801443850024093783428456713252955029898919203 - }, - "NTildei": 24994956227076674591884630981779172203865768220760105778917045460431009170643871311441325740381986704967509520237213499110913141699438942079330934296123878126221458393813392650257532736876928680850398095409199615880598952523891907259068374895254048501309267640049573545543380216639053563493695198402868892709503073745220760704417028380485128533099521584341387728479986274058268348442098945185310097195030571471470817106755808526424436566540792178100286209827489438813061575219552757776063112290932317690177055665269069388128702220510810351664791522505073749040578999517442736351369540093131740218665844400601929011033, - "H1i": 11526299846167502805233157409709956322807827867964346168105161567495505962769290583802951593781158875452039214638586913136558656851308790516993369327227141508267826567991390284880009339652997340869634804163985320406762198256062291400130219786512011992804896098782261698638898241667710792603468416064620729513850598808001058752491325065550388512523154012115705770159875362725387669512478661441756420202125844887044747731530373616555022228016974731458263197972679291566108090919403169837163849583568677074950211629646172533121919666630002575369920133268026408836089615092806448616934341155378719941992162671780303567841, - "H2i": 17273560235315291114336134639363740138803378419998412532943273622594656268909327485893252239743331037794598929323085324054392272548614420980064878976894960170834904113414717602719783912929753700152107107938093193510800827819837757790384199310481187568692363197312902907985138332598410405555774500466466523981876720237225853953554178358552299511673715455708844057439248080122954739648097334001705604615399878963291716182921719835221321121331183567273031245347373435066060697298399530925195813680143947864646548559587508118125818886132808805998728907922198866947386621346393158745875920255737309960343892592361420214283, - "Alpha": 12623909759189234354782709344447179955821750915902924858051134858340021367472066297086217673069716716001956923878665729685207992549231198826652649461106845448520202412752582494124149866709369064038806264270881689567648123177979717730265255179380437476831695954784375966371787919795473293938100534063790928051316661968245401683510792338050083063858566247098471437345651982954654795544138052851964210108028576812129699453453803755052784875261705193395649189493629035399808116761557495293627031445845771737951891098118933901329286496317867549571614105707574659689461212741944674059512038108644237682160159240091500138028, - "Beta": 2086218188374386808887068569539969719378436958037450456684522371222276637033831681617724711460297595517627302062028519318276323310029965444372594479302692057400983438541790774051512204792224345838921438164054580164046120314260751032142710692011267031671798513965800662313503667865100227891310447816112359066641285723342857231859267734796312127597012531138390537615905718109297090748476622958600900635835595546103689187359422561548241534362583520207482573737241034832207282032921494841502220441019786363693709032944900686496562334197141443779196442065464995447532311734071982598253501248011745439265150094325704697903, - "P": 78775730952014193318302536775449146851601886586086033230023647284028148459013090513468316501830825560421509773063982140131108986521138463153042078596430722908660161793137913999131904229102833589272348386731848861668622368117594313563915141315209070260408681460710009539905818503485276911064236359483710056013, - "Q": 79323149163484804084059739133372629543288998300139315685220683931733730663727749668413370674516083584589180688610933198063900981135262183404600501639571332963979140202851401877249014993354661644528642188579602510417771051534317171002475179782823170818691938983844075139520304026803664865781413953053342773389, - "Xi": 111059855070125419421085987285709737120492413266877548944519171929378436689842, - "ShareID": 81919286088315161017690744477278249499916188325636267709352456217873077245109274785919807003142190095279372263929830995459357750902880001291812059437191878195106397355837540337645869225475053333634108295126846989860294639401897224216127706620144253449271973898650415180609062777048688277653182267613657828163573342402839291236815717344955543555134072117948802595719767156460818639256727371836746167930614746714645725152411462922, - "Ks": [ - 81919286088315161017690744477278249499916188325636267709352456217873077245109274785919807003142190095279372263929830995459357750902880001291812059437191878195106397355837540337645869225475053333634108295126846989860294639401897224216127706620144253449271973898650415180609062777048688277653182267613657828163573342402839291236815717344955543555134072117948802595719767156460818639256727371836746167930614746714645725152411462922, - 81919286088315161017690744477278249499916188325636267709352456217873077245109274785919807003142190095279372263929830995459357750902880001291812059437192877652868890400737131507071178782909387946993799568251705901348854078766228591027905098367083525586162002879365955096018541794896470925564794118903358442341427838773054546292103860866945524740958863639080618448271401914604805987661317850319637239960638961693745949298656095498, - 81919286088315161017690744477278249499916188325636267709352456217873077245109274785919807003142190095279372263929830995459357750902880001291812059437193297562879629869247185370776478619760054682797283897211689520350939182390182455023027245257245471272875662862323780512889531883643668990511177798500827575064951105162021759313711239402186521007462290254164572022754944020262278744943274564769838966962557827139841407640411122954, - 81919286088315161017690744477278249499916188325636267709352456217873077245109274785919807003142190095279372263929830995459357750902880001291812059437193322434016240176923893121857087862365910980527134687626109124044096294887689879303078493618273296794161884602283592346846724904099323884092389657767775567843751422603243988818925816264901113703906429114110767266684948994562638883781027350993227596937472664292824385986314972426, - 81919286088315161017690744477278249499916188325636267709352456217873077245109274785919807003142190095279372263929830995459357750902880001291812059437193775228428225547893957050310601237422323058022810108742597434986300885882660527295120504556833780062226131181091004931027380627530908454858353555462536472685047279406375506229518023019227255725505397151328465202090056527734171611661367407895461932566100119510894286186394496266 - ], - "NTildej": [ - 24994956227076674591884630981779172203865768220760105778917045460431009170643871311441325740381986704967509520237213499110913141699438942079330934296123878126221458393813392650257532736876928680850398095409199615880598952523891907259068374895254048501309267640049573545543380216639053563493695198402868892709503073745220760704417028380485128533099521584341387728479986274058268348442098945185310097195030571471470817106755808526424436566540792178100286209827489438813061575219552757776063112290932317690177055665269069388128702220510810351664791522505073749040578999517442736351369540093131740218665844400601929011033, - 24370705801330534830534039391881256875941059886912860175099956638073221806786646621087968337706327167295065214839215939412630872599590444402212732484845057863896248925986118101983154167813040877726333722773161637046022331071172636891330011854907179704458889586513024199084002278340108049975972308392180204926350728053997254751198786043684317198309607248564655615934415214736455342302903654731386256823966620298603151651875331297162629710755217288037979503713748412470240867431159706924972092291761451633584801352094885393282094593692214503630081481740448451564692094435146983691848743285330322951160195864939092791821, - 24384957895511316224437059156783196377160674452373442709363293777830464259247840168702215969722588683804536189506016665255930964374334605337486507447398605217544577567878133154911496677378952347173594946552329163460314213135560622240329490947250490761444989173794791220662338336123538601895849274458597475574312668034020865383239546725231013743158566040435246081569861228517628765645639965252445833377934449866278148531192214223167524479700804232046929029577267590823261625216386106659208521207831411230502027003665184407334078784297685084645373591438137370018175018358205340006150232770280880878441374133038244514461, - 23157928813825451606094459978369966072618713627460400675676351690549774357798086323388690179904170435793783800092479755369286004146392260209842558445915223132430354725203058581914181485116017558059562722948787909164575249328546682126108266826500154900746928801704913439106965184103979508264369418746200735004414858620503094423819434066958067052008377963224066978653095333869940859157165318045928124169285512606705188407171745189003134089902883834067101182725348184171895304027948430476371637381026397296431291969528423845689866113775785995691314051673421528807214979115102880038491202910710047703960033173700627219317, - 23398812734887556921722625506643684460465369838850937754986557172097894704821315311402049103292618378421119568231340368597198682912767227790817495566843557624352286795949662394816578993811827848992800392583090745588863568300263933484627125371957541499159440307738010201499390471556902538438651528711152516833151800096619733383819888241344159820856911051655827545774689166352855876233600741246026721740989539362916588736027031838998510232714595823463324776187004395595451366006503889021583429842604389264443656005907897045337632120627586392793430658693116149130984557475326548797549699350336528076010622218318905860261 - ], - "H1j": [ - 11526299846167502805233157409709956322807827867964346168105161567495505962769290583802951593781158875452039214638586913136558656851308790516993369327227141508267826567991390284880009339652997340869634804163985320406762198256062291400130219786512011992804896098782261698638898241667710792603468416064620729513850598808001058752491325065550388512523154012115705770159875362725387669512478661441756420202125844887044747731530373616555022228016974731458263197972679291566108090919403169837163849583568677074950211629646172533121919666630002575369920133268026408836089615092806448616934341155378719941992162671780303567841, - 7485674387115688685583945947596738852330895084894800871907779544383272469162139903834420036443394132111581959656140001932441799355714832171647343859701890450244345641116653646956926136981948455163070051594940103888499345247193748028794477596188319262605684887024406019659572800305344573299280128203905772738417822155768405247511681395256941541414433197779810326175084870753196799448655214321612790991689181877346804384422383227840247794689836333599862570269075735629642524490998100592729352786756682826613054292041379784704017375004057847553788865461193907017580843437149810254468514873368518048820963897485186729888, - 4986271205495688383957316937096523882259677196680884428067461004285092883736970360297576340747186753514642388953115290148403588182994758724029056736036094996351340290321377091941570644851509680973562538387954655861183566219440737401170358755147086126339486387000577354990562379334459908125723828421042483863499849289944944079654516203502463480832939488783880387269781538221815507749984231468396083036274485439935564605505714571369753544809527012342084562679097618151379856298082840390514579109134598594921026245874499250632328496327621292345579462578913579312978724942451319591666131193358548282398561279066982536972, - 19110993291555769291775903691017186128573677817312893259590790717801011373427612873966739970432810899039079262042176441732768371921382399531696775145371496367869178495593924422016790484706351097562574384782386706253379215708853982343711649440673542959622153586731190431454119517591873426018741282984854545106253617303692232138421733639640476351200045117299517172494144724048872280163799212768157944848968580971663943819077726596565876421966594513336771115264035136516146298710095227621631122390843438894055354965055142998644279994621628438082769149173533469849373153509890252160503027920370532555624527328620230717773, - 3184872235072818218328411637294933900216679855100334360652656794280390694063581768302717509647564678430240978457996004659501297541637849024866452759297370542337354401044832978398612338708437667540320440351955799958119088700776939215346484179667018706119703360365193776446058597923546036708853737830230376773707454430892806652783739504891994718901000458213546279230093597466483563082668338298593441337099473345066036861730283618095899874845504504391535136875489857472041201782778162511904803670780276968361183936868487720672931412618711672784124228316813147780315468310077623707233871408207623664987912050577777431102 - ], - "H2j": [ - 17273560235315291114336134639363740138803378419998412532943273622594656268909327485893252239743331037794598929323085324054392272548614420980064878976894960170834904113414717602719783912929753700152107107938093193510800827819837757790384199310481187568692363197312902907985138332598410405555774500466466523981876720237225853953554178358552299511673715455708844057439248080122954739648097334001705604615399878963291716182921719835221321121331183567273031245347373435066060697298399530925195813680143947864646548559587508118125818886132808805998728907922198866947386621346393158745875920255737309960343892592361420214283, - 22375866997060572074967321233402861678866114641885789713861217124261054570421889917976132766998798452225510219705017765657738620249716884522716915248760406455971735136701523143766350339714429757170559386227365538338115335503646160920147494068613825724138021122694685666501542900449444545922980501725738746790214478428620647038834457901735696263228154970747166934366948839682016940591404531098893199928666735298689827349203815986230942709462612628298003827026304798789916169198085704784934149895088686894572021773399759479822894886610519523857437780777089114300417767157156843950888666889656974353153934293979785893490, - 13516002517032327150551170998348222984157176364475027395401791051970621957542689624603116085802102416397133044927310951411842764793445373607032527569246903158235499443625577902499231335274205909057933040128717430906251343547070330915178032594891086608279038961811363562573784294960647534667687311769793777150743376805322862428895145520849539954277157802377369028722284923007922771798688362166483288610821482801024526306480695349921297032736745754952543319537078917177038586194948155403436336887838382484637311292968067839652097743834865565503309116850736077004511297260490335203172123633519837155234475567857260119317, - 19576463193879774758637242537728127773935820761281679264122914720280373100430203554164408051074053170336365988324046424899027082025368315867824463163497574019490367107117843819031364960697535633170216782130681534039419885888072698852124466375080425895094488576437734028976708432084995950187743726310094138254130130020766145658464972337756344993097288239392922908105864341171155514314674557268831583712700514833961407563077379758523488786737723196806190469686210604639272556394174015744014888173694098379185366988604417878235709450047427215910370130876309825508697857785957644744016302358768707733513857649593170638463, - 2110559602576114323663379948172731075861111768741302424334328289201486138594751018658476891683764538630994476420166245943378470656214907072575991573898327051307592826570817510804156481926172719231044338470927990407125602799807364296165012983937603823199785670339625130920377291097622958647103152443641883214225871845681041242215559303312252270494909836814761228416044335400805323918699277732064205458295651795317098194898605329933926651713395230391035054130686567535742699998376323527517797233535697931608374208748468594391739979313527093358818667090268595712675653433705850967694552360652254551525650738537142545157 - ], - "BigXj": [ - { - "Curve": "secp256k1", - "Coords": [ - 112079895186571273534424550486699877219473033814038222230843781037882199094461, - 62517718587753738003457543262509578687764139250581201296332708463150365517596 - ] - }, - { - "Curve": "secp256k1", - "Coords": [ - 36873316030799119142398293876678794854365330225027308055321251707741349530791, - 21102113440520057625599379914575963878827264997454368586065143173771610236561 - ] - }, - { - "Curve": "secp256k1", - "Coords": [ - 57637669429528654132101308398926140346705986094479792374290659367907163315104, - 27556224039161592774727209821004584135444551760191873249067720019749644111212 - ] - }, - { - "Curve": "secp256k1", - "Coords": [ - 48007217746445374414345754050662807189649462020328147615088205917621349437627, - 4100388086984785270549160580341161479914011418603602760825277685774244334365 - ] - }, - { - "Curve": "secp256k1", - "Coords": [ - 45184866829243441850626518020803410348472683857601499131267069034793654961986, - 25726744969942303340051497301183730214550033051278276544147462776623093780214 - ] - } - ], - "PaillierPKs": [ - { - "N": 28742372743403056606446119372474808413449427671068534972535037456917918264618776189041384821916763483527040599734050224364878628311591430017099199281757380786886595619434239515103945437824279005604694460768572095243383787826599929313843639044222831310637028069986274126797211309487511756328576086119228899073246792142393439718987992767449395625375612093809800566086421926293196039666085419517134761932363174673503603847067457296553729850089089754159590223002317510208127841446920329268690686068704436035232780301545837962441135296307644061706926471399773519934597701663247135126876761960948324517873662168260899797569 - }, - { - "N": 21614654482706356427067242793456396367321871825795049988810585563283688111463562384269466504548917248842238126738326068188676351432764921523821598013873035293312949944167617549606879146614459092209946501461101249487674672886382813063555340452181216912173263688118363416750374836670043289602600314777594809980789020582341790453282265957155850216962022256959097810056476789004177460122881110764237298231977722241407548680264136109348942680845268907615507690018942723574383658507007068672105229245403699583724329118631999535727556475153236040733328528925072760381264282652545923726627511235372151348182615978486638639497 - }, - { - "N": 25103570832949059939787870586033840610967133040297795700355041117196689203757273856502210650234589237849574099180521682355435169940214830395469805172903822609132464242873554495378118741326473103647846260688789536476599502273587843278065363902214987484784607384564225398265505380179508573538480814374833644551412344302258018966069040949007606426181519234602123174847038837768077826069236918390912529246042861237950977764541331502837907388224517177360592248307819909981546744183929911793566385212487239650313788760748875238626122421229305499143082441403270351523044568733949276934332086560593900672273438954784181078101 - }, - { - "N": 22288003499634757010373575826955892152734003419252888095085333727025943720790328064904262186637031603790054800278987978584193555497726257007205676498171288541490686713491764724445417553093505084296477094490812969334651002948849031751809247842588348012286160385252033955276443773834480688756917103278046477087765787525246911538480026693514138910666937520345440729627692903395115935374217871701872737567452562781826733117261862826912753941756899099727194297161103114138484949078824984606361467563108678090821609850920448616355213755684238535508801334351523548908015991629467225106000363202830479374421423778639670339417 - }, - { - "N": 22643791568468160545040597286946500363798101532874726310694292287165491294130124870014503035827337632041430238121330329641704733560631721980546572866324705366758897929760104209558920804086163526462968355169301371123058881582668188182574234852878222419849992395820430449999296852887969251251543611958747296841013975701639873305193357504994065833353691096573669427708706937650642837490273344648369414828995524071554332498480169002212042001974963989396443079401906683605056572161969108519451645041854212725705184854252070012523741857579924998362120594941598430738675058851049782018109589641808877665823540408632916548113 - } - ], - "ECDSAPub": { - "Curve": "secp256k1", - "Coords": [ - 86293567520138067444305578294175506340339061981965738717798349041858088535645, - 79986243107625842359605534889585972984019842226666709414446860472822997234728 - ] - } - }, - "LoadDistributionKey": "PKsqx0KPF2m2rHB8nxmCkomlnm23MRya9dZSq3grVls=" -} \ No newline at end of file diff --git a/node/pkg/tss/internal/cmd/setkey/main.go b/node/pkg/tss/internal/cmd/setkey/main.go deleted file mode 100644 index 591445eccc..0000000000 --- a/node/pkg/tss/internal/cmd/setkey/main.go +++ /dev/null @@ -1,60 +0,0 @@ -// used to finish the DKG setup bysetting the private key into the secrets.json generatedby local DKG. - -package main - -import ( - "encoding/json" - "flag" - "fmt" - "os" - - "github.com/certusone/wormhole/node/pkg/tss" - "github.com/certusone/wormhole/node/pkg/tss/internal" -) - -var secretkeypath = flag.String("key", "", "path to the secret key PEM file") -var lkgSecrets = flag.String("lkg", "", "path to the LKG secrets json file") - -func main() { - flag.Parse() - - if *lkgSecrets == "" || *secretkeypath == "" { - flag.PrintDefaults() - - return - } - - fmt.Println("loading lkg secrets from: " + *lkgSecrets) - gsbts, err := os.ReadFile(*lkgSecrets) - if err != nil { - panic("couldn't load secrets from key generation protocol " + err.Error()) - } - var gs tss.GuardianStorage - json.Unmarshal(gsbts, &gs) - - fmt.Println("loading key from: " + *secretkeypath) - bts, err := os.ReadFile(*secretkeypath) - if err != nil { - panic("issue reading secret key file" + err.Error()) - } - - // Cleaning up the pem file - k, err := internal.PemToPrivateKey(bts) - if err != nil { - panic("issue parsing private key" + err.Error()) - } - - bts = internal.PrivateKeyToPem(k) - - gs.PrivateKey = bts - - fmt.Println("embbeding key into lkg secrets file") - secrets, err := json.MarshalIndent(gs, "", " ") - if err != nil { - panic("issue marshalling secrets" + err.Error()) - } - - if err := os.WriteFile(*lkgSecrets, secrets, 0600); err != nil { - panic("couldn't write to file" + err.Error()) - } -} diff --git a/node/pkg/tss/internal/marshal_test.go b/node/pkg/tss/internal/marshal_test.go deleted file mode 100644 index e8c5c77bbb..0000000000 --- a/node/pkg/tss/internal/marshal_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package internal - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "testing" - - "github.com/test-go/testify/assert" -) - -func TestMarshalSecretKey(t *testing.T) { - a := assert.New(t) - sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - a.NoError(err) - - bz := PrivateKeyToPem(sk) - unmarshaled, err := PemToPrivateKey(bz) - a.NoError(err) - - a.True(sk.PublicKey.Equal(&unmarshaled.PublicKey)) - a.True(sk.Equal(unmarshaled)) - -} - -func TestMarshalPK(t *testing.T) { - a := assert.New(t) - sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - a.NoError(err) - - bz, err := PublicKeyToPem(&sk.PublicKey) - a.NoError(err) - - unmarshaled, err := PemToPublicKey(bz) - a.NoError(err) - - a.True(sk.PublicKey.Equal(unmarshaled)) -} diff --git a/node/pkg/tss/internal/timed_heap.go b/node/pkg/tss/internal/timed_heap.go deleted file mode 100644 index 990ec1a978..0000000000 --- a/node/pkg/tss/internal/timed_heap.go +++ /dev/null @@ -1,125 +0,0 @@ -package internal - -import ( - "container/heap" - "time" -) - -type HasTTL interface { - GetEndTime() time.Time -} - -type Ttlheap[T HasTTL] interface { - Enqueue(T) - Dequeue() T - // If the heap is not empty, this will fire once per element in the queue according to their time. - WaitOnTimer() <-chan time.Time - - Peek() T -} - -type ttlHeap[T HasTTL] struct { - theap timedHeap[T] - timer *time.Timer -} - -// Dequeue implements Ttlheap. -func (t *ttlHeap[T]) Dequeue() T { - var res T - if t.theap.Len() == 0 { - return res - } - - elem := heap.Pop(&t.theap).(T) - t.setTopAsTimer() - return elem -} - -// Enqueue implements Ttlheap. -func (t *ttlHeap[T]) Enqueue(elem T) { - heap.Push(&t.theap, elem) - t.setTopAsTimer() -} - -func (t *ttlHeap[T]) setTopAsTimer() { - if t.theap.Len() == 0 { - t.stopAndDrainTimer() // no elements: stop the timer. - - return - } - - endTime := t.theap.peek().GetEndTime() // we have at least one element. - - t.stopAndDrainTimer() - // endtime.Sub(time.Now()) uses monotonic clock. - - // Notice that if timer.Reset is called with time.Until(endTime) which is negative, it will immediately fire (which is what we want). - t.timer.Reset(time.Until(endTime)) -} - -func (t *ttlHeap[T]) stopAndDrainTimer() { - // stopping the timer, if its channel is not drained: drain it. - if !t.timer.Stop() && len(t.timer.C) > 0 { - select { - case <-t.timer.C: - default: - } - } -} - -// WaitOnTimer implements Ttlheap. -func (t *ttlHeap[T]) WaitOnTimer() <-chan time.Time { - return t.timer.C -} - -func NewTtlHeap[T HasTTL]() Ttlheap[T] { - t := &ttlHeap[T]{ - theap: timedHeap[T]{}, - timer: time.NewTimer(time.Second), // safe creation of timer. we'll drain it soon. - } - - heap.Init(&t.theap) - t.stopAndDrainTimer() - return t -} -func (d *ttlHeap[T]) Peek() T { - return d.theap.peek() -} - -type timedHeap[T HasTTL] struct { - heap []T -} - -func (d *timedHeap[T]) Len() int { - return len(d.heap) -} - -func (d *timedHeap[T]) Swap(i int, j int) { - d.heap[i], d.heap[j] = d.heap[j], d.heap[i] -} - -func (d *timedHeap[T]) Push(x any) { - if v, ok := x.(T); ok { - d.heap = append(d.heap, v) - } -} - -func (d *timedHeap[T]) peek() T { - var res T - if len(d.heap) == 0 { - return res - } - res = d.heap[0] - return res -} - -func (d *timedHeap[T]) Less(i int, j int) bool { - return d.heap[i].GetEndTime().Before(d.heap[j].GetEndTime()) -} - -func (d *timedHeap[T]) Pop() any { - elem := d.heap[len(d.heap)-1] - d.heap = d.heap[:len(d.heap)-1] - - return elem -} diff --git a/node/pkg/tss/internal/timed_heap_test.go b/node/pkg/tss/internal/timed_heap_test.go deleted file mode 100644 index dcd430ca88..0000000000 --- a/node/pkg/tss/internal/timed_heap_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package internal - -import ( - "testing" - "time" -) - -type TestStruct struct { - endTime time.Time -} - -func (t TestStruct) GetEndTime() time.Time { - return t.endTime -} - -func TestFireEvenWithPassedTimes(t *testing.T) { - // test that if we enqueue a bunch of elements, and wait to long to treat them, then it still fires for each one of them. - - heap := NewTtlHeap[TestStruct]() - - heap.Enqueue(TestStruct{endTime: time.Now().Add(time.Microsecond * 50)}) - heap.Enqueue(TestStruct{endTime: time.Now().Add(time.Millisecond * 100)}) - heap.Enqueue(TestStruct{endTime: time.Now().Add(time.Millisecond * 150)}) - - time.Sleep(time.Millisecond * 200) - - failure := time.After(time.Second) - for i := 0; i < 3; i++ { - select { - case <-heap.WaitOnTimer(): - case <-failure: - t.FailNow() - } - - heap.Dequeue() - } -} diff --git a/node/pkg/tss/messages.go b/node/pkg/tss/messages.go deleted file mode 100644 index fc1e6643a1..0000000000 --- a/node/pkg/tss/messages.go +++ /dev/null @@ -1,142 +0,0 @@ -package tss - -import ( - tsscommv1 "github.com/certusone/wormhole/node/pkg/proto/tsscomm/v1" - "google.golang.org/protobuf/proto" -) - -// Echo is a broadcast message. -// can contain a `tsscommv1.Echo` and a list of recipients. -// and implements the `Sendable` interface. -type Echo struct { - Echo *tsscommv1.Echo - Recipients []*Identity -} - -type Unicast struct { - Unicast *tsscommv1.Unicast - Receipients []*Identity -} - -type IncomingMessage struct { - Source *Identity - Content *tsscommv1.PropagatedMessage -} - -func (i *IncomingMessage) hasContent() bool { - return i != nil && i.Content != nil -} - -func (i *IncomingMessage) IsUnicast() bool { - if !i.hasContent() { - return false - } - - _, ok := i.Content.Message.(*tsscommv1.PropagatedMessage_Unicast) - - return ok -} - -func (i *IncomingMessage) toBroadcastMsg() *tsscommv1.Echo { - if !i.hasContent() { - return nil - } - - if echo, ok := i.Content.Message.(*tsscommv1.PropagatedMessage_Echo); ok { - return echo.Echo - } - - return nil -} - -func (i *IncomingMessage) toUnicast() *tsscommv1.Unicast { - if !i.hasContent() { - return nil - } - - if unicast, ok := i.Content.Message.(*tsscommv1.PropagatedMessage_Unicast); ok { - return unicast.Unicast - } - - return nil -} - -func (i *IncomingMessage) IsBroadcast() bool { - if !i.hasContent() { - return false - } - - _, ok := i.Content.Message.(*tsscommv1.PropagatedMessage_Echo) - - return ok -} - -func (i *IncomingMessage) GetNetworkMessage() *tsscommv1.PropagatedMessage { - if i.hasContent() { - return i.Content - } - - return nil -} - -func (i *IncomingMessage) GetSource() *Identity { - if i == nil { - return nil - } - - return i.Source -} - -func newEcho(msg *tsscommv1.SignedMessage, recipients []*Identity) *Echo { - return &Echo{Echo: &tsscommv1.Echo{Message: msg}, Recipients: recipients} -} - -// GetDestinations implements Sendable. -func (e *Echo) GetDestinations() []*Identity { - return e.Recipients -} - -// GetNetworkMessage implements Sendable. -func (e *Echo) GetNetworkMessage() *tsscommv1.PropagatedMessage { - return &tsscommv1.PropagatedMessage{ - Message: &tsscommv1.PropagatedMessage_Echo{Echo: e.Echo}, - } -} - -// IsBroadcast implements Sendable. -func (e *Echo) IsBroadcast() bool { - return true -} -func (e *Echo) cloneSelf() Sendable { - return &Echo{Echo: proto.Clone(e.Echo).(*tsscommv1.Echo)} -} - -func (u *Unicast) IsBroadcast() bool { - return false -} - -// GetDestination implements Sendable. -func (u *Unicast) GetDestinations() []*Identity { - return u.Receipients -} - -// GetNetworkMessage implements Sendable. -func (u *Unicast) GetNetworkMessage() *tsscommv1.PropagatedMessage { - return &tsscommv1.PropagatedMessage{ - Message: &tsscommv1.PropagatedMessage_Unicast{ - Unicast: u.Unicast, - }, - } -} - -func (u *Unicast) cloneSelf() Sendable { - clns := make([]*Identity, 0, len(u.Receipients)) - for _, id := range u.Receipients { - clns = append(clns, id.Copy()) - } - - return &Unicast{ - Unicast: proto.Clone(u.Unicast).(*tsscommv1.Unicast), - Receipients: clns, - } -} diff --git a/node/pkg/tss/metrics.go b/node/pkg/tss/metrics.go deleted file mode 100644 index ceab087884..0000000000 --- a/node/pkg/tss/metrics.go +++ /dev/null @@ -1,110 +0,0 @@ -package tss - -import ( - "fmt" - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/wormhole-foundation/wormhole/sdk/vaa" - - common "github.com/xlabs/tss-common" - "github.com/xlabs/tss-lib/v2/party" - - "go.uber.org/zap" -) - -var ( - // counter of expired. and state it might be bad due to the distributed nature of the system. - // Same with inProgressSigs. - - sigProducedCntr = promauto.NewCounterVec( - prometheus.CounterOpts{ - Name: "wormhole_tss_signature_produced", - Help: "total number of tss signatures produced", - }, []string{"chain_name"}, // followed example from ccq - ) - - tooManySimulSigsErrCntr = promauto.NewCounter( - prometheus.CounterOpts{ - Name: "wormhole_tss_too_many_somultaneous_signatures", - Help: "total number of tss signing requests that were rejected due to too many simultaneous signature requests", - }, - ) - - sigLatency = promauto.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "wormhole_tss_signature_latency", - Help: "Histogram of the times taken to produce a signature", - }, []string{"chain_name"}, //followed example from ccq - ) - - signatureEndingWithError = promauto.NewCounter( - prometheus.CounterOpts{ - Name: "wormhole_tss_signature_ending_with_error", - Help: "total number of tss signatures that ended with an error", - }, - ) -) - -// used for collecting metrics. -type signatureMetadata struct { - timeOfCreation time.Time -} - -func (t *Engine) createSignatureMetrics(vaaDigest []byte, chainID vaa.ChainID) { - key := intoSigKey(party.Digest(vaaDigest), chainID) - t.SignatureMetrics.Store(key, &signatureMetadata{ - timeOfCreation: time.Now(), - }) -} - -func (t *Engine) sigMetricDone(trackid *common.TrackingID, hadIssue bool) { - key := trackingIdIntoSigKey(trackid) - - metrics, ok := t.loadMetric(key, trackid) - if !ok { - return - } - - if hadIssue { - signatureEndingWithError.Inc() - - return - } - - chain := extractChainIDFromTrackingID(trackid) - sigProducedCntr. - WithLabelValues(chain.String()). - Inc() - - latency := time.Since(metrics.timeOfCreation) - sigLatency.WithLabelValues(chain.String()).Observe(float64(latency.Milliseconds())) - - t.SignatureMetrics.Delete(key) - - t.logger.Info("tss signature produced", - zap.String("digest", fmt.Sprintf("%x", trackid.Digest)), - zap.String("chain", chain.String()), - zap.Duration("protocol duration", latency), - ) -} - -func (t *Engine) loadMetric(key sigKey, trackid *common.TrackingID) (*signatureMetadata, bool) { - tmp, loaded := t.SignatureMetrics.Load(key) - if !loaded { - return nil, false - } - - metrics, ok := tmp.(*signatureMetadata) - if !ok { - t.logger.Error("signature metrics stored is of wrong type", - zap.String("digest", fmt.Sprintf("%x", trackid.Digest)), - zap.Any("metrics", tmp), - ) - - return nil, false - } - - return metrics, true -} diff --git a/node/pkg/tss/mock/mock.go b/node/pkg/tss/mock/mock.go new file mode 100644 index 0000000000..3e7f475992 --- /dev/null +++ b/node/pkg/tss/mock/mock.go @@ -0,0 +1,150 @@ +package tssmock + +import ( + "context" + "crypto/rand" + + gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1" + "github.com/certusone/wormhole/node/pkg/tss" + "github.com/wormhole-foundation/wormhole/sdk/vaa" + "github.com/xlabs/multi-party-sig/pkg/math/curve" + "github.com/xlabs/multi-party-sig/pkg/math/sample" + frostsign "github.com/xlabs/multi-party-sig/protocols/frost/sign" + tsscommon "github.com/xlabs/tss-common" + "github.com/xlabs/tss-common/service/signer" +) + +// TODO: this mock should ideally be removed in favor of using the real TSS signer from github.com/xlabs/tss-lib/v2/service. + +// MockSigner is a mock implementation of the Signer interface for testing purposes. +type MockSigner struct { + secret curve.Scalar + pub curve.Point +} + +// GetProtocol implements tss.Signer. +func (m *MockSigner) GetProtocol(int) tsscommon.ProtocolType { + return tsscommon.ProtocolFROSTSign +} + +func (m *MockSigner) Sign(digest []byte, protocol tsscommon.ProtocolType) *signer.SignResponse { + grp := curve.Secp256k1{} + + sig, err := frostsign.SignEcSchnorr(m.secret, digest) + if err != nil { + panic(err) + } + + Rbits, _ := grp.MarshalPoint(sig.R) + Sbits, _ := grp.MarshalScalar(sig.Z) + resp := &signer.SignResponse{ + Response: &signer.SignResponse_Signature{ + Signature: &tsscommon.SignatureData{ + // Signature: []byte{}, + // SignatureRecovery: []byte{}, + R: Rbits, + S: Sbits, + M: digest, + TrackingId: &tsscommon.TrackingID{ + Protocol: uint32(protocol.ToInt()), + Digest: digest, + PartiesState: []byte{}, + AuxiliaryData: []byte{}, + }, + }, + }, + } + return resp + +} + +// GetPublicKey implements tss.Signer. + +// NewMockSigner returns a new MockSigner. +func NewMockSigner() *MockSigner { + crv := curve.Secp256k1{} + var secretKey curve.Scalar + var pubkey curve.Point + for range 10 { + secretKey = sample.Scalar(rand.Reader, crv) + pubKey := secretKey.ActOnBase() + if !frostsign.PublicKeyValidForContract(pubKey) { + continue + } + + pubkey = pubKey + break + } + + if pubkey == nil { + panic("failed to generate valid frost public key for mock signer") + } + + return &MockSigner{ + secret: secretKey, + pub: pubkey, + } +} + +func (m *MockSigner) AsyncSign(rq *signer.SignRequest) error { return nil } + +func (m *MockSigner) Response() <-chan *signer.SignResponse { return nil } + +// AddResponse adds a response to the sign response channel. +func (m *MockSigner) AddResponse(resp *signer.SignResponse) {} + +func (m *MockSigner) GetPublicData(ctx context.Context) (*signer.PublicData, error) { + pubbts, err := m.pub.Curve().MarshalPoint(m.pub) + if err != nil { + return nil, err + } + + return &signer.PublicData{FrostPublicData: pubbts}, nil +} + +func (m *MockSigner) Verify(ctx context.Context, rq *signer.VerifySignatureRequest) error { + return nil +} + +func (m *MockSigner) WitnessNewVaaV1(ctx context.Context, v *vaa.VAA) error { + return nil +} + +func (m *MockSigner) GetPublicKey(ctx context.Context, protocol tsscommon.ProtocolType) (curve.Point, error) { + return m.pub, nil +} + +// MockSignerConnection is a mock implementation of the SignerConnection interface for testing purposes. +type MockSignerConnection struct { + *MockSigner +} + +// UpdateKeys implements tss.SignerConnection. +func (m *MockSignerConnection) UpdateKeys(ctx context.Context, req *signer.UpdateKeysRequest) error { + return nil +} + +func NewMockSignerConnection() *MockSignerConnection { + // TODO: add frost. modified schnorr signature. + return &MockSignerConnection{ + MockSigner: NewMockSigner(), + } +} + +func (m *MockSignerConnection) Connect(ctx context.Context) error { + return nil +} + +func (m *MockSignerConnection) Inform(msg *gossipv1.TSSGossipMessage) error { + return nil +} + +func (m *MockSignerConnection) Outbound() <-chan *gossipv1.TSSGossipMessage { + return nil +} + +// ensure the mock implements the interface +var ( + _ tss.Signer = (*MockSigner)(nil) + _ tss.SignerConnection = (*MockSignerConnection)(nil) +) diff --git a/node/pkg/tss/parse.go b/node/pkg/tss/parse.go deleted file mode 100644 index 5b3a0c11f3..0000000000 --- a/node/pkg/tss/parse.go +++ /dev/null @@ -1,114 +0,0 @@ -package tss - -import ( - "fmt" - - tsscommv1 "github.com/certusone/wormhole/node/pkg/proto/tsscomm/v1" - common "github.com/xlabs/tss-common" -) - -func (t *Engine) parseBroadcast(m Incoming) (broadcastMessage, error) { - broadcastMsg := m.toBroadcastMsg() - if err := validateBroadcastCorrectForm(broadcastMsg); err != nil { - return nil, err - } - - senderId := SenderIndex(broadcastMsg.Message.Sender) - - if !t.GuardianStorage.contains(senderId) { - return nil, fmt.Errorf("%w: %v", ErrUnkownSender, senderId) - } - - switch v := broadcastMsg.Message.Content.(type) { - case *tsscommv1.SignedMessage_TssContent: - if err := validateContentCorrectForm(v.TssContent); err != nil { - return nil, err - } - - id, err := t.GuardianStorage.fetchIdentityFromIndex(senderId) - if err != nil { - return nil, fmt.Errorf("couldn't fetch identity from index %d: %w", senderId, err) - } - - // since this is a broadcast, we set the `to` field to nil, indicating no specific recipient. - p, err := common.ParseWireMessage(v.TssContent.Payload, id.Pid, nil) - if err != nil { - return nil, err - } - - if !isBroadcastMsg(p) { - return nil, fmt.Errorf("non-broadcast message received in broadcast router: %T. sender: %s", p, m.GetSource().Hostname) - } - - parsed := &parsedTssContent{p, ""} - res := &deliverableMessage{parsed} - - rnd, err := getRound(parsed) - if err != nil { - return res, fmt.Errorf("couldn't extract from broadcast message: %w", err) - } - - parsed.signingRound = rnd - - // TODO: once keygen/reshare is implemented, we need to redefine this check, since we'll be using unicasts in round 1. - // according to gg18 (tss ecdsa paper), unicasts are sent in these rounds. - // if rnd == round1Message1 || rnd == round2Message { - // return res, errBadRoundsInBroadcast - // } - - if err := t.validateTrackingIDForm(parsed.getTrackingID()); err != nil { - return res, err - } - - return res, nil - case *tsscommv1.SignedMessage_HashEcho: - if err := validateHashEchoMessageCorrectForm(v); err != nil { - return nil, err - } - - return &parsedHashEcho{ - HashEcho: v.HashEcho, - }, nil - default: - return nil, fmt.Errorf("unknown content type: %T", v) - } -} - -func (t *Engine) parseTssContent(m *tsscommv1.TssContent, source *Identity) (*parsedTssContent, error) { - if err := validateContentCorrectForm(m); err != nil { - return nil, err - } - - spid := source.getPidCopy() - - // since this is a unicast, we set the `to` field to self. - p, err := common.ParseWireMessage(m.Payload, spid, t.GuardianStorage.Self.Pid) - if err != nil { - return nil, err - } - - parsed := &parsedTssContent{p, ""} - - // ensuring the reported source of the message matches the claimed source. (parsed.GetFrom() used by the tss-lib) - if !spid.Equals(parsed.GetFrom()) { - return parsed, fmt.Errorf("parsed message sender doesn't match the source of the message") - } - - rnd, err := getRound(parsed) - if err != nil { - return parsed, fmt.Errorf("unicast parsing error: %w", err) - } - - parsed.signingRound = rnd - - // only round 1 and round 2 are unicasts. - // if rnd != round1Message1 && rnd != round2Message { - // return parsed, errUnicastBadRound - // } - - if err := t.validateTrackingIDForm(parsed.getTrackingID()); err != nil { - return parsed, err - } - - return parsed, nil -} diff --git a/node/pkg/tss/setup_test.go b/node/pkg/tss/setup_test.go deleted file mode 100644 index 329378cc08..0000000000 --- a/node/pkg/tss/setup_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package tss - -import ( - "testing" - - "github.com/certusone/wormhole/node/pkg/internal/testutils" -) - -const ( - Participants = 5 - Threshold = 2 // not including, meaning 3 guardians are needed to sign. -) - -func TestGuardianStorageUnmarshal(t *testing.T) { - var st GuardianStorage - err := st.load(testutils.MustGetMockGuardianTssStorage()) - if err != nil { - t.Error(err) - } -} diff --git a/node/pkg/tss/util.go b/node/pkg/tss/util.go deleted file mode 100644 index 247b3dec69..0000000000 --- a/node/pkg/tss/util.go +++ /dev/null @@ -1,380 +0,0 @@ -package tss - -import ( - "context" - "encoding/binary" - "errors" - "fmt" - "sync" - "time" - - tsscommv1 "github.com/certusone/wormhole/node/pkg/proto/tsscomm/v1" - "github.com/wormhole-foundation/wormhole/sdk/vaa" - "github.com/xlabs/multi-party-sig/protocols/frost/keygen" - "github.com/xlabs/multi-party-sig/protocols/frost/sign" - common "github.com/xlabs/tss-common" - "github.com/xlabs/tss-lib/v2/party" - "go.uber.org/zap" -) - -type logableError struct { - cause error - trackingId *common.TrackingID - round signingRound -} - -type set[T comparable] map[T]struct{} - -type strPartyId string - -// activeSigCounter is a helper struct to keep track of active signatures. -// Each signature has a digest, and each guardian is allowed to be active -// for a certain number of signatures. -// a guardian is allowed to send how many messages it want per signature, but not allowed to -// participate in more than maxActiveSignaturesPerGuardian signatures at a time. -type activeSigCounter struct { - mtx sync.RWMutex - - digestToGuardians map[sigKey]set[strPartyId] - guardianToDigests map[strPartyId]set[sigKey] - firstSeen map[sigKey]time.Time -} - -func newSigCounter() activeSigCounter { - return activeSigCounter{ - mtx: sync.RWMutex{}, - - digestToGuardians: make(map[sigKey]set[strPartyId]), - guardianToDigests: make(map[strPartyId]set[sigKey]), - firstSeen: make(map[sigKey]time.Time), - } -} - -// Add adds a guardian to the counter for a given digest. -// returns false if this guardian is active for too many signatures ( > maxActiveSignaturesPerGuardian). -func (c *activeSigCounter) add(trackId *common.TrackingID, guardian *common.PartyID, maxActiveSignaturesPerGuardian int) bool { - if trackId == nil || guardian == nil { - return false - } - - c.mtx.Lock() - defer c.mtx.Unlock() - - sgkey := trackingIdIntoSigKey(trackId) - - if _, ok := c.digestToGuardians[sgkey]; !ok { - c.digestToGuardians[sgkey] = make(set[strPartyId]) - } - - strPartyId := strPartyId(guardian.ToString()) - - if _, ok := c.guardianToDigests[strPartyId]; !ok { - c.guardianToDigests[strPartyId] = make(set[sigKey]) - } - - // if already an active signature for this guardian, then it doesn't count as an additional signature - if _, ok := c.guardianToDigests[strPartyId][sgkey]; ok { - return true - } - - // the guardian hasn't yet participated in this signing for the digest, we must ensure an additional signature is allowed - if len(c.guardianToDigests[strPartyId])+1 > maxActiveSignaturesPerGuardian { - return false - } - - c.digestToGuardians[sgkey][strPartyId] = struct{}{} - c.guardianToDigests[strPartyId][sgkey] = struct{}{} - if _, ok := c.firstSeen[sgkey]; !ok { - c.firstSeen[sgkey] = time.Now() - } - - return true -} - -func (c *activeSigCounter) remove(trackid *common.TrackingID) { - if trackid == nil { - return - } - - key := trackingIdIntoSigKey(trackid) - - c.mtx.Lock() - defer c.mtx.Unlock() - - c.unlockedRemover(key) -} - -func (c *activeSigCounter) unlockedRemover(key sigKey) { - guardians := c.digestToGuardians[key] - delete(c.digestToGuardians, key) - - for g := range guardians { - delete(c.guardianToDigests[g], key) - } - - delete(c.firstSeen, key) -} - -func (c *activeSigCounter) cleanSelf(maxDuration time.Duration) { - c.mtx.Lock() - defer c.mtx.Unlock() - - for k, v := range c.firstSeen { - if time.Since(v).Abs() > maxDuration { - c.unlockedRemover(k) - } - } - -} - -func (l logableError) Error() string { - if l.cause == nil { - return "" - } - - return l.cause.Error() -} - -// Unwrap ensures logableError supports errors.Is and errors.As methods. -func (l logableError) Unwrap() error { - return l.cause -} - -func logErr(l *zap.Logger, err error) { - if l == nil { - return - } - - if err == nil { - return - } - - informativeErr, ok := err.(logableError) - if !ok { - l.Error(err.Error()) - - return - } - - var zapFields []zap.Field - if informativeErr.trackingId != nil { - zapFields = append(zapFields, zap.String("trackingId", informativeErr.trackingId.ToString())) - zapFields = append(zapFields, zap.String("chainID", extractChainIDFromTrackingID(informativeErr.trackingId).String())) - } - - if informativeErr.round != "" { - zapFields = append(zapFields, zap.String("round", string(informativeErr.round))) - } - - l.Error(informativeErr.Error(), zapFields...) -} - -var ( - ErrBroadcastIsNil = fmt.Errorf("broadcast is nil") - ErrNilPartyId = fmt.Errorf("party id is nil") - ErrEmptyIDInPID = fmt.Errorf("partyId identifier is empty") - ErrEmptyKeyInPID = fmt.Errorf("partyId doesn't contain a key") - ErrSignedMessageIsNil = fmt.Errorf("SignedMessage is nil") - ErrNoContent = fmt.Errorf("SignedMessage doesn't contain a content") - ErrNilPayload = fmt.Errorf("SignedMessage doesn't contain a payload") - ErrMissingTimestamp = fmt.Errorf("problem struct missing timestamp field") -) - -func validateBroadcastCorrectForm(e *tsscommv1.Echo) error { - if e == nil { - return ErrBroadcastIsNil - } - - m := e.Message - if m == nil { - return ErrSignedMessageIsNil - } - - if m.Content == nil { - return ErrNoContent - } - - if len(m.Signature) == 0 { - return errEmptySignature - } - - return nil -} - -var ( - errNilEcho = errors.New("echo is nil") - errEchoDigestBadSize = errors.New("digest is not the correct size") - errEchoSessionUUID = errors.New("echo sessionUUID is not the correct size") -) - -func validateHashEchoMessageCorrectForm(v *tsscommv1.SignedMessage_HashEcho) error { - if v == nil || v.HashEcho == nil { - return errNilEcho - } - - if len(v.HashEcho.OriginalContetDigest) != len(digest{}) { - return errEchoDigestBadSize - } - - if len(v.HashEcho.SessionUuid) != len(uuid{}) { - return errEchoSessionUUID - } - - return nil -} - -func validateUnicastCorrectForm(m *tsscommv1.Unicast) error { - if m == nil { - return ErrNoContent - } - - if m.Content == nil { - return ErrNoContent - } - - return nil -} - -func validateContentCorrectForm(m *tsscommv1.TssContent) error { - if m == nil { - return ErrNoContent - } - - if m.Payload == nil { - return ErrNilPayload - } - - return nil -} - -type signingRound string - -const ( - round1Message signingRound = "round1" - round2Message signingRound = "round2" - round3Message signingRound = "round3" -) - -var _intToRoundArr = []signingRound{ - round1Message, - round2Message, - round3Message, -} - -func intToRound(i int) signingRound { - if i < 0 || i > 2 { - return "" - } - - return _intToRoundArr[i-1] -} - -func getRound(m common.ParsedMessage) (signingRound, error) { - if m == nil { - return "", fmt.Errorf("message is nil") - } - - if m.Content() == nil { - return "", fmt.Errorf("message content is nil") - } - - rnd := m.Content().RoundNumber() - if rnd < 1 || rnd > len(_intToRoundArr) { - return "", fmt.Errorf("message content round number is out of range") - } - - return _intToRoundArr[m.Content().RoundNumber()-1], nil -} - -func isBroadcastMsg(m common.ParsedMessage) bool { - switch m.Content().(type) { - case *sign.Broadcast2: - return true - case *sign.Broadcast3: - return true - case *keygen.Broadcast2: - return true - case *keygen.Broadcast3: - return true - default: - return false - } -} - -func intoChannelOrDone[T any](ctx context.Context, c chan T, v T) error { - select { - case c <- v: - return nil - case <-ctx.Done(): - return fmt.Errorf("error sending to channel: %w", ctx.Err()) - } -} - -func outOfChannelOrDone[T any](ctx context.Context, c chan T) (T, error) { - var v T - select { - case v = <-c: - return v, nil - case <-ctx.Done(): - return v, ctx.Err() - } -} - -func (st *GuardianStorage) validateTrackingIDForm(tid *common.TrackingID) error { - if tid == nil { - return fmt.Errorf("trackingID is nil") - } - - if len(tid.Digest) != party.DigestSize { - return fmt.Errorf("trackingID digest is not in correct size") - } - - // checking that the byte array is the correct size - if len(tid.PartiesState) < (st.NumGuardians()+7)/8 { - return fmt.Errorf("trackingID partiesState is too short") - } - - // TODO: expecting AuxiliaryData to be set. - - return nil -} - -func extractChainIDFromTrackingID(tid *common.TrackingID) vaa.ChainID { - bts := [2]byte{} - copy(bts[:], tid.AuxiliaryData) - - return vaa.ChainID(binary.BigEndian.Uint16(bts[:])) -} - -func chainIDToBytes(chainID vaa.ChainID) []byte { - bts := [2]byte{} - binary.BigEndian.PutUint16(bts[:], uint16(chainID)) - - return bts[:] -} - -// sigKey contains two main parts of common.TrackID: the digest and the chainID. -// it doesan't contain the faulty bitmap since we want to point to the same signature even if the faulty bitmap changes. -type sigKey [party.DigestSize + auxiliaryDataSize]byte - -func intoSigKey(dgst party.Digest, chain vaa.ChainID) sigKey { - var key sigKey - - copy(key[:party.DigestSize], dgst[:]) - copy(key[party.DigestSize:], chainIDToBytes(chain)) - - return key -} - -func trackingIdIntoSigKey(tid *common.TrackingID) sigKey { - dgst := party.Digest{} - copy(dgst[:], tid.Digest) - - return intoSigKey(dgst, extractChainIDFromTrackingID(tid)) -} - -type SenderIndex uint32 - -func (s SenderIndex) toProto() uint32 { - return uint32(s) -} diff --git a/node/pkg/tss/vaav1_handling.go b/node/pkg/tss/vaav1_handling.go deleted file mode 100644 index be44125641..0000000000 --- a/node/pkg/tss/vaav1_handling.go +++ /dev/null @@ -1,165 +0,0 @@ -package tss - -import ( - "errors" - "fmt" - - tsscommv1 "github.com/certusone/wormhole/node/pkg/proto/tsscomm/v1" - "github.com/wormhole-foundation/wormhole/sdk/vaa" - "go.uber.org/zap" -) - -var errNilGuardianSetState = errors.New("tss' guardianSetState nil") -var errNilVaa = errors.New("nil VAA") -var errNetworkOutputChannelFull = errors.New("network output channel buffer is full") - -// Assumes valid VAAs only. -func (t *Engine) WitnessNewVaa(v *vaa.VAA) error { - if t == nil { - return errNilTssEngine - } - - if t.started.Load() != started { - return errTssEngineNotStarted - } - - // consider removing this check: Going leaderless, and letting everyone send VAAs they see - // adds a layer of availability (no need to worry about leader missing VAAs or going offline), - // but will spam the network with duplicated VAAs. - if !t.isleader { - return nil - } - - if v == nil { - return errNilVaa - } - - if t.gst == nil { - t.logger.Error("issue sending VAAv1 to others", zap.Error(errNilGuardianSetState)) - - return errNilGuardianSetState - } - - dgst := digest{} - copy(dgst[:], v.SigningDigest().Bytes()) - - if v.Version != vaa.VaaVersion1 { - // not an error. but will not accept. - return nil - } - - gs := t.gst.Get() - if err := v.Verify(gs.Keys); err != nil { - return nil // won't send invalid VAAs. - } - - bts, err := v.Marshal() - if err != nil { - err := fmt.Errorf("failed to marshal VAA: %w", err) - - t.logger.Error("issue sending VAAv1 to others", zap.Error(err)) - return err - } - - send := Unicast{ - Unicast: &tsscommv1.Unicast{ - Content: &tsscommv1.Unicast_Vaav1{ - Vaav1: &tsscommv1.VaaV1Info{ - Marshaled: bts, - }, - }, - }, - Receipients: t.GuardianStorage.Identities, // sending to all guardians. - } - - select { - case t.messageOutChan <- &send: - t.logger.Info("informed all guardians on vaav1", - zap.String("chainID", v.EmitterChain.String()), - zap.String("digest", fmt.Sprintf("%x", v.SigningDigest())), - ) - default: - t.logger.Error("issue sending VAAv1 to others", zap.Error(errNetworkOutputChannelFull)) - } - - return nil -} - -var errNilGuardianSet = fmt.Errorf("nil guardian set") -var errNotVaaV1 = fmt.Errorf("not a v1 VAA") - -// handleUnicastVaaV1 expects to receive valid Vaav1 messages. -// If the VAA is valid, it will trigger the TSS signing protocol too for that VAA (beginTSSSign, will ensure double signing for the same digest). -func (t *Engine) handleUnicastVaaV1(v *tsscommv1.Unicast_Vaav1) error { - if t == nil { - return errNilTssEngine - } - - if t.started.Load() != started { - return errTssEngineNotStarted - } - - if t.gst == nil { - return errNilGuardianSetState - } - - gs := t.gst.Get() - if gs == nil { - return errNilGuardianSet - } - - if v == nil || v.Vaav1 == nil { - return errNilVaa - } - - newVaa, err := vaa.Unmarshal(v.Vaav1.Marshaled) - if err != nil { - return fmt.Errorf("unmarshal err: %w", err) - } - - dgst := digest{} - copy(dgst[:], newVaa.SigningDigest().Bytes()) - - if newVaa.Version != vaa.VaaVersion1 { - return errNotVaaV1 - } - - if err := newVaa.Verify(gs.Keys); err != nil { - return err - } - - signatureMeta := signingMeta{ - isFromVaav1: true, - verifiedVAAv1: newVaa, - } - - return t.beginTSSSign(dgst[:], newVaa.EmitterChain, newVaa.ConsistencyLevel, signatureMeta) -} - -var errCouldNotMapAllVaaV1Signers = fmt.Errorf("could not map all VAAv1 signers to senderIndexes") - -func (t *Engine) translateVaaV1Signers(v *vaa.VAA) (map[SenderIndex]*Identity, error) { - gs := t.gst.Get() - // translating the VAAv1 signers to senderIndexes to use. - signersID := make(map[SenderIndex]*Identity, len(gs.Keys)) - for _, s := range v.Signatures { - if int(s.Index) >= len(gs.Keys) { - // shouldn't happen, since the signature was verified. but just in case. - return nil, fmt.Errorf("signature index %d out of bounds for guardian set size %d", s.Index, len(gs.Keys)) - } - - id, err := t.GuardianStorage.fetchIdentityFromVaav1Pubkey(gs.Keys[s.Index]) - if err != nil { - return nil, errCouldNotMapAllVaaV1Signers - } - - signersID[id.CommunicationIndex] = id - } - - if len(signersID) != len(v.Signatures) { - // make into an error: - return nil, errCouldNotMapAllVaaV1Signers - } - - return signersID, nil -} diff --git a/proto/gossip/v1/gossip.proto b/proto/gossip/v1/gossip.proto index a665e39d45..10aa06184d 100644 --- a/proto/gossip/v1/gossip.proto +++ b/proto/gossip/v1/gossip.proto @@ -15,9 +15,21 @@ message GossipMessage { SignedQueryRequest signed_query_request = 10; SignedQueryResponse signed_query_response = 11; SignedObservationBatch signed_observation_batch = 12; + TSSGossipMessage tss_gossip_message = 13; } } + +// TSSGossipMessage is used to forward different TSS messages between nodes. +// For instance: in the TSS protocol we incorporated a leader-based protocol where a specific guardian +// can forward a VAAv1 with a specific committee it wants others to participate in. +message TSSGossipMessage{ + // currently marshalled VAAv1. Can later be other items signers wish to gossip about. + bytes message = 1; + // ECDSA signature using the node's guardian public key. + bytes signature = 2; +} + message SignedHeartbeat { // Serialized Heartbeat message. bytes heartbeat = 1; diff --git a/proto/tsscomm/v1/tsscomm.proto b/proto/tsscomm/v1/tsscomm.proto deleted file mode 100644 index 82209cb6a0..0000000000 --- a/proto/tsscomm/v1/tsscomm.proto +++ /dev/null @@ -1,71 +0,0 @@ -syntax = "proto3"; - -package tsscomm.v1; - -option go_package = "github.com/certusone/wormhole/node/pkg/proto/tss/v1;tsscommv1"; -import "google/protobuf/empty.proto"; -import "google/protobuf/timestamp.proto"; - - - -// DirectLink is used for TSS communications between guardians. -// Since TSS requires reliable broadcast logic -service DirectLink { - // Send uses a stream since the clients of this RPC will invoke it multiple times throughout the life of the server. - rpc Send(stream PropagatedMessage) returns (google.protobuf.Empty); -} - -// copyOfTssLib proto. -message PartyId{ - string id = 1; -} - -message TssContent{ - bytes payload = 1; - uint64 msg_serial_number = 2; -} - -// SignedMessage is the content of a broadcast message. It may be echoed and as a result requires a signature. -message SignedMessage { - // Although we don’t anticipate more senders than 255 (which is equivalent to 1 byte), - // the uint32 data type is highly optimized in protobuf for sending smaller - // numbers (refer to https://protobuf.dev/programming-guides/encoding/#simple). - uint32 sender = 1; // pointer to specific PartyID - bytes signature = 2; - - oneof content { - TssContent tss_content = 3; - HashEcho hashEcho = 6; - }; -} - -message HashEcho { - bytes sessionUuid = 1; // should be the relevant uuid. without it, we cant math this echo with the tssContent that we receive a hash of. - bytes originalContetDigest = 2; // should be a hash of what the original sender signed. -} - -// Echo is a message explicitly used by the Reliable Broadcast protocol. -message Echo { - SignedMessage message = 1; -} - -message VaaV1Info { - bytes marshaled = 1; -} - -message Unicast{ - oneof content{ - TssContent tss = 1; - VaaV1Info vaav1 = 2; - } -} - - -// PropagatedMessage is a message that is sent across the network, -// either to a specific recipient or all nodes (using reliable broadcast). -message PropagatedMessage { - oneof Message{ - Unicast Unicast = 1; - Echo Echo = 2; - } -} \ No newline at end of file