2022-02-14 18:07:36 -08:00
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"flag"
|
|
|
|
|
"fmt"
|
2022-02-15 13:51:30 -08:00
|
|
|
|
"log"
|
|
|
|
|
"net/http"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
2022-02-14 18:07:36 -08:00
|
|
|
|
"github.com/certusone/wormhole/node/pkg/common"
|
|
|
|
|
"github.com/certusone/wormhole/node/pkg/db"
|
|
|
|
|
"github.com/certusone/wormhole/node/pkg/ethereum/abi"
|
|
|
|
|
gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1"
|
|
|
|
|
nodev1 "github.com/certusone/wormhole/node/pkg/proto/node/v1"
|
|
|
|
|
"github.com/certusone/wormhole/node/pkg/vaa"
|
|
|
|
|
abi2 "github.com/ethereum/go-ethereum/accounts/abi"
|
|
|
|
|
eth_common "github.com/ethereum/go-ethereum/common"
|
|
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
|
|
|
"golang.org/x/time/rate"
|
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var etherscanAPIMap = map[vaa.ChainID]string{
|
|
|
|
|
vaa.ChainIDEthereum: "https://api.etherscan.io/api",
|
|
|
|
|
vaa.ChainIDBSC: "https://api.bscscan.com/api",
|
|
|
|
|
vaa.ChainIDAvalanche: "https://api.snowtrace.io/api",
|
|
|
|
|
vaa.ChainIDPolygon: "https://api.polygonscan.com/api",
|
2022-02-15 13:51:30 -08:00
|
|
|
|
vaa.ChainIDOasis: "https://explorer.emerald.oasis.dev/api",
|
2022-04-11 13:27:20 -07:00
|
|
|
|
vaa.ChainIDAurora: "https://explorer.mainnet.aurora.dev/api",
|
2022-02-14 18:07:36 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var coreContractMap = map[vaa.ChainID]string{
|
|
|
|
|
vaa.ChainIDEthereum: "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B",
|
|
|
|
|
vaa.ChainIDBSC: "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B",
|
|
|
|
|
vaa.ChainIDAvalanche: "0x54a8e5f9c4CbA08F9943965859F6c34eAF03E26c",
|
|
|
|
|
vaa.ChainIDPolygon: "0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7",
|
2022-02-15 13:51:30 -08:00
|
|
|
|
vaa.ChainIDOasis: "0xfe8cd454b4a1ca468b57d79c0cc77ef5b6f64585", // <- converted to all lower case for easy compares
|
2022-04-11 13:27:20 -07:00
|
|
|
|
vaa.ChainIDAurora: "0xa321448d90d4e5b0a732867c18ea198e75cac48e",
|
2022-02-14 18:07:36 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
adminRPC = flag.String("adminRPC", "/run/guardiand/admin.socket", "Admin RPC address")
|
|
|
|
|
etherscanKey = flag.String("etherscanKey", "", "Etherscan API Key")
|
|
|
|
|
chain = flag.String("chain", "ethereum", "Eth Chain name")
|
|
|
|
|
dryRun = flag.Bool("dryRun", true, "Dry run")
|
|
|
|
|
step = flag.Uint64("step", 10000, "Step")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
tokenLockupTopic = eth_common.HexToHash("0x6eb224fb001ed210e379b335e35efe88672a8ce935d981a6896b27ffdf52a3b2")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func getAdminClient(ctx context.Context, addr string) (*grpc.ClientConn, error, nodev1.NodePrivilegedServiceClient) {
|
|
|
|
|
conn, err := grpc.DialContext(ctx, fmt.Sprintf("unix:///%s", addr), grpc.WithInsecure())
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("failed to connect to %s: %v", addr, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c := nodev1.NewNodePrivilegedServiceClient(conn)
|
|
|
|
|
return conn, err, c
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type logEntry struct {
|
|
|
|
|
// 0x98f3c9e6e3face36baad05fe09d375ef1464288b
|
|
|
|
|
Address string `json:"address"`
|
|
|
|
|
// [
|
|
|
|
|
// "0x6eb224fb001ed210e379b335e35efe88672a8ce935d981a6896b27ffdf52a3b2",
|
|
|
|
|
// "0x0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585"
|
|
|
|
|
// ]
|
|
|
|
|
Topics []string `json:"topics"`
|
|
|
|
|
// Hex-encoded log data
|
|
|
|
|
Data string `json:"data"`
|
|
|
|
|
// 0xcaebbf
|
|
|
|
|
BlockNumber string `json:"blockNumber"`
|
|
|
|
|
// 0x614fd32b
|
|
|
|
|
TimeStamp string `json:"timeStamp"`
|
|
|
|
|
// 0x960778c48
|
|
|
|
|
GasPrice string `json:"gasPrice"`
|
|
|
|
|
// 0x139d5
|
|
|
|
|
GasUsed string `json:"gasUsed"`
|
|
|
|
|
// 0x18d
|
|
|
|
|
LogIndex string `json:"logIndex"`
|
|
|
|
|
// 0xcc5d73aea74ffe6c8e5e9c212da7eb3ea334f41ac3fd600a9979de727535c849
|
|
|
|
|
TransactionHash string `json:"transactionHash"`
|
|
|
|
|
// 0x117
|
|
|
|
|
TransactionIndex string `json:"transactionIndex"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type logResponse struct {
|
|
|
|
|
// "1" if ok, "0" if error
|
|
|
|
|
Status string `json:"status"`
|
|
|
|
|
// "OK" if ok, "NOTOK" otherwise
|
|
|
|
|
Message string `json:"message"`
|
|
|
|
|
// String when status is "0", result type otherwise.
|
|
|
|
|
Result json.RawMessage `json:"result"`
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-15 13:51:30 -08:00
|
|
|
|
func getCurrentHeight(chainId vaa.ChainID, ctx context.Context, c *http.Client, api, key string) (uint64, error) {
|
2022-03-08 11:51:55 -08:00
|
|
|
|
var req *http.Request
|
|
|
|
|
var err error
|
2022-04-11 13:27:20 -07:00
|
|
|
|
if chainId == vaa.ChainIDOasis || chainId == vaa.ChainIDAurora {
|
|
|
|
|
// This is the BlockScout based explorer leg
|
2022-02-15 13:51:30 -08:00
|
|
|
|
req, err = http.NewRequest("GET", fmt.Sprintf("%s?module=block&action=eth_block_number", api), nil)
|
|
|
|
|
} else {
|
|
|
|
|
req, err = http.NewRequest("GET", fmt.Sprintf("%s?module=proxy&action=eth_blockNumber&apikey=%s", api, key), nil)
|
|
|
|
|
}
|
2022-02-14 18:07:36 -08:00
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resp, err := c.Do(req.WithContext(ctx))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, fmt.Errorf("failed to get current height: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
|
|
var r struct {
|
|
|
|
|
Result string `json:"result"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
|
|
|
|
|
return 0, fmt.Errorf("failed to decode response: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hexutil.DecodeUint64(r.Result)
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-15 13:51:30 -08:00
|
|
|
|
func getLogs(chainId vaa.ChainID, ctx context.Context, c *http.Client, api, key, contract, topic0 string, from, to string) ([]*logEntry, error) {
|
2022-03-08 11:51:55 -08:00
|
|
|
|
var req *http.Request
|
|
|
|
|
var err error
|
2022-04-11 13:27:20 -07:00
|
|
|
|
if chainId == vaa.ChainIDOasis || chainId == vaa.ChainIDAurora {
|
|
|
|
|
// This is the BlockScout based explorer leg
|
2022-02-15 13:51:30 -08:00
|
|
|
|
req, err = http.NewRequestWithContext(ctx, "GET", fmt.Sprintf(
|
|
|
|
|
"%s?module=logs&action=getLogs&fromBlock=%s&toBlock=%s&topic0=%s",
|
|
|
|
|
api, from, to, topic0), nil)
|
|
|
|
|
} else {
|
|
|
|
|
req, err = http.NewRequestWithContext(ctx, "GET", fmt.Sprintf(
|
|
|
|
|
"%s?module=logs&action=getLogs&fromBlock=%s&toBlock=%s&address=%s&topic0=%s&apikey=%s",
|
|
|
|
|
api, from, to, contract, topic0, key), nil)
|
|
|
|
|
}
|
2022-02-14 18:07:36 -08:00
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resp, err := c.Do(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to get logs: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
|
|
var r logResponse
|
|
|
|
|
|
|
|
|
|
if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to decode response: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if r.Status != "1" && r.Message != "No records found" {
|
|
|
|
|
var e string
|
|
|
|
|
_ = json.Unmarshal(r.Result, &e)
|
|
|
|
|
return nil, fmt.Errorf("failed to get logs (%s): %s", r.Message, e)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var logs []*logEntry
|
|
|
|
|
if err := json.Unmarshal(r.Result, &logs); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to unmarshal log entry: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-11 13:27:20 -07:00
|
|
|
|
if chainId == vaa.ChainIDOasis || chainId == vaa.ChainIDAurora {
|
|
|
|
|
// Because of a bug in BlockScout based explorers we need to check the address
|
|
|
|
|
// in the log to see if it is the core bridge
|
2022-02-15 13:51:30 -08:00
|
|
|
|
var filtered []*logEntry
|
|
|
|
|
for _, logLine := range logs {
|
|
|
|
|
// Check value of address in log
|
2022-03-08 11:51:55 -08:00
|
|
|
|
if logLine.Address == contract {
|
2022-02-15 13:51:30 -08:00
|
|
|
|
filtered = append(filtered, logLine)
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-08 11:51:55 -08:00
|
|
|
|
logs = filtered
|
2022-02-15 13:51:30 -08:00
|
|
|
|
}
|
|
|
|
|
|
2022-02-14 18:07:36 -08:00
|
|
|
|
return logs, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
flag.Parse()
|
|
|
|
|
chainID, err := vaa.ChainIDFromString(*chain)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("Invalid chain: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-15 13:51:30 -08:00
|
|
|
|
if *etherscanKey == "" {
|
2022-04-11 13:27:20 -07:00
|
|
|
|
// BlockScout based explorers don't require an ether scan key
|
|
|
|
|
if chainID != vaa.ChainIDOasis && chainID != vaa.ChainIDAurora {
|
2022-02-15 13:51:30 -08:00
|
|
|
|
log.Fatal("Etherscan API Key is required")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-14 18:07:36 -08:00
|
|
|
|
etherscanAPI, ok := etherscanAPIMap[chainID]
|
|
|
|
|
if !ok {
|
|
|
|
|
log.Fatalf("Unsupported chain: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
coreContract, ok := coreContractMap[chainID]
|
|
|
|
|
if !ok {
|
|
|
|
|
panic("no core contract")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
2022-02-15 13:51:30 -08:00
|
|
|
|
currentHeight, err := getCurrentHeight(chainID, ctx, http.DefaultClient, etherscanAPI, *etherscanKey)
|
2022-02-14 18:07:36 -08:00
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("Failed to get current height: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Printf("Current height: %d", currentHeight)
|
|
|
|
|
|
|
|
|
|
missingMessages := make(map[eth_common.Address]map[uint64]bool)
|
|
|
|
|
|
|
|
|
|
conn, err, admin := getAdminClient(ctx, *adminRPC)
|
|
|
|
|
defer conn.Close()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("failed to get admin client: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, emitter := range common.KnownEmitters {
|
|
|
|
|
if emitter.ChainID != chainID {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
contract := eth_common.HexToAddress(emitter.Emitter)
|
|
|
|
|
|
|
|
|
|
log.Printf("Requesting missing messages for %s (%v)", emitter.Emitter, contract)
|
|
|
|
|
|
|
|
|
|
msg := nodev1.FindMissingMessagesRequest{
|
|
|
|
|
EmitterChain: uint32(chainID),
|
|
|
|
|
EmitterAddress: emitter.Emitter,
|
|
|
|
|
RpcBackfill: true,
|
|
|
|
|
BackfillNodes: common.PublicRPCEndpoints,
|
|
|
|
|
}
|
|
|
|
|
resp, err := admin.FindMissingMessages(ctx, &msg)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("failed to run find FindMissingMessages RPC: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
msgs := make([]*db.VAAID, len(resp.MissingMessages))
|
|
|
|
|
for i, id := range resp.MissingMessages {
|
|
|
|
|
fmt.Println(id)
|
|
|
|
|
vId, err := db.VaaIDFromString(id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("failed to parse VAAID: %v", err)
|
|
|
|
|
}
|
|
|
|
|
msgs[i] = vId
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(msgs) == 0 {
|
|
|
|
|
log.Printf("No missing messages found for %s", emitter.Emitter)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lowest := msgs[0].Sequence
|
|
|
|
|
highest := msgs[len(msgs)-1].Sequence
|
|
|
|
|
|
|
|
|
|
log.Printf("Found %d missing messages for %s: %d – %d", len(msgs), emitter.Emitter, lowest, highest)
|
|
|
|
|
|
|
|
|
|
if _, ok := missingMessages[contract]; !ok {
|
|
|
|
|
missingMessages[contract] = make(map[uint64]bool)
|
|
|
|
|
}
|
|
|
|
|
for _, msg := range msgs {
|
|
|
|
|
missingMessages[contract][msg.Sequence] = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Press enter to continue if not in dryRun mode
|
|
|
|
|
if !*dryRun {
|
|
|
|
|
fmt.Println("Press enter to continue")
|
|
|
|
|
fmt.Scanln()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Printf("finding sequences")
|
|
|
|
|
|
|
|
|
|
limiter := rate.NewLimiter(rate.Every(1*time.Second), 1)
|
|
|
|
|
|
|
|
|
|
c := &http.Client{Timeout: 5 * time.Second}
|
|
|
|
|
|
|
|
|
|
ethAbi, err := abi2.JSON(strings.NewReader(abi.AbiABI))
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("failed to parse Eth ABI: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var lastHeight uint64
|
|
|
|
|
step := *step
|
|
|
|
|
for {
|
|
|
|
|
if err := limiter.Wait(ctx); err != nil {
|
|
|
|
|
log.Fatalf("failed to wait: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var from, to string
|
|
|
|
|
if lastHeight == 0 {
|
|
|
|
|
from = strconv.Itoa(int(currentHeight - step))
|
|
|
|
|
to = "latest"
|
|
|
|
|
lastHeight = currentHeight
|
|
|
|
|
} else {
|
|
|
|
|
from = strconv.Itoa(int(lastHeight - step))
|
|
|
|
|
to = strconv.Itoa(int(lastHeight))
|
|
|
|
|
}
|
|
|
|
|
lastHeight -= step
|
|
|
|
|
|
|
|
|
|
log.Printf("Requesting logs from block %s to %s", from, to)
|
|
|
|
|
|
2022-02-15 13:51:30 -08:00
|
|
|
|
logs, err := getLogs(chainID, ctx, c, etherscanAPI, *etherscanKey, coreContract, tokenLockupTopic.Hex(), from, to)
|
2022-02-14 18:07:36 -08:00
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("failed to get logs: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(logs) == 0 {
|
|
|
|
|
log.Printf("No logs found")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
firstBlock, err := hexutil.DecodeUint64(logs[0].BlockNumber)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("failed to decode block number: %v", err)
|
|
|
|
|
}
|
|
|
|
|
lastBlock, err := hexutil.DecodeUint64(logs[len(logs)-1].BlockNumber)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("failed to decode block number: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Printf("Got %d logs (first block: %d, last block: %d)",
|
|
|
|
|
len(logs), firstBlock, lastBlock)
|
|
|
|
|
|
|
|
|
|
if len(logs) >= 1000 {
|
|
|
|
|
// Bail if we exceeded the maximum number of logs returns in single API call -
|
|
|
|
|
// we might have skipped some and would have to make another call to get the rest.
|
|
|
|
|
//
|
|
|
|
|
// This is a one-off script, so we just set an appropriate interval and bail
|
|
|
|
|
// if we ever hit this.
|
|
|
|
|
log.Fatalf("Range exhausted - %d logs found", len(logs))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var min, max uint64
|
|
|
|
|
for _, l := range logs {
|
|
|
|
|
if eth_common.HexToHash(l.Topics[0]) != tokenLockupTopic {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b, err := hexutil.Decode(l.Data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("failed to decode log data for %s: %v", l.TransactionHash, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var seq uint64
|
|
|
|
|
if m, err := ethAbi.Unpack("LogMessagePublished", b); err != nil {
|
|
|
|
|
log.Fatalf("failed to unpack log data for %s: %v", l.TransactionHash, err)
|
|
|
|
|
} else {
|
|
|
|
|
seq = m[0].(uint64)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if seq < min || min == 0 {
|
|
|
|
|
min = seq
|
|
|
|
|
}
|
|
|
|
|
if seq > max {
|
|
|
|
|
max = seq
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emitter := eth_common.HexToAddress(l.Topics[1])
|
|
|
|
|
tx := eth_common.HexToHash(l.TransactionHash)
|
|
|
|
|
|
|
|
|
|
if _, ok := missingMessages[emitter]; !ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if !missingMessages[emitter][seq] {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Printf("Found missing message %d for %s in tx %s", seq, emitter, tx.Hex())
|
|
|
|
|
delete(missingMessages[emitter], seq)
|
|
|
|
|
|
|
|
|
|
if *dryRun {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Printf("Requesting re-observation for %s", tx.Hex())
|
|
|
|
|
|
|
|
|
|
_, err = admin.SendObservationRequest(ctx, &nodev1.SendObservationRequestRequest{
|
|
|
|
|
ObservationRequest: &gossipv1.ObservationRequest{
|
|
|
|
|
ChainId: uint32(chainID),
|
|
|
|
|
TxHash: tx.Bytes(),
|
|
|
|
|
}})
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("SendObservationRequest: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-08 11:51:55 -08:00
|
|
|
|
for i := 0; i < 10; i++ {
|
2022-02-14 18:07:36 -08:00
|
|
|
|
log.Printf("verifying %d", seq)
|
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf(
|
|
|
|
|
"%s/v1/signed_vaa/%d/%s/%d",
|
|
|
|
|
common.PublicRPCEndpoints[0],
|
|
|
|
|
chainID,
|
|
|
|
|
hex.EncodeToString(eth_common.LeftPadBytes(emitter.Bytes(), 32)),
|
|
|
|
|
seq), nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
resp, err := c.Do(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("verify: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
|
|
|
log.Printf("status %d, retrying", resp.StatusCode)
|
|
|
|
|
time.Sleep(5 * time.Second)
|
|
|
|
|
continue
|
|
|
|
|
} else {
|
|
|
|
|
log.Printf("success %d", seq)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Printf("Seq: %d - %d", min, max)
|
|
|
|
|
|
|
|
|
|
var total int
|
|
|
|
|
for em, entries := range missingMessages {
|
|
|
|
|
total += len(entries)
|
|
|
|
|
log.Printf("%d missing messages for %s left", len(entries), em.Hex())
|
|
|
|
|
}
|
|
|
|
|
if total == 0 {
|
|
|
|
|
log.Printf("No missing messages left")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|