Przeglądaj źródła

node/pkg/ethereum: add MessageEventsForTransaction

This retrieves a single transaction's MessagePublication events. This
has the same security assumptions than listening to the log events -
namely, ensuring the right contract has emitted them.

Tested locally with a mainnet transaction.

commit-id:64347ecc
Leo 3 lat temu
rodzic
commit
8f63e123a5

+ 59 - 0
node/hack/parse_eth_tx/parse_eth_tx.go

@@ -0,0 +1,59 @@
+package main
+
+import (
+	"context"
+	"flag"
+	"github.com/certusone/wormhole/node/pkg/ethereum"
+	"github.com/certusone/wormhole/node/pkg/vaa"
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/ethclient"
+	"log"
+)
+
+var (
+	flagEthRPC       = flag.String("ethRPC", "http://localhost:8545", "Ethereum JSON-RPC endpoint")
+	flagContractAddr = flag.String("contractAddr", "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B", "Ethereum contract address")
+	flagTx           = flag.String("tx", "", "Transaction to parse")
+)
+
+func main() {
+	flag.Parse()
+	if *flagTx == "" {
+		log.Fatal("No transaction specified")
+	}
+
+	ctx := context.Background()
+
+	c, err := ethclient.DialContext(ctx, *flagEthRPC)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	contractAddr := common.HexToAddress(*flagContractAddr)
+	transactionHash := common.HexToHash(*flagTx)
+
+	msgs, err := ethereum.MessageEventsForTransaction(ctx, c, contractAddr, vaa.ChainIDEthereum, transactionHash)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	for _, k := range msgs {
+		v := &vaa.VAA{
+			Version:          vaa.SupportedVAAVersion,
+			GuardianSetIndex: 1,
+			Signatures:       nil,
+			Timestamp:        k.Timestamp,
+			Nonce:            k.Nonce,
+			EmitterChain:     k.EmitterChain,
+			EmitterAddress:   k.EmitterAddress,
+			Payload:          k.Payload,
+			Sequence:         k.Sequence,
+			ConsistencyLevel: k.ConsistencyLevel,
+		}
+
+		log.Println("------------------------------------------------------")
+		log.Printf("Message ID: %s", v.MessageID())
+		log.Printf("Digest: %s", v.HexDigest())
+		log.Printf("VAA: %+v", v)
+	}
+}

+ 82 - 0
node/pkg/ethereum/by_transaction.go

@@ -0,0 +1,82 @@
+package ethereum
+
+import (
+	"context"
+	"fmt"
+	"github.com/certusone/wormhole/node/pkg/common"
+	"github.com/certusone/wormhole/node/pkg/ethereum/abi"
+	"github.com/certusone/wormhole/node/pkg/vaa"
+	eth_common "github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/ethclient"
+	"time"
+)
+
+var (
+	// SECURITY: Hardcoded ABI identifier for the LogMessagePublished topic. When using the watcher, we don't need this
+	// since the node will only hand us pre-filtered events. In this case, we need to manually verify it
+	// since ParseLogMessagePublished will only verify whether it parses.
+	logMessagePublishedTopic = eth_common.HexToHash("0x6eb224fb001ed210e379b335e35efe88672a8ce935d981a6896b27ffdf52a3b2")
+)
+
+func MessageEventsForTransaction(
+	ctx context.Context,
+	c *ethclient.Client,
+	contract eth_common.Address,
+	chainId vaa.ChainID,
+	tx eth_common.Hash) ([]*common.MessagePublication, error) {
+
+	f, err := abi.NewAbiFilterer(contract, c)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create ABI filterer: %w", err)
+	}
+
+	// Get transactions logs from transaction
+	receipt, err := c.TransactionReceipt(ctx, tx)
+	if err != nil {
+		return nil, fmt.Errorf("failed to get transaction receipt: %w", err)
+	}
+
+	// Get block
+	block, err := c.BlockByHash(ctx, receipt.BlockHash)
+	if err != nil {
+		return nil, fmt.Errorf("failed to get block: %w", err)
+	}
+
+	msgs := make([]*common.MessagePublication, 0, len(receipt.Logs))
+
+	// Extract logs
+	for _, l := range receipt.Logs {
+		// SECURITY: Skip logs not produced by our contract.
+		if l.Address != contract {
+			continue
+		}
+
+		if l == nil {
+			continue
+		}
+
+		if l.Topics[0] != logMessagePublishedTopic {
+			continue
+		}
+
+		ev, err := f.ParseLogMessagePublished(*l)
+		if err != nil {
+			return nil, fmt.Errorf("failed to parse log: %w", err)
+		}
+
+		message := &common.MessagePublication{
+			TxHash:           ev.Raw.TxHash,
+			Timestamp:        time.Unix(int64(block.Time()), 0),
+			Nonce:            ev.Nonce,
+			Sequence:         ev.Sequence,
+			EmitterChain:     chainId,
+			EmitterAddress:   PadAddress(ev.Sender),
+			Payload:          ev.Payload,
+			ConsistencyLevel: ev.ConsistencyLevel,
+		}
+
+		msgs = append(msgs, message)
+	}
+
+	return msgs, nil
+}