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
This commit is contained in:
parent
9771128e59
commit
8f63e123a5
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue