2022-07-06 07:19:57 -07:00
package cosmwasm
2020-11-16 04:28:07 -08:00
import (
"context"
2021-12-23 14:59:32 -08:00
"encoding/base64"
2020-11-16 04:28:07 -08:00
"encoding/hex"
"fmt"
2022-11-28 05:48:27 -08:00
"io"
2020-11-16 04:28:07 -08:00
"net/http"
2021-12-23 14:59:32 -08:00
"strconv"
2020-11-16 04:28:07 -08:00
"time"
2022-06-21 12:18:16 -07:00
"github.com/certusone/wormhole/node/pkg/p2p"
gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1"
"github.com/prometheus/client_golang/prometheus/promauto"
2021-02-04 10:48:54 -08:00
"github.com/prometheus/client_golang/prometheus"
2020-11-16 04:28:07 -08:00
eth_common "github.com/ethereum/go-ethereum/common"
2021-08-26 01:35:09 -07:00
"github.com/certusone/wormhole/node/pkg/common"
"github.com/certusone/wormhole/node/pkg/readiness"
"github.com/certusone/wormhole/node/pkg/supervisor"
2020-11-16 04:28:07 -08:00
"github.com/gorilla/websocket"
"github.com/tidwall/gjson"
2022-08-18 01:52:36 -07:00
"github.com/wormhole-foundation/wormhole/sdk/vaa"
2020-11-16 04:28:07 -08:00
"go.uber.org/zap"
)
type (
2022-07-06 07:19:57 -07:00
// Watcher is responsible for looking over a cosmwasm blockchain and reporting new transactions to the contract
2021-08-30 07:19:00 -07:00
Watcher struct {
urlWS string
urlLCD string
contract string
2020-11-16 04:28:07 -08:00
2021-06-29 04:55:44 -07:00
msgChan chan * common . MessagePublication
2022-02-09 09:54:33 -08:00
// Incoming re-observation requests from the network. Pre-filtered to only
// include requests for our chainID.
obsvReqC chan * gossipv1 . ObservationRequest
2022-06-16 12:17:43 -07:00
// Readiness component
readiness readiness . Component
// VAA ChainID of the network we're connecting to.
chainID vaa . ChainID
// Key for contract address in the wasm logs
contractAddressFilterKey string
// Key for contract address in the wasm logs
contractAddressLogKey string
2022-07-14 10:41:31 -07:00
// URL to get the latest block info from
latestBlockURL string
2020-11-16 04:28:07 -08:00
}
)
2021-02-04 05:20:49 -08:00
var (
2022-07-06 07:19:57 -07:00
connectionErrors = promauto . NewCounterVec (
2021-02-04 05:20:49 -08:00
prometheus . CounterOpts {
Name : "wormhole_terra_connection_errors_total" ,
2022-07-06 07:19:57 -07:00
Help : "Total number of connection errors on a cosmwasm chain" ,
2022-06-16 12:17:43 -07:00
} , [ ] string { "terra_network" , "reason" } )
2022-07-06 07:19:57 -07:00
messagesConfirmed = promauto . NewCounterVec (
2021-02-04 05:20:49 -08:00
prometheus . CounterOpts {
2021-07-21 10:46:10 -07:00
Name : "wormhole_terra_messages_confirmed_total" ,
2022-07-06 07:19:57 -07:00
Help : "Total number of verified messages found on a cosmwasm chain" ,
2022-06-16 12:17:43 -07:00
} , [ ] string { "terra_network" } )
2022-07-06 07:19:57 -07:00
currentSlotHeight = promauto . NewGaugeVec (
2021-02-04 05:20:49 -08:00
prometheus . GaugeOpts {
Name : "wormhole_terra_current_height" ,
2022-07-06 07:19:57 -07:00
Help : "Current slot height on a cosmwasm chain (at default commitment level, not the level used for observations)" ,
2022-06-16 12:17:43 -07:00
} , [ ] string { "terra_network" } )
2021-07-23 07:06:35 -07:00
queryLatency = promauto . NewHistogramVec (
2021-02-04 05:20:49 -08:00
prometheus . HistogramOpts {
Name : "wormhole_terra_query_latency" ,
2022-07-06 07:19:57 -07:00
Help : "Latency histogram for RPC calls on a cosmwasm chain" ,
2022-06-16 12:17:43 -07:00
} , [ ] string { "terra_network" , "operation" } )
2021-02-04 05:20:49 -08:00
)
2020-11-16 04:28:07 -08:00
type clientRequest struct {
JSONRPC string ` json:"jsonrpc" `
// A String containing the name of the method to be invoked.
Method string ` json:"method" `
// Object to pass as request parameter to the method.
Params [ 1 ] string ` json:"params" `
// The request id. This can be of any type. It is used to match the
// response with the request that it is replying to.
ID uint64 ` json:"id" `
}
2022-07-06 07:19:57 -07:00
// NewWatcher creates a new cosmwasm contract watcher
2022-02-09 09:54:33 -08:00
func NewWatcher (
urlWS string ,
urlLCD string ,
contract string ,
lockEvents chan * common . MessagePublication ,
2022-06-16 12:17:43 -07:00
obsvReqC chan * gossipv1 . ObservationRequest ,
readiness readiness . Component ,
chainID vaa . ChainID ) * Watcher {
// CosmWasm 1.0.0
contractAddressFilterKey := "execute._contract_address"
contractAddressLogKey := "_contract_address"
if chainID == vaa . ChainIDTerra {
// CosmWasm <1.0.0
contractAddressFilterKey = "execute_contract.contract_address"
contractAddressLogKey = "contract_address"
}
2022-07-14 10:41:31 -07:00
// Do not add a leading slash
latestBlockURL := "blocks/latest"
// Injective does things slightly differently than terra
if chainID == vaa . ChainIDInjective {
latestBlockURL = "cosmos/base/tendermint/v1beta1/blocks/latest"
}
return & Watcher {
urlWS : urlWS ,
urlLCD : urlLCD ,
contract : contract ,
msgChan : lockEvents ,
obsvReqC : obsvReqC ,
readiness : readiness ,
chainID : chainID ,
contractAddressFilterKey : contractAddressFilterKey ,
contractAddressLogKey : contractAddressLogKey ,
latestBlockURL : latestBlockURL ,
}
2020-11-16 04:28:07 -08:00
}
2021-08-30 07:19:00 -07:00
func ( e * Watcher ) Run ( ctx context . Context ) error {
2022-06-16 12:17:43 -07:00
networkName := vaa . ChainID ( e . chainID ) . String ( )
p2p . DefaultRegistry . SetNetworkStats ( e . chainID , & gossipv1 . Heartbeat_Network {
2021-08-30 07:19:00 -07:00
ContractAddress : e . contract ,
2021-02-09 17:00:45 -08:00
} )
2020-11-16 04:28:07 -08:00
errC := make ( chan error )
logger := supervisor . Logger ( ctx )
2022-07-06 07:19:57 -07:00
logger . Info ( "connecting to websocket" , zap . String ( "network" , networkName ) , zap . String ( "url" , e . urlWS ) )
2020-11-16 04:28:07 -08:00
2020-11-16 07:59:58 -08:00
c , _ , err := websocket . DefaultDialer . DialContext ( ctx , e . urlWS , nil )
2020-11-16 04:28:07 -08:00
if err != nil {
2022-06-16 12:17:43 -07:00
p2p . DefaultRegistry . AddErrorCount ( e . chainID , 1 )
2022-07-06 07:19:57 -07:00
connectionErrors . WithLabelValues ( networkName , "websocket_dial_error" ) . Inc ( )
2020-11-16 07:59:58 -08:00
return fmt . Errorf ( "websocket dial failed: %w" , err )
2020-11-16 04:28:07 -08:00
}
defer c . Close ( )
// Subscribe to smart contract transactions
2022-06-16 12:17:43 -07:00
params := [ ... ] string { fmt . Sprintf ( "tm.event='Tx' AND %s='%s'" , e . contractAddressFilterKey , e . contract ) }
2020-11-16 04:28:07 -08:00
command := & clientRequest {
JSONRPC : "2.0" ,
Method : "subscribe" ,
Params : params ,
ID : 1 ,
}
err = c . WriteJSON ( command )
if err != nil {
2022-06-16 12:17:43 -07:00
p2p . DefaultRegistry . AddErrorCount ( e . chainID , 1 )
2022-07-06 07:19:57 -07:00
connectionErrors . WithLabelValues ( networkName , "websocket_subscription_error" ) . Inc ( )
2020-11-16 04:28:07 -08:00
return fmt . Errorf ( "websocket subscription failed: %w" , err )
}
// Wait for the success response
_ , _ , err = c . ReadMessage ( )
if err != nil {
2022-06-16 12:17:43 -07:00
p2p . DefaultRegistry . AddErrorCount ( e . chainID , 1 )
2022-07-06 07:19:57 -07:00
connectionErrors . WithLabelValues ( networkName , "event_subscription_error" ) . Inc ( )
2020-11-16 04:28:07 -08:00
return fmt . Errorf ( "event subscription failed: %w" , err )
}
2022-07-06 07:19:57 -07:00
logger . Info ( "subscribed to new transaction events" , zap . String ( "network" , networkName ) )
2020-11-16 04:28:07 -08:00
2022-06-16 12:17:43 -07:00
readiness . SetReady ( e . readiness )
2020-11-27 15:46:37 -08:00
2021-02-05 06:16:31 -08:00
go func ( ) {
t := time . NewTicker ( 5 * time . Second )
client := & http . Client {
Timeout : time . Second * 5 ,
}
for {
<- t . C
2022-07-14 10:41:31 -07:00
msm := time . Now ( )
2022-07-06 07:19:57 -07:00
// Query and report height and set currentSlotHeight
2022-07-14 10:41:31 -07:00
resp , err := client . Get ( fmt . Sprintf ( "%s/%s" , e . urlLCD , e . latestBlockURL ) )
2021-02-05 06:16:31 -08:00
if err != nil {
2022-07-06 07:19:57 -07:00
logger . Error ( "query latest block response error" , zap . String ( "network" , networkName ) , zap . Error ( err ) )
2021-02-05 06:16:31 -08:00
continue
}
2022-11-28 05:48:27 -08:00
blocksBody , err := io . ReadAll ( resp . Body )
2021-02-05 06:16:31 -08:00
if err != nil {
2022-07-06 07:19:57 -07:00
logger . Error ( "query latest block response read error" , zap . String ( "network" , networkName ) , zap . Error ( err ) )
2021-02-05 06:16:31 -08:00
errC <- err
resp . Body . Close ( )
continue
}
resp . Body . Close ( )
2022-07-14 10:41:31 -07:00
// Update the prom metrics with how long the http request took to the rpc
queryLatency . WithLabelValues ( networkName , "block_latest" ) . Observe ( time . Since ( msm ) . Seconds ( ) )
2021-02-05 06:16:31 -08:00
blockJSON := string ( blocksBody )
latestBlock := gjson . Get ( blockJSON , "block.header.height" )
2022-07-06 07:19:57 -07:00
logger . Info ( "current height" , zap . String ( "network" , networkName ) , zap . Int64 ( "block" , latestBlock . Int ( ) ) )
currentSlotHeight . WithLabelValues ( networkName ) . Set ( float64 ( latestBlock . Int ( ) ) )
2022-06-16 12:17:43 -07:00
p2p . DefaultRegistry . SetNetworkStats ( e . chainID , & gossipv1 . Heartbeat_Network {
2021-12-23 14:59:32 -08:00
Height : latestBlock . Int ( ) ,
2021-08-30 07:19:00 -07:00
ContractAddress : e . contract ,
2021-02-09 17:00:45 -08:00
} )
2021-02-05 06:16:31 -08:00
}
} ( )
2022-02-09 09:54:33 -08:00
go func ( ) {
for {
select {
case <- ctx . Done ( ) :
return
case r := <- e . obsvReqC :
2022-06-16 12:17:43 -07:00
if vaa . ChainID ( r . ChainId ) != e . chainID {
2022-02-09 09:54:33 -08:00
panic ( "invalid chain ID" )
}
tx := hex . EncodeToString ( r . TxHash )
2022-07-06 07:19:57 -07:00
logger . Info ( "received observation request" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , tx ) )
2022-02-09 09:54:33 -08:00
client := & http . Client {
Timeout : time . Second * 5 ,
}
// Query for tx by hash
resp , err := client . Get ( fmt . Sprintf ( "%s/cosmos/tx/v1beta1/txs/%s" , e . urlLCD , tx ) )
if err != nil {
2022-07-06 07:19:57 -07:00
logger . Error ( "query tx response error" , zap . String ( "network" , networkName ) , zap . Error ( err ) )
2022-02-09 09:54:33 -08:00
continue
}
2022-11-28 05:48:27 -08:00
txBody , err := io . ReadAll ( resp . Body )
2022-02-09 09:54:33 -08:00
if err != nil {
2022-07-06 07:19:57 -07:00
logger . Error ( "query tx response read error" , zap . String ( "network" , networkName ) , zap . Error ( err ) )
2022-02-09 09:54:33 -08:00
resp . Body . Close ( )
continue
}
resp . Body . Close ( )
txJSON := string ( txBody )
txHashRaw := gjson . Get ( txJSON , "tx_response.txhash" )
if ! txHashRaw . Exists ( ) {
2022-07-06 07:19:57 -07:00
logger . Error ( "tx does not have tx hash" , zap . String ( "network" , networkName ) , zap . String ( "payload" , txJSON ) )
2022-02-09 09:54:33 -08:00
continue
}
txHash := txHashRaw . String ( )
events := gjson . Get ( txJSON , "tx_response.events" )
if ! events . Exists ( ) {
2022-07-06 07:19:57 -07:00
logger . Error ( "tx has no events" , zap . String ( "network" , networkName ) , zap . String ( "payload" , txJSON ) )
2022-02-09 09:54:33 -08:00
continue
}
2022-06-16 12:17:43 -07:00
msgs := EventsToMessagePublications ( e . contract , txHash , events . Array ( ) , logger , e . chainID , e . contractAddressLogKey )
2022-02-09 09:54:33 -08:00
for _ , msg := range msgs {
e . msgChan <- msg
2022-07-06 07:19:57 -07:00
messagesConfirmed . WithLabelValues ( networkName ) . Inc ( )
2022-02-09 09:54:33 -08:00
}
}
}
} ( )
2020-11-16 04:28:07 -08:00
go func ( ) {
defer close ( errC )
for {
_ , message , err := c . ReadMessage ( )
if err != nil {
2022-06-16 12:17:43 -07:00
p2p . DefaultRegistry . AddErrorCount ( e . chainID , 1 )
2022-07-06 07:19:57 -07:00
connectionErrors . WithLabelValues ( networkName , "channel_read_error" ) . Inc ( )
logger . Error ( "error reading channel" , zap . String ( "network" , networkName ) , zap . Error ( err ) )
2020-11-16 04:28:07 -08:00
errC <- err
return
}
// Received a message from the blockchain
json := string ( message )
2021-12-23 14:59:32 -08:00
txHashRaw := gjson . Get ( json , "result.events.tx\\.hash.0" )
if ! txHashRaw . Exists ( ) {
2022-07-06 07:19:57 -07:00
logger . Warn ( "message does not have tx hash" , zap . String ( "network" , networkName ) , zap . String ( "payload" , json ) )
2021-12-23 14:59:32 -08:00
continue
}
txHash := txHashRaw . String ( )
events := gjson . Get ( json , "result.data.value.TxResult.result.events" )
if ! events . Exists ( ) {
2022-07-06 07:19:57 -07:00
logger . Warn ( "message has no events" , zap . String ( "network" , networkName ) , zap . String ( "payload" , json ) )
2021-12-23 14:59:32 -08:00
continue
}
2022-06-16 12:17:43 -07:00
msgs := EventsToMessagePublications ( e . contract , txHash , events . Array ( ) , logger , e . chainID , e . contractAddressLogKey )
2022-02-09 09:54:33 -08:00
for _ , msg := range msgs {
e . msgChan <- msg
2022-07-06 07:19:57 -07:00
messagesConfirmed . WithLabelValues ( networkName ) . Inc ( )
2020-11-16 04:28:07 -08:00
}
2020-11-16 04:23:29 -08:00
// We do not send guardian changes to the processor - ETH guardians are the source of truth.
2020-11-16 04:28:07 -08:00
}
} ( )
select {
case <- ctx . Done ( ) :
err := c . WriteMessage ( websocket . CloseMessage , websocket . FormatCloseMessage ( websocket . CloseNormalClosure , "" ) )
if err != nil {
2022-07-06 07:19:57 -07:00
logger . Error ( "error on closing socket " , zap . String ( "network" , networkName ) , zap . Error ( err ) )
2020-11-16 04:28:07 -08:00
}
return ctx . Err ( )
case err := <- errC :
return err
}
}
2022-06-16 12:17:43 -07:00
func EventsToMessagePublications ( contract string , txHash string , events [ ] gjson . Result , logger * zap . Logger , chainID vaa . ChainID , contractAddressKey string ) [ ] * common . MessagePublication {
2022-07-06 07:19:57 -07:00
networkName := vaa . ChainID ( chainID ) . String ( )
2022-02-09 09:54:33 -08:00
msgs := make ( [ ] * common . MessagePublication , 0 , len ( events ) )
for _ , event := range events {
if ! event . IsObject ( ) {
2022-07-06 07:19:57 -07:00
logger . Warn ( "event is invalid" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "event" , event . String ( ) ) )
2022-02-09 09:54:33 -08:00
continue
}
eventType := gjson . Get ( event . String ( ) , "type" )
if eventType . String ( ) != "wasm" {
continue
}
attributes := gjson . Get ( event . String ( ) , "attributes" )
if ! attributes . Exists ( ) {
2022-07-06 07:19:57 -07:00
logger . Warn ( "message event has no attributes" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "event" , event . String ( ) ) )
2022-02-09 09:54:33 -08:00
continue
}
mappedAttributes := map [ string ] string { }
for _ , attribute := range attributes . Array ( ) {
if ! attribute . IsObject ( ) {
2022-07-06 07:19:57 -07:00
logger . Warn ( "event attribute is invalid" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "attribute" , attribute . String ( ) ) )
2022-02-09 09:54:33 -08:00
continue
}
keyBase := gjson . Get ( attribute . String ( ) , "key" )
if ! keyBase . Exists ( ) {
2022-07-06 07:19:57 -07:00
logger . Warn ( "event attribute does not have key" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "attribute" , attribute . String ( ) ) )
2022-02-09 09:54:33 -08:00
continue
}
valueBase := gjson . Get ( attribute . String ( ) , "value" )
if ! valueBase . Exists ( ) {
2022-07-06 07:19:57 -07:00
logger . Warn ( "event attribute does not have value" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "attribute" , attribute . String ( ) ) )
2022-02-09 09:54:33 -08:00
continue
}
key , err := base64 . StdEncoding . DecodeString ( keyBase . String ( ) )
if err != nil {
2022-07-06 07:19:57 -07:00
logger . Warn ( "event key attribute is invalid" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "key" , keyBase . String ( ) ) )
2022-02-09 09:54:33 -08:00
continue
}
value , err := base64 . StdEncoding . DecodeString ( valueBase . String ( ) )
if err != nil {
2022-07-06 07:19:57 -07:00
logger . Warn ( "event value attribute is invalid" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "key" , keyBase . String ( ) ) , zap . String ( "value" , valueBase . String ( ) ) )
2022-02-09 09:54:33 -08:00
continue
}
if _ , ok := mappedAttributes [ string ( key ) ] ; ok {
2022-07-06 07:19:57 -07:00
logger . Debug ( "duplicate key in events" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "key" , keyBase . String ( ) ) , zap . String ( "value" , valueBase . String ( ) ) )
2022-02-09 09:54:33 -08:00
continue
}
mappedAttributes [ string ( key ) ] = string ( value )
}
2022-06-16 12:17:43 -07:00
contractAddress , ok := mappedAttributes [ contractAddressKey ]
2022-02-09 09:54:33 -08:00
if ! ok {
2022-07-06 07:19:57 -07:00
logger . Warn ( "wasm event without contract address field set" , zap . String ( "network" , networkName ) , zap . String ( "event" , event . String ( ) ) )
2022-02-09 09:54:33 -08:00
continue
}
// This is not a wormhole message
if contractAddress != contract {
continue
}
payload , ok := mappedAttributes [ "message.message" ]
if ! ok {
2022-07-06 07:19:57 -07:00
logger . Error ( "wormhole event does not have a message field" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "attributes" , attributes . String ( ) ) )
2022-02-09 09:54:33 -08:00
continue
}
sender , ok := mappedAttributes [ "message.sender" ]
if ! ok {
2022-07-06 07:19:57 -07:00
logger . Error ( "wormhole event does not have a sender field" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "attributes" , attributes . String ( ) ) )
2022-02-09 09:54:33 -08:00
continue
}
chainId , ok := mappedAttributes [ "message.chain_id" ]
if ! ok {
2022-07-06 07:19:57 -07:00
logger . Error ( "wormhole event does not have a chain_id field" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "attributes" , attributes . String ( ) ) )
2022-02-09 09:54:33 -08:00
continue
}
nonce , ok := mappedAttributes [ "message.nonce" ]
if ! ok {
2022-07-06 07:19:57 -07:00
logger . Error ( "wormhole event does not have a nonce field" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "attributes" , attributes . String ( ) ) )
2022-02-09 09:54:33 -08:00
continue
}
sequence , ok := mappedAttributes [ "message.sequence" ]
if ! ok {
2022-07-06 07:19:57 -07:00
logger . Error ( "wormhole event does not have a sequence field" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "attributes" , attributes . String ( ) ) )
2022-02-09 09:54:33 -08:00
continue
}
blockTime , ok := mappedAttributes [ "message.block_time" ]
if ! ok {
2022-07-06 07:19:57 -07:00
logger . Error ( "wormhole event does not have a block_time field" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "attributes" , attributes . String ( ) ) )
2022-02-09 09:54:33 -08:00
continue
}
2022-09-28 06:27:13 -07:00
logger . Info ( "new message detected on cosmwasm" ,
2022-07-06 07:19:57 -07:00
zap . String ( "network" , networkName ) ,
2022-02-09 09:54:33 -08:00
zap . String ( "chainId" , chainId ) ,
zap . String ( "txHash" , txHash ) ,
zap . String ( "sender" , sender ) ,
zap . String ( "nonce" , nonce ) ,
zap . String ( "sequence" , sequence ) ,
zap . String ( "blockTime" , blockTime ) ,
)
senderAddress , err := StringToAddress ( sender )
if err != nil {
2022-07-06 07:19:57 -07:00
logger . Error ( "cannot decode emitter hex" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "value" , sender ) )
2022-02-09 09:54:33 -08:00
continue
}
txHashValue , err := StringToHash ( txHash )
if err != nil {
2022-07-06 07:19:57 -07:00
logger . Error ( "cannot decode tx hash hex" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "value" , txHash ) )
2022-02-09 09:54:33 -08:00
continue
}
payloadValue , err := hex . DecodeString ( payload )
if err != nil {
2022-07-06 07:19:57 -07:00
logger . Error ( "cannot decode payload" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "value" , payload ) )
2022-02-09 09:54:33 -08:00
continue
}
blockTimeInt , err := strconv . ParseInt ( blockTime , 10 , 64 )
if err != nil {
2022-07-06 07:19:57 -07:00
logger . Error ( "blocktime cannot be parsed as int" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "value" , blockTime ) )
2022-02-09 09:54:33 -08:00
continue
}
nonceInt , err := strconv . ParseUint ( nonce , 10 , 32 )
if err != nil {
2022-07-06 07:19:57 -07:00
logger . Error ( "nonce cannot be parsed as int" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "value" , blockTime ) )
2022-02-09 09:54:33 -08:00
continue
}
sequenceInt , err := strconv . ParseUint ( sequence , 10 , 64 )
if err != nil {
2022-07-06 07:19:57 -07:00
logger . Error ( "sequence cannot be parsed as int" , zap . String ( "network" , networkName ) , zap . String ( "tx_hash" , txHash ) , zap . String ( "value" , blockTime ) )
2022-02-09 09:54:33 -08:00
continue
}
messagePublication := & common . MessagePublication {
TxHash : txHashValue ,
Timestamp : time . Unix ( blockTimeInt , 0 ) ,
Nonce : uint32 ( nonceInt ) ,
Sequence : sequenceInt ,
2022-06-16 12:17:43 -07:00
EmitterChain : chainID ,
2022-02-09 09:54:33 -08:00
EmitterAddress : senderAddress ,
Payload : payloadValue ,
ConsistencyLevel : 0 , // Instant finality
}
msgs = append ( msgs , messagePublication )
}
return msgs
}
2020-11-16 04:28:07 -08:00
// StringToAddress convert string into address
func StringToAddress ( value string ) ( vaa . Address , error ) {
var address vaa . Address
res , err := hex . DecodeString ( value )
if err != nil {
return address , err
}
copy ( address [ : ] , res )
return address , nil
}
// StringToHash convert string into transaction hash
func StringToHash ( value string ) ( eth_common . Hash , error ) {
var hash eth_common . Hash
res , err := hex . DecodeString ( value )
if err != nil {
return hash , err
}
copy ( hash [ : ] , res )
return hash , nil
}