diff --git a/Tiltfile b/Tiltfile index 4b51e8915..7558f33a9 100644 --- a/Tiltfile +++ b/Tiltfile @@ -276,7 +276,7 @@ def build_node_yaml(): "--accountantContract", "wormhole14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9srrg465", "--accountantKeyPath", - "/tmp/mounted-keys/wormchain/wormchainKey", + "/tmp/mounted-keys/wormchain/accountantKey", "--accountantKeyPassPhrase", "test0000", "--accountantWS", @@ -294,7 +294,7 @@ def build_node_yaml(): "--gatewayRelayerContract", "wormhole17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9jfksztgw5uh69wac2pgshdnj3k", "--gatewayRelayerKeyPath", - "/tmp/mounted-keys/wormchain/wormchainKey", + "/tmp/mounted-keys/wormchain/gwrelayerKey", "--gatewayRelayerKeyPassPhrase", "test0000", diff --git a/devnet/node.yaml b/devnet/node.yaml index b8838b9f3..475f043da 100644 --- a/devnet/node.yaml +++ b/devnet/node.yaml @@ -49,10 +49,14 @@ spec: secretName: node-wormchain-key optional: false items: - - key: wormchainKey0 - path: wormchainKey0 - - key: wormchainKey1 - path: wormchainKey1 + - key: accountantKey0 + path: accountantKey0 + - key: accountantKey1 + path: accountantKey1 + - key: gwrelayerKey0 + path: gwrelayerKey0 + - key: gwrelayerKey1 + path: gwrelayerKey1 containers: - name: guardiand image: guardiand-image @@ -98,9 +102,9 @@ spec: - ws://eth-devnet:8545 # - --wormchainURL # - wormchain:9090 - # - --wormchainKeyPath - # - /tmp/mounted-keys/wormchain/wormchainKey - # - --wormchainKeyPassPhrase + # - --accountantKeyPath + # - /tmp/mounted-keys/wormchain/accountantKey + # - --accountantKeyPassPhrase # - test0000 # - --accountantWS # - http://wormchain:26657 @@ -189,5 +193,7 @@ metadata: name: node-wormchain-key type: Opaque data: - wormchainKey0: LS0tLS1CRUdJTiBURU5ERVJNSU5UIFBSSVZBVEUgS0VZLS0tLS0Ka2RmOiBiY3J5cHQKc2FsdDogNDc2ODc2NkE3OEZEN0ZBQjMwMUJGOTM5MUYwQ0Y2M0YKdHlwZTogc2VjcDI1NmsxCgpkbEZuN1ZqRk02RnJjYkdaVDRWeE5yRlE3SUhQS2RyVVBCRTYraW8yK0w0VFZqcis5emNIQTF3dzNubWtqNVFlCnVSekJWMjQyeUdTc3hNTTJZckI2Q1ZXdzlaWXJJY3JFeks1c0FuST0KPXB2aHkKLS0tLS1FTkQgVEVOREVSTUlOVCBQUklWQVRFIEtFWS0tLS0t - wormchainKey1: LS0tLS1CRUdJTiBURU5ERVJNSU5UIFBSSVZBVEUgS0VZLS0tLS0Ka2RmOiBiY3J5cHQKc2FsdDogNzc1M0NCQTBBMUQ0NTJCMkE2QzlERDM4ODc3MTg0NEEKdHlwZTogc2VjcDI1NmsxCgpSYnhRVWRnK2ZHcjMzZTAyVWFFQW1YTDFlNFkrTGJUMFdqbnl4RVR3OXBoL2JXOGI0MzdhWmErOWlCc3NBa0UyCnRScUwvb0J1NWFnQXJocHNnWUgxNlhOWjJHMXRwY0R3V0dQZ1VWVT0KPUd6YUwKLS0tLS1FTkQgVEVOREVSTUlOVCBQUklWQVRFIEtFWS0tLS0t + accountantKey0: LS0tLS1CRUdJTiBURU5ERVJNSU5UIFBSSVZBVEUgS0VZLS0tLS0Ka2RmOiBiY3J5cHQKc2FsdDogNDc2ODc2NkE3OEZEN0ZBQjMwMUJGOTM5MUYwQ0Y2M0YKdHlwZTogc2VjcDI1NmsxCgpkbEZuN1ZqRk02RnJjYkdaVDRWeE5yRlE3SUhQS2RyVVBCRTYraW8yK0w0VFZqcis5emNIQTF3dzNubWtqNVFlCnVSekJWMjQyeUdTc3hNTTJZckI2Q1ZXdzlaWXJJY3JFeks1c0FuST0KPXB2aHkKLS0tLS1FTkQgVEVOREVSTUlOVCBQUklWQVRFIEtFWS0tLS0t + accountantKey1: LS0tLS1CRUdJTiBURU5ERVJNSU5UIFBSSVZBVEUgS0VZLS0tLS0Ka2RmOiBiY3J5cHQKc2FsdDogNzc1M0NCQTBBMUQ0NTJCMkE2QzlERDM4ODc3MTg0NEEKdHlwZTogc2VjcDI1NmsxCgpSYnhRVWRnK2ZHcjMzZTAyVWFFQW1YTDFlNFkrTGJUMFdqbnl4RVR3OXBoL2JXOGI0MzdhWmErOWlCc3NBa0UyCnRScUwvb0J1NWFnQXJocHNnWUgxNlhOWjJHMXRwY0R3V0dQZ1VWVT0KPUd6YUwKLS0tLS1FTkQgVEVOREVSTUlOVCBQUklWQVRFIEtFWS0tLS0t + gwrelayerKey0: LS0tLS1CRUdJTiBURU5ERVJNSU5UIFBSSVZBVEUgS0VZLS0tLS0KdHlwZTogc2VjcDI1NmsxCmtkZjogYmNyeXB0CnNhbHQ6IDc4OUYzRTBCMkVGNDcyNjAyQzNFMUE0OUI2OENFQzlBCgpGWHAvSllPS3E4WmZtOWxHZ3ZFNEM3NXFyUXFNZFp2RHNWRjhObTdMQU1oR2dHbXBnZnpoZjUrZ3IwZ1hjYjVWCmtSTXA2c0p0NkxCVzRPYWF2ckk3ay84Vml2NWhMVU1la1dPMHg5bz0KPUxrb1MKLS0tLS1FTkQgVEVOREVSTUlOVCBQUklWQVRFIEtFWS0tLS0t + gwrelayerKey1: LS0tLS1CRUdJTiBURU5ERVJNSU5UIFBSSVZBVEUgS0VZLS0tLS0Ka2RmOiBiY3J5cHQKc2FsdDogNDc5RDk3RDE2OTE0QkQ4QjlFNUUwQzkzMDA0RDA4RUEKdHlwZTogc2VjcDI1NmsxCgpvTEJ0aUkwT2pudXo5bHlzeVlZOFhQeEVkTnpwYUJOVWFkL0UySlJld2pFWFZNVVNTWll2QVZKbERiN3hEQjlSCmEvdm45SFNPM2hKOFc1QTBKOVFqUVZXRzVoZXBNZVpQUEI4M1FCUT0KPVJuTGEKLS0tLS1FTkQgVEVOREVSTUlOVCBQUklWQVRFIEtFWS0tLS0t diff --git a/node/pkg/gwrelayer/gwrelayer.go b/node/pkg/gwrelayer/gwrelayer.go index 80293f615..9427d037e 100644 --- a/node/pkg/gwrelayer/gwrelayer.go +++ b/node/pkg/gwrelayer/gwrelayer.go @@ -13,6 +13,7 @@ import ( "github.com/certusone/wormhole/node/pkg/common" "github.com/certusone/wormhole/node/pkg/supervisor" + "github.com/wormhole-foundation/wormhole/sdk" "github.com/wormhole-foundation/wormhole/sdk/vaa" wasmdtypes "github.com/CosmWasm/wasmd/x/wasm/types" @@ -36,24 +37,54 @@ type ( ) // GatewayRelayer is the object that manages the interface to the wormchain accountant smart contract. -type GatewayRelayer struct { - ctx context.Context - logger *zap.Logger - contractAddress string - wormchainConn GatewayRelayerWormchainConn - env common.Environment - subChan chan *vaa.VAA - targetAddress vaa.Address -} +type ( + GatewayRelayer struct { + ctx context.Context + logger *zap.Logger + ibcTranslatorAddress string + wormchainConn GatewayRelayerWormchainConn + env common.Environment + subChan chan *VaaToPublish + tokenBridges tokenBridgeMap + tokenBridgeAddress string + ibcTranslatorPayloadAddress vaa.Address + } + + tokenBridgeMap map[tokenBridgeKey]struct{} + + tokenBridgeKey struct { + emitterChainId vaa.ChainID + emitterAddr vaa.Address + } + + VaaToPublish struct { + V *vaa.VAA + ContractAddress string + VType VaaType + } + + VaaType uint8 +) + +const ( + IbcTranslator VaaType = iota + TokenBridge +) // subChanSize is the capacity of the submit channel used to publish VAAs. const subChanSize = 50 var ( - vaasSubmitted = promauto.NewCounter( + vaasSubmittedToIbcTranslator = promauto.NewCounter( prometheus.CounterOpts{ - Name: "gwrelayer_vaas_submitted", - Help: "Total number of VAAs submitted to the gateway relayer", + Name: "gwrelayer_vaas_submitted_to_ibc_translator", + Help: "Total number of VAAs submitted to the ibc translator contract by the gateway relayer", + }) + + vaasSubmittedToTokenBridge = promauto.NewCounter( + prometheus.CounterOpts{ + Name: "gwrelayer_vaas_submitted_to_token_bridge", + Help: "Total number of VAAs submitted to the token bridge contract by the gateway relayer", }) channelFullErrors = promauto.NewCounter( @@ -64,7 +95,7 @@ var ( submitErrors = promauto.NewCounter( prometheus.CounterOpts{ Name: "gwrelayer_submit_errors", - Help: "Total number of errors encountered while submitting VAAs to the gateway relayer", + Help: "Total number of errors encountered while submitting VAAs", }) ) @@ -72,32 +103,46 @@ var ( func NewGatewayRelayer( ctx context.Context, logger *zap.Logger, - contractAddress string, + ibcTranslatorAddress string, wormchainConn GatewayRelayerWormchainConn, env common.Environment, ) *GatewayRelayer { - if contractAddress == "" { + if ibcTranslatorAddress == "" { return nil // This is not an error, it just means the feature is not enabled. } + return &GatewayRelayer{ - ctx: ctx, - logger: logger.With(zap.String("component", "gwrelayer")), - contractAddress: contractAddress, - wormchainConn: wormchainConn, - env: env, - subChan: make(chan *vaa.VAA, subChanSize), + ctx: ctx, + logger: logger.With(zap.String("component", "gwrelayer")), + ibcTranslatorAddress: ibcTranslatorAddress, + wormchainConn: wormchainConn, + env: env, + subChan: make(chan *VaaToPublish, subChanSize), + // tokenBridgeAddress and tokenBridges are initialized in Start(). } } // Start initializes the gateway relayer and starts the worker runnable that submits VAAs to the contract. func (gwr *GatewayRelayer) Start(ctx context.Context) error { var err error - gwr.targetAddress, err = convertBech32AddressToWormhole(gwr.contractAddress) + gwr.ibcTranslatorPayloadAddress, err = convertBech32AddressToWormhole(gwr.ibcTranslatorAddress) if err != nil { return err } - gwr.logger.Info("starting gateway relayer", zap.String("contract", gwr.contractAddress), zap.String("targetAddress", hex.EncodeToString(gwr.targetAddress.Bytes()))) + gwr.tokenBridges, gwr.tokenBridgeAddress, err = buildTokenBridgeMap(gwr.logger, gwr.env) + if err != nil { + return fmt.Errorf("failed to build token bridge map: %w", err) + } + if gwr.tokenBridgeAddress == "" { + return fmt.Errorf("failed to look up token bridge address for gateway") + } + + gwr.logger.Info("starting gateway relayer", + zap.String("ibcTranslatorAddress", gwr.ibcTranslatorAddress), + zap.String("ibcTranslatorPayloadAddress", hex.EncodeToString(gwr.ibcTranslatorPayloadAddress.Bytes())), + zap.String("tokenBridgeAddress", gwr.tokenBridgeAddress), + ) // Start the watcher to listen to transfer events from the smart contract. if gwr.env == common.GoTest { @@ -114,6 +159,47 @@ func (gwr *GatewayRelayer) Start(ctx context.Context) error { return nil } +// buildTokenBridgeMap builds a set of all token bridge emitter chain / emitter addresses. +func buildTokenBridgeMap(logger *zap.Logger, env common.Environment) (tokenBridgeMap, string, error) { + emitterMap := sdk.KnownTokenbridgeEmitters + if env == common.TestNet { + emitterMap = sdk.KnownTestnetTokenbridgeEmitters + } else if env == common.UnsafeDevNet || env == common.GoTest || env == common.AccountantMock { + emitterMap = sdk.KnownDevnetTokenbridgeEmitters + } + + // Build the map of token bridges to be monitored. + tokenBridges := make(tokenBridgeMap) + tokenBridgeAddress := "" + for chainId, emitterAddrBytes := range emitterMap { + if chainId == vaa.ChainIDWormchain { + var err error + tokenBridgeAddress, err = sdktypes.Bech32ifyAddressBytes("wormhole", emitterAddrBytes) + if err != nil { + return nil, "", fmt.Errorf(`failed to convert gateway emitter address "%s" to bech32: %v`, hex.EncodeToString(emitterAddrBytes), err) + } + + // We don't want to forward stuff that originated on Gateway, so don't add it to the map. + continue + } + emitterAddr, err := vaa.BytesToAddress(emitterAddrBytes) + if err != nil { + return nil, "", fmt.Errorf("failed to convert emitter address for chain: %v", chainId) + } + + tbk := tokenBridgeKey{emitterChainId: chainId, emitterAddr: emitterAddr} + _, exists := tokenBridges[tbk] + if exists { + return nil, "", fmt.Errorf("detected duplicate token bridge for chain: %v", chainId) + } + + tokenBridges[tbk] = struct{}{} + logger.Info("will monitor token bridge:", zap.Stringer("emitterChainId", tbk.emitterChainId), zap.Stringer("emitterAddr", tbk.emitterAddr)) + } + + return tokenBridges, tokenBridgeAddress, nil +} + // Close closes the connection to the smart contract. func (gwr *GatewayRelayer) Close() { if gwr.wormchainConn != nil { @@ -133,24 +219,34 @@ func convertBech32AddressToWormhole(contractAddress string) (vaa.Address, error) // SubmitVAA checks to see if the VAA should be submitted to the smart contract, and if so, writes it to the channel for publishing. func (gwr *GatewayRelayer) SubmitVAA(v *vaa.VAA) { - if shouldPub, err := shouldPublish(v.Payload, vaa.ChainIDWormchain, gwr.targetAddress); err != nil { + var v2p VaaToPublish + if shouldPub, err := shouldPublishToIbcTranslator(v.Payload, vaa.ChainIDWormchain, gwr.ibcTranslatorPayloadAddress); err != nil { gwr.logger.Error("failed to check if vaa should be published", zap.String("msgId", v.MessageID()), zap.Error(err)) return - } else if !shouldPub { + } else if shouldPub { + v2p.VType = IbcTranslator + v2p.ContractAddress = gwr.ibcTranslatorAddress + } else if shouldPub = shouldPublishToTokenBridge(gwr.tokenBridges, v); shouldPub { + v2p.VType = TokenBridge + v2p.ContractAddress = gwr.tokenBridgeAddress + } else { + gwr.logger.Debug("not relaying vaa", zap.String("msgId", v.MessageID())) return } + v2p.V = v + select { - case gwr.subChan <- v: - gwr.logger.Debug("submitted vaa to channel", zap.String("msgId", v.MessageID())) + case gwr.subChan <- &v2p: + gwr.logger.Debug("submitted vaa to channel", zap.String("msgId", v.MessageID()), zap.String("contract", v2p.ContractAddress), zap.Uint8("vaaType", uint8(v2p.VType))) default: channelFullErrors.Inc() - gwr.logger.Error("unable to submit vaa because the channel is full, dropping it", zap.String("msgId", v.MessageID())) + gwr.logger.Error("unable to submit vaa because the channel is full, dropping it", zap.String("msgId", v.MessageID()), zap.String("contract", v2p.ContractAddress), zap.Uint8("vaaType", uint8(v2p.VType))) } } -// shouldPublish returns true if a message should be forwarded to the contract on wormchain, false if not. -func shouldPublish(payload []byte, targetChain vaa.ChainID, targetAddress vaa.Address) (bool, error) { +// shouldPublishToIbcTranslator returns true if a message should be forwarded to the contract on wormchain, false if not. +func shouldPublishToIbcTranslator(payload []byte, targetChain vaa.ChainID, targetAddress vaa.Address) (bool, error) { if len(payload) == 0 { return false, nil } @@ -171,15 +267,33 @@ func shouldPublish(payload []byte, targetChain vaa.ChainID, targetAddress vaa.Ad return true, nil } +// shouldPublishToTokenBridge returns true if a message should be forwarded to the token bridge, false if not. +func shouldPublishToTokenBridge(tokenBridges tokenBridgeMap, v *vaa.VAA) bool { + if _, exists := tokenBridges[tokenBridgeKey{emitterChainId: v.EmitterChain, emitterAddr: v.EmitterAddress}]; !exists { + return false + } + + if len(v.Payload) == 0 { + return false + } + + // We only forward attestations (type two). + if v.Payload[0] != 2 { + return false + } + + return true +} + // worker listens for VAAs and submits them to the smart contract. func (gwr *GatewayRelayer) worker(ctx context.Context) error { for { select { case <-ctx.Done(): return nil - case v := <-gwr.subChan: - if err := gwr.submitVAAToContract(v); err != nil { - gwr.logger.Error("failed to submit vaa to contract", zap.String("msgId", v.MessageID()), zap.Error(err)) + case v2p := <-gwr.subChan: + if err := gwr.submitVAAToContract(v2p); err != nil { + gwr.logger.Error("failed to submit vaa to contract", zap.String("msgId", v2p.V.MessageID()), zap.String("contract", v2p.ContractAddress), zap.Uint8("vaaType", uint8(v2p.VType)), zap.Error(err)) // TODO: For now we don't want to restart because this will happen if the VAA has already been submitted by another guardian. //return fmt.Errorf("failed to submit vaa to contract: %w", err) } @@ -188,18 +302,23 @@ func (gwr *GatewayRelayer) worker(ctx context.Context) error { } // submitVAAToContract submits a VAA to the smart contract on wormchain. -func (gwr *GatewayRelayer) submitVAAToContract(v *vaa.VAA) error { - _, err := SubmitVAAToContract(gwr.ctx, gwr.logger, gwr.wormchainConn, gwr.contractAddress, v) +func (gwr *GatewayRelayer) submitVAAToContract(v2p *VaaToPublish) error { + _, err := SubmitVAAToContract(gwr.ctx, gwr.logger, gwr.wormchainConn, v2p) if err != nil { submitErrors.Inc() return err } - // TODO: Need to check txResp for "VAA already submitted", which should not be an error. - vaasSubmitted.Inc() + + if v2p.VType == IbcTranslator { + vaasSubmittedToIbcTranslator.Inc() + } else { + vaasSubmittedToTokenBridge.Inc() + } return nil } type ( + // completeTransferAndConvertMsg is used to submit a VAA to the IBC translator contract. completeTransferAndConvertMsg struct { Params completeTransferAndConvertParams `json:"complete_transfer_and_convert"` } @@ -207,6 +326,15 @@ type ( completeTransferAndConvertParams struct { VAA []byte `json:"vaa"` } + + // submitVAA is used to submit a VAA to the token bridge contract. + submitVAA struct { + Params submitVAAParams `json:"submit_vaa"` + } + + submitVAAParams struct { + Data []byte `json:"data"` + } ) // SubmitVAAToContract submits a VAA to the smart contract on wormchain. @@ -214,30 +342,41 @@ func SubmitVAAToContract( ctx context.Context, logger *zap.Logger, wormchainConn GatewayRelayerWormchainConn, - contract string, - v *vaa.VAA, + v2p *VaaToPublish, ) (*sdktx.BroadcastTxResponse, error) { - logger.Info("submitting VAA to contract", zap.String("message_id", v.MessageID())) + logger.Info("submitting VAA to contract", zap.String("message_id", v2p.V.MessageID()), zap.String("contract", v2p.ContractAddress), zap.Uint8("vaaType", uint8(v2p.VType))) - vaaBytes, err := v.Marshal() + vaaBytes, err := v2p.V.Marshal() if err != nil { return nil, fmt.Errorf("failed to marshal vaa: %w", err) } - msgData := completeTransferAndConvertMsg{ - Params: completeTransferAndConvertParams{ - VAA: vaaBytes, - }, - } + var msgBytes []byte - msgBytes, err := json.Marshal(msgData) + if v2p.VType == IbcTranslator { + msgData := completeTransferAndConvertMsg{ + Params: completeTransferAndConvertParams{ + VAA: vaaBytes, + }, + } + msgBytes, err = json.Marshal(msgData) + } else if v2p.VType == TokenBridge { + msgData := submitVAA{ + Params: submitVAAParams{ + Data: vaaBytes, + }, + } + msgBytes, err = json.Marshal(msgData) + } else { + return nil, fmt.Errorf("invalid vtype: %d", uint8(v2p.VType)) + } if err != nil { return nil, fmt.Errorf("failed to marshal request: %w", err) } subMsg := wasmdtypes.MsgExecuteContract{ Sender: wormchainConn.SenderAddress(), - Contract: contract, + Contract: v2p.ContractAddress, Msg: msgBytes, Funds: sdktypes.Coins{}, } @@ -268,7 +407,7 @@ func SubmitVAAToContract( return txResp, fmt.Errorf("submit failed: %s", txResp.TxResponse.RawLog) } - logger.Info("done sending broadcast", zap.String("msgId", v.MessageID()), zap.Int64("gasUsed", txResp.TxResponse.GasUsed), zap.Stringer("elapsedTime", time.Since(start))) + logger.Info("done sending broadcast", zap.String("msgId", v2p.V.MessageID()), zap.String("contract", v2p.ContractAddress), zap.Uint8("vaaType", uint8(v2p.VType)), zap.Int64("gasUsed", txResp.TxResponse.GasUsed), zap.Stringer("elapsedTime", time.Since(start))) logger.Debug("in SubmitVAAToContract, done sending broadcast", zap.String("resp", wormchainConn.BroadcastTxResponseToString(txResp))) return txResp, nil diff --git a/node/pkg/gwrelayer/gwrelayer_test.go b/node/pkg/gwrelayer/gwrelayer_test.go index 5548ee24a..9842fd251 100644 --- a/node/pkg/gwrelayer/gwrelayer_test.go +++ b/node/pkg/gwrelayer/gwrelayer_test.go @@ -3,12 +3,18 @@ package gwrelayer import ( "bytes" "encoding/hex" + "fmt" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/certusone/wormhole/node/pkg/common" "github.com/wormhole-foundation/wormhole/sdk/vaa" + + sdktypes "github.com/cosmos/cosmos-sdk/types" + "go.uber.org/zap" ) func Test_convertBech32AddressToWormhole(t *testing.T) { @@ -29,7 +35,7 @@ func Test_convertBech32AddressToWormhole(t *testing.T) { assert.Error(t, err) } -func Test_shouldPublish(t *testing.T) { +func Test_shouldPublishToIbcTranslator(t *testing.T) { type Test struct { label string payload []byte @@ -52,13 +58,63 @@ func Test_shouldPublish(t *testing.T) { for _, tc := range tests { t.Run(string(tc.label), func(t *testing.T) { - result, err := shouldPublish(tc.payload, vaa.ChainIDWormchain, targetAddress) + result, err := shouldPublishToIbcTranslator(tc.payload, vaa.ChainIDWormchain, targetAddress) assert.Equal(t, tc.err, err != nil) assert.Equal(t, tc.result, result) }) } } +func Test_shouldPublishToTokenBridge(t *testing.T) { + type Test struct { + label string + chain vaa.ChainID + address vaa.Address + payload []byte + result bool + } + + logger := zap.NewNop() + + tokenBridges, tokenBridgeAddress, err := buildTokenBridgeMap(logger, common.MainNet) + require.NoError(t, err) + require.NotNil(t, tokenBridges) + require.Equal(t, tokenBridgeAddress, "wormhole1466nf3zuxpya8q9emxukd7vftaf6h4psr0a07srl5zw74zh84yjq4lyjmh") + + tests := []Test{ + {label: "unknown chain", chain: vaa.ChainIDUnset, address: addr("0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585"), payload: []byte{}, result: false}, + {label: "unknown emitter", chain: vaa.ChainIDEthereum, address: addr("0000000000000000000000000000000000000000000000000000000000000000"), payload: []byte{}, result: false}, + {label: "wormchain", chain: vaa.ChainIDWormchain, address: addr("aeb534c45c3049d380b9d9b966f9895f53abd4301bfaff407fa09dea8ae7a924"), payload: []byte{}, result: false}, + {label: "empty payload", chain: vaa.ChainIDEthereum, address: addr("0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585"), payload: []byte{}, result: false}, + {label: "not an attest", chain: vaa.ChainIDEthereum, address: addr("0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585"), payload: []byte{0x1}, result: false}, + {label: "should publish", chain: vaa.ChainIDEthereum, address: addr("0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585"), payload: []byte{0x2}, result: true}, + } + + for _, tc := range tests { + t.Run(string(tc.label), func(t *testing.T) { + v := &vaa.VAA{ + Version: uint8(1), + GuardianSetIndex: uint32(1), + Signatures: []*vaa.Signature{}, + Timestamp: time.Unix(0, 0), + Nonce: uint32(1), + Sequence: uint64(1), + ConsistencyLevel: uint8(32), + EmitterChain: tc.chain, + EmitterAddress: tc.address, + Payload: tc.payload, + } + + result := shouldPublishToTokenBridge(tokenBridges, v) + assert.Equal(t, tc.result, result) + }) + } + + addr, err := sdktypes.Bech32ifyAddressBytes("wormhole", decodeBytes("aeb534c45c3049d380b9d9b966f9895f53abd4301bfaff407fa09dea8ae7a924")) + require.NoError(t, err) + fmt.Println(addr) +} + func decodeBytes(s string) []byte { b, err := hex.DecodeString(s) if err != nil { @@ -66,3 +122,11 @@ func decodeBytes(s string) []byte { } return b } + +func addr(str string) vaa.Address { + a, err := vaa.StringToAddress(str) + if err != nil { + panic("failed to convert address") + } + return a +} diff --git a/sdk/devnet_consts.go b/sdk/devnet_consts.go index 562779e53..c7b1431a1 100644 --- a/sdk/devnet_consts.go +++ b/sdk/devnet_consts.go @@ -15,7 +15,7 @@ var knownDevnetTokenbridgeEmitters = map[vaa.ChainID]string{ vaa.ChainIDTerra: "000000000000000000000000784999135aaa8a3ca5914468852fdddbddd8789d", vaa.ChainIDBSC: "0000000000000000000000000290fb167208af455bb137780163b7b7a9a10c16", vaa.ChainIDAlgorand: "8ec299cb7f3efec28f542397e07f07118d74c875f85409ed8e6b93c17b60e992", - vaa.ChainIDWormchain: "f04a313a7349b120c55c99788f12f712176bb3e5926d012d0ea72fa2bbb85051", + vaa.ChainIDWormchain: "45dbea4617971d93188eda21530bc6503d153313b6f575048c2c35dbc6e4fb06", vaa.ChainIDSui: "be8d2e6809d4873bcf1d8be6af2b92500091ad6aa5dc76bc717af86a58d300ca", } diff --git a/wormchain/devnet/base/config/genesis.json b/wormchain/devnet/base/config/genesis.json index 746254cce..0c92c5098 100644 --- a/wormchain/devnet/base/config/genesis.json +++ b/wormchain/devnet/base/config/genesis.json @@ -14,9 +14,7 @@ "max_bytes": "1048576" }, "validator": { - "pub_key_types": [ - "ed25519" - ] + "pub_key_types": ["ed25519"] }, "version": {} }, @@ -51,6 +49,20 @@ "pub_key": null, "account_number": "0", "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "wormhole1s5a6dg9p902z5rhjgkk0ts8lulvtmhmpftasxe", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "wormhole1dtwappgz4zfmlhay44x5r787u6ap0zhrk2m09m", + "pub_key": null, + "account_number": "0", + "sequence": "0" } ] }, @@ -98,6 +110,32 @@ "amount": "200000000" } ] + }, + { + "address": "wormhole1s5a6dg9p902z5rhjgkk0ts8lulvtmhmpftasxe", + "coins": [ + { + "denom": "utest", + "amount": "100000000000" + }, + { + "denom": "uworm", + "amount": "200000000" + } + ] + }, + { + "address": "wormhole1dtwappgz4zfmlhay44x5r787u6ap0zhrk2m09m", + "coins": [ + { + "denom": "utest", + "amount": "100000000000" + }, + { + "denom": "uworm", + "amount": "200000000" + } + ] } ], "supply": [], @@ -344,10 +382,7 @@ "create_localhost": false, "next_client_sequence": "0", "params": { - "allowed_clients": [ - "06-solomachine", - "07-tendermint" - ] + "allowed_clients": ["06-solomachine", "07-tendermint"] } }, "connection_genesis": { @@ -438,9 +473,7 @@ { "expirationTime": 0, "index": 0, - "keys": [ - "vvpCnVfNGLf4pNkaLamrSvBdD74=" - ] + "keys": ["vvpCnVfNGLf4pNkaLamrSvBdD74="] } ], "guardianValidatorList": [