Node/Gateway: Relay attestations (#3350)

This commit is contained in:
bruce-riley 2023-09-07 14:11:15 -05:00 committed by GitHub
parent edba6449a7
commit c1ff1e1d1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 315 additions and 73 deletions

View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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",
}

View File

@ -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": [