diff --git a/cmd/resign/emit_withdrawals/emit_withdrawals.go b/cmd/resign/emit_withdrawals/emit_withdrawals.go new file mode 100644 index 00000000000..40b0e332233 --- /dev/null +++ b/cmd/resign/emit_withdrawals/emit_withdrawals.go @@ -0,0 +1,197 @@ +// Copyright (C) 2023 Gobalsky Labs Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package emit_withdrawals + +import ( + "bytes" + "encoding/csv" + "flag" + "fmt" + "log" + "os" + "sort" + "strconv" + "time" + + "code.vegaprotocol.io/vega/core/bridges" + "code.vegaprotocol.io/vega/core/config" + "code.vegaprotocol.io/vega/core/nodewallets" + "code.vegaprotocol.io/vega/libs/num" + "code.vegaprotocol.io/vega/paths" +) + +const ( + addressIndex = 1 + assetIndex = 2 + amountIndex = 4 + creationIndex = 5 + nonceIndex = 6 +) + +var ( + bundlesPath string + out string + passphrase config.Passphrase + home string +) + +var ( + bridgeAddressMapping = map[string]string{ + "WETH": "0x23872549cE10B40e31D6577e0A920088B0E0666a", + "USDT ETH": "0x23872549cE10B40e31D6577e0A920088B0E0666a", + "USDT ARB": "0x475B597652bCb2769949FD6787b1DC6916518407", + "USDC": "0x23872549cE10B40e31D6577e0A920088B0E0666a", + } + + assetAddressMapping = map[string]string{ + "WETH": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "USDT ETH": "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "USDT ARB": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", + "USDC": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + } + + bridgeChainIDMapping = map[string]string{ + "0x23872549cE10B40e31D6577e0A920088B0E0666a": "", + "0x475B597652bCb2769949FD6787b1DC6916518407": "42161", + } +) + +func init() { + flag.StringVar(&out, "out", "rebundled.csv", "where to store the outputs rebundled signatures") + flag.StringVar(&home, "home", "", "path to the vega home root") + flag.StringVar((*string)(&passphrase), "passphrase", "", "passphrase of the node wallet") + flag.StringVar(&bundlesPath, "bundles", "", "path to the signatures bundles (required)") +} + +func readCSV(path string) [][]string { + buf, err := os.ReadFile(path) + if err != nil { + log.Fatalf("couldn't open the bundles file: %v", err) + } + + r := csv.NewReader(bytes.NewReader(buf)) + records, err := r.ReadAll() + if err != nil { + log.Fatal(err) + } + + return records[1:] +} + +func signBundle( + s bridges.Signer, + token, amountStr, receiver, creationStr, nonceStr string, +) string { + var ( + bridgeAddress = bridgeAddressMapping[token] + tokenAddress = assetAddressMapping[token] + chainID = bridgeChainIDMapping[bridgeAddress] + erc20Logic = bridges.NewERC20Logic(s, bridgeAddress, chainID, chainID == "") + ) + + nonce, overflowed := num.UintFromString(nonceStr, 10) + if overflowed { + log.Fatalf("invalid nonce, needs to be base 10, got: %v", nonceStr) + } + + amount, overflowed := num.UintFromString(amountStr, 10) + if overflowed { + log.Fatalf("invalid amount, needs to be base 10, got: %v", amountStr) + } + + creationInt64, err := strconv.ParseInt(creationStr, 10, 64) + if err != nil { + panic(err) + } + creation := time.Unix(creationInt64, 0) + + bundle, err := erc20Logic.WithdrawAsset( + tokenAddress, amount, receiver, creation, nonce, + ) + if err != nil { + log.Fatalf("couldn't sign bundle: %v", err) + } + + return fmt.Sprintf("0x%v", bundle.Signature.Hex()) +} + +func signAllBundles(s bridges.Signer, bundles [][]string) [][]string { + entries := [][]string{} + for _, entry := range bundles { + signature := signBundle(s, + entry[assetIndex], + entry[amountIndex], + entry[addressIndex], + entry[creationIndex], + entry[nonceIndex], + ) + + entries = append(entries, append(entry, signature)) + } + + return entries +} + +func Main() { + flag.Parse() + + if len(bundlesPath) <= 0 { + log.Fatal("-bundles argument is required") + } + + bundles := readCSV(bundlesPath) + + pass, err := passphrase.Get("node wallet", false) + if err != nil { + log.Fatalf("couldn't get the node wallet passphrase: %v", err) + } + + vegaPaths := paths.New(home) + if _, _, err := config.EnsureNodeConfig(vegaPaths); err != nil { + log.Fatalf("couldn't load vega configuration: %v", err) + } + + s, err := nodewallets.GetEthereumWallet(vegaPaths, pass) + if err != nil { + log.Fatalf("couldn't get Ethereum node wallet: %v", err) + } + + output := signAllBundles(s, bundles) + + // just sort it to get the same output across nodes + sort.Slice(output, func(i, j int) bool { + return output[i][creationIndex] < output[j][creationIndex] + }) + + // then add headers + output = append([][]string{ + { + "party", "address", "asset", "realAmount", "amount", "createdTimestamp", "nonce", "signature", + }, + }, output...) + + f, err := os.Create(out) + if err != nil { + log.Fatalf("couldn't create file: %v", err) + } + + w := csv.NewWriter(f) + w.WriteAll(output) + + if err := w.Error(); err != nil { + log.Fatalf("error writing output:", err) + } +} diff --git a/cmd/resign/main.go b/cmd/resign/main.go new file mode 100644 index 00000000000..9ef9d84fee4 --- /dev/null +++ b/cmd/resign/main.go @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Gobalsky Labs Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package main + +import "code.vegaprotocol.io/vega/cmd/resign/emit_withdrawals" + +func main() { + emit_withdrawals.Main() +} diff --git a/cmd/vega/commands/emit_withdrawals.go b/cmd/vega/commands/emit_withdrawals.go new file mode 100644 index 00000000000..1502215e2a7 --- /dev/null +++ b/cmd/vega/commands/emit_withdrawals.go @@ -0,0 +1,45 @@ +// Copyright (C) 2023 Gobalsky Labs Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package commands + +import ( + "context" + "os" + + "code.vegaprotocol.io/vega/cmd/resign/emit_withdrawals" + + "github.com/jessevdk/go-flags" +) + +type emitWithdrawalsCmd struct{} + +func (opts *emitWithdrawalsCmd) Execute(_ []string) error { + os.Args = os.Args[1:] + emit_withdrawals.Main() + + return nil +} + +func EmitWithdrawals(ctx context.Context, parser *flags.Parser) error { + _, err := parser.AddCommand( + "emit_withdrawals", + "Emit unclaimed withdrawals on vega", + "Emit unclaimed withdrawals on vega", + &emitWithdrawalsCmd{}, + ) + + return err +} diff --git a/cmd/vega/commands/root.go b/cmd/vega/commands/root.go index ab7899f525c..508482eb732 100644 --- a/cmd/vega/commands/root.go +++ b/cmd/vega/commands/root.go @@ -49,6 +49,8 @@ func Main(ctx context.Context) error { switch os.Args[1] { case "tendermint", "tm", "cometbft": return (&cometbftCmd{}).Execute(nil) + case "emit_withdrawals": + return (&emitWithdrawalsCmd{}).Execute(nil) case "wallet": return (&walletCmd{}).Execute(nil) case "datanode": @@ -84,6 +86,7 @@ func Main(ctx context.Context) error { Start, Node, BlockExplorer, + EmitWithdrawals, ); err != nil { _, _ = fmt.Fprintln(os.Stderr, err) return err