From 396a25b3f8e2404d66b34aebab0876a74036986b Mon Sep 17 00:00:00 2001 From: kevinssgh <79858682+kevinssgh@users.noreply.github.com> Date: Fri, 9 Feb 2024 14:41:29 -0500 Subject: [PATCH 1/7] feat: added cmd to encrypt tss keyshare file (#1744) * added cmd to encrypt tss keyshare file, allowing empty tss password for backward compatibility. * add changelog + make generate * update go-tss version * use positional args instead * remove unnecessary struct * fix gosec issue --- changelog.md | 1 + cmd/zetaclientd/encrypt_tss.go | 67 ++++++++++++++++++++++++++++++++++ cmd/zetaclientd/start.go | 6 +-- go.mod | 4 +- go.sum | 8 +--- 5 files changed, 74 insertions(+), 12 deletions(-) create mode 100644 cmd/zetaclientd/encrypt_tss.go diff --git a/changelog.md b/changelog.md index d76a340213..6f09eadd3d 100644 --- a/changelog.md +++ b/changelog.md @@ -18,6 +18,7 @@ * [1712](https://github.com/zeta-chain/node/issues/1712) - increase EVM outtx inclusion timeout to 20 minutes * [1733](https://github.com/zeta-chain/node/pull/1733)) - remove the unnecessary 2x multiplier in the convertGasToZeta RPC * [1721](https://github.com/zeta-chain/node/issues/1721) - zetaclient should provide bitcoin_chain_id when querying TSS address +* [1744](https://github.com/zeta-chain/node/pull/1744) - added cmd to encrypt tss keyshare file, allowing empty tss password for backward compatibility. ### Tests diff --git a/cmd/zetaclientd/encrypt_tss.go b/cmd/zetaclientd/encrypt_tss.go new file mode 100644 index 0000000000..6fca9064cb --- /dev/null +++ b/cmd/zetaclientd/encrypt_tss.go @@ -0,0 +1,67 @@ +package main + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/sha256" + "encoding/json" + "errors" + "io" + "os" + "path/filepath" + + "github.com/spf13/cobra" +) + +var encTssCmd = &cobra.Command{ + Use: "tss-encrypt [file-path] [secret-key]", + Short: "Utility command to encrypt existing tss key-share file", + Args: cobra.ExactArgs(2), + RunE: EncryptTSSFile, +} + +func init() { + RootCmd.AddCommand(encTssCmd) +} + +func EncryptTSSFile(_ *cobra.Command, args []string) error { + filePath := args[0] + secretKey := args[1] + + filePath = filepath.Clean(filePath) + data, err := os.ReadFile(filePath) + if err != nil { + return err + } + + if !json.Valid(data) { + return errors.New("file does not contain valid json, may already be encrypted") + } + + block, err := aes.NewCipher(getFragmentSeed(secretKey)) + if err != nil { + return err + } + + // Creating GCM mode + gcm, err := cipher.NewGCM(block) + if err != nil { + return err + } + // Generating random nonce + nonce := make([]byte, gcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return err + } + + cipherText := gcm.Seal(nonce, nonce, data, nil) + return os.WriteFile(filePath, cipherText, 0o600) +} + +func getFragmentSeed(password string) []byte { + h := sha256.New() + h.Write([]byte(password)) + seed := h.Sum(nil) + return seed +} diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index 483cab01ee..72d6c43b3f 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -333,9 +333,9 @@ func promptPasswords() (string, string, error) { return "", "", err } - if TSSKeyPass == "" { - return "", "", errors.New("tss password is required to start zetaclient") - } + //trim delimiters + hotKeyPass = strings.TrimSuffix(hotKeyPass, "\n") + TSSKeyPass = strings.TrimSuffix(TSSKeyPass, "\n") return hotKeyPass, TSSKeyPass, err } diff --git a/go.mod b/go.mod index 95525856ce..6669039f30 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/rakyll/statik v0.1.7 github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 - github.com/zeta-chain/go-tss v0.1.1-0.20240103170132-35850edf5dbd + github.com/zeta-chain/go-tss v0.1.1-0.20240208222330-f3be0d4a0d98 github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2 github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20230816152528-db7d2bf9144b google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc @@ -338,8 +338,6 @@ replace ( // use cometbft github.com/tendermint/tendermint => github.com/cometbft/cometbft v0.34.28 github.com/tendermint/tm-db => github.com/BlockPILabs/cosmos-db v0.0.3 - github.com/zeta-chain/go-tss => github.com/zeta-chain/go-tss v0.1.1-0.20240115203400-a5b80e5da933 - ) replace github.com/cometbft/cometbft-db => github.com/notional-labs/cometbft-db v0.0.0-20230321185329-6dc7c0ca6345 diff --git a/go.sum b/go.sum index 5ebeddd613..7b2dc1998c 100644 --- a/go.sum +++ b/go.sum @@ -1848,7 +1848,6 @@ github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8 github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8= -github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= @@ -2761,7 +2760,6 @@ github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34c github.com/sourcegraph/go-diff v0.5.3/go.mod h1:v9JDtjCE4HHHCZGId75rg8gkKKa98RVjBcBGsVmMmak= github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= -github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -3035,10 +3033,8 @@ github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQ github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -github.com/zeta-chain/go-tss v0.1.1-0.20240103170132-35850edf5dbd h1:wv+VGLFX8IhPuoqAVQGAQjlEPWqYjowJgJVNReolJTM= -github.com/zeta-chain/go-tss v0.1.1-0.20240103170132-35850edf5dbd/go.mod h1:+lJfk/qqt+oxXeVuJV+PzpUoxftUfoTRf2eF3qlbyFI= -github.com/zeta-chain/go-tss v0.1.1-0.20240115203400-a5b80e5da933 h1:cx6ZXVmV9LpkYRQER7+sTgu56wdmaU1U5VJcx3rsCwc= -github.com/zeta-chain/go-tss v0.1.1-0.20240115203400-a5b80e5da933/go.mod h1:+lJfk/qqt+oxXeVuJV+PzpUoxftUfoTRf2eF3qlbyFI= +github.com/zeta-chain/go-tss v0.1.1-0.20240208222330-f3be0d4a0d98 h1:GCSRgszQbAR7h/qK0YKjlm1mcnZOaGMbztRLaAfoOx0= +github.com/zeta-chain/go-tss v0.1.1-0.20240208222330-f3be0d4a0d98/go.mod h1:+lJfk/qqt+oxXeVuJV+PzpUoxftUfoTRf2eF3qlbyFI= github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2 h1:gd2uE0X+ZbdFJ8DubxNqLbOVlCB12EgWdzSNRAR82tM= github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2/go.mod h1:x7Bkwbzt2W2lQfjOirnff0Dj+tykdbTG1FMJPVPZsvE= github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20230816152528-db7d2bf9144b h1:aZRt5BtXdoDdyrUKwcv3B7mS30m/B854cjKjmnXBE5A= From b85ba73544bd5fb9b13fefe1380e04a8a6cc2f00 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Mon, 12 Feb 2024 01:34:12 +0400 Subject: [PATCH 2/7] chore: add Ethermint endpoints to OpenAPI (#1736) * chore: add Ethermint endpoints to OpenAPI * changelog --------- Co-authored-by: brewmaster012 <88689859+brewmaster012@users.noreply.github.com> --- changelog.md | 4 + docs/openapi/openapi.swagger.yaml | 6146 +++++++++++++++++++++++------ scripts/protoc-gen-openapi.sh | 9 +- 3 files changed, 4901 insertions(+), 1258 deletions(-) diff --git a/changelog.md b/changelog.md index 6f09eadd3d..2beb385214 100644 --- a/changelog.md +++ b/changelog.md @@ -24,6 +24,10 @@ * [1584](https://github.com/zeta-chain/node/pull/1584) - allow to run E2E tests on any networks +### CI + +* [1736](https://github.com/zeta-chain/node/pull/1736) - chore: add Ethermint endpoints to OpenAPI + ### Chores * [1729](https://github.com/zeta-chain/node/pull/1729) - add issue templates diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index 1ba9d75fcb..f9a8b1cd43 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -28256,1455 +28256,4327 @@ paths: $ref: '#/definitions/googlerpcStatus' tags: - Query -definitions: - cosmos.auth.v1beta1.AddressBytesToStringResponse: - type: object - properties: - address_string: - type: string - description: >- - AddressBytesToStringResponse is the response type for AddressString rpc method. + /ethermint/evm/v1/account/{address}: + get: + summary: Account queries an Ethereum account. + operationId: Account + responses: + '200': + description: A successful response. + schema: + type: object + properties: + balance: + type: string + description: balance is the balance of the EVM denomination. + code_hash: + type: string + description: code hash is the hex-formatted code bytes from the EOA. + nonce: + type: string + format: uint64 + description: nonce is the account's sequence number. + description: >- + QueryAccountResponse is the response type for the Query/Account RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized - Since: cosmos-sdk 0.46 - cosmos.auth.v1beta1.AddressStringToBytesResponse: - type: object - properties: - address_bytes: - type: string - format: byte - description: >- - AddressStringToBytesResponse is the response type for AddressBytes rpc method. + protocol buffer message. This string must contain at least - Since: cosmos-sdk 0.46 - cosmos.auth.v1beta1.Bech32PrefixResponse: - type: object - properties: - bech32_prefix: - type: string - description: |- - Bech32PrefixResponse is the response type for Bech32Prefix rpc method. + one "/" character. The last segment of the URL's path must represent - Since: cosmos-sdk 0.46 - cosmos.auth.v1beta1.Params: - type: object - properties: - max_memo_characters: - type: string - format: uint64 - tx_sig_limit: - type: string - format: uint64 - tx_size_cost_per_byte: - type: string - format: uint64 - sig_verify_cost_ed25519: - type: string - format: uint64 - sig_verify_cost_secp256k1: - type: string - format: uint64 - description: Params defines the parameters for the auth module. - cosmos.auth.v1beta1.QueryAccountAddressByIDResponse: - type: object - properties: - account_address: - type: string - description: 'Since: cosmos-sdk 0.46.2' - title: >- - QueryAccountAddressByIDResponse is the response type for AccountAddressByID rpc method - cosmos.auth.v1beta1.QueryAccountResponse: - type: object - properties: - account: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized + the fully qualified name of the type (as in - protocol buffer message. This string must contain at least + `path/google.protobuf.Duration`). The name should be in a canonical form - one "/" character. The last segment of the URL's path must represent + (e.g., leading "." is not accepted). - the fully qualified name of the type (as in + In practice, teams usually precompile into the binary all types that they - `path/google.protobuf.Duration`). The name should be in a canonical form + expect it to use in the context of Any. However, for URLs which use the - (e.g., leading "." is not accepted). + scheme `http`, `https`, or no scheme, one can optionally set up a type - In practice, teams usually precompile into the binary all types that they + server that maps type URLs to message definitions as follows: - expect it to use in the context of Any. However, for URLs which use the + * If no scheme is provided, `https` is assumed. - scheme `http`, `https`, or no scheme, one can optionally set up a type + * An HTTP GET on the URL must yield a [google.protobuf.Type][] - server that maps type URLs to message definitions as follows: + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the - * If no scheme is provided, `https` is assumed. + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - * An HTTP GET on the URL must yield a [google.protobuf.Type][] + Note: this functionality is not currently available in the official - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the + protobuf release, and it is not used for type URLs beginning with - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + type.googleapis.com. - Note: this functionality is not currently available in the official + Schemes other than `http`, `https` (or the empty scheme) might be - protobuf release, and it is not used for type URLs beginning with + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a - type.googleapis.com. + URL that describes the type of the serialized message. - Schemes other than `http`, `https` (or the empty scheme) might be + Protobuf library provides support to pack/unpack Any values in the form - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a + of utility functions or additional generated methods of the Any type. - URL that describes the type of the serialized message. + Example 1: Pack and unpack a message in C++. - Protobuf library provides support to pack/unpack Any values in the form + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - of utility functions or additional generated methods of the Any type. + Example 2: Pack and unpack a message in Java. - Example 1: Pack and unpack a message in C++. + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + Example 3: Pack and unpack a message in Python. - Example 2: Pack and unpack a message in Java. + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } + Example 4: Pack and unpack a message in Go - Example 3: Pack and unpack a message in Python. + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + The pack methods provided by protobuf library will by default use - Example 4: Pack and unpack a message in Go + 'type.googleapis.com/full.type.name' as the type URL and the unpack - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } + methods only use the fully qualified type name after the last '/' - The pack methods provided by protobuf library will by default use + in the type URL, for example "foo.bar.com/x/y.z" will yield type - 'type.googleapis.com/full.type.name' as the type URL and the unpack + name "y.z". - methods only use the fully qualified type name after the last '/' + JSON - in the type URL, for example "foo.bar.com/x/y.z" will yield type + ==== - name "y.z". + The JSON representation of an `Any` value uses the regular - JSON + representation of the deserialized, embedded message, with an - ==== + additional field `@type` which contains the type URL. Example: - The JSON representation of an `Any` value uses the regular + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - representation of the deserialized, embedded message, with an + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - additional field `@type` which contains the type URL. Example: + If the embedded message type is well-known and has a custom JSON - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a custom JSON + representation, that representation will be embedded adding a field - representation, that representation will be embedded adding a field + `value` which holds the custom JSON in addition to the `@type` - `value` which holds the custom JSON in addition to the `@type` + field. Example (for message [google.protobuf.Duration][]): - field. Example (for message [google.protobuf.Duration][]): + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: address + description: address is the ethereum hex address to query the account for. + in: path + required: true + type: string + tags: + - Query + /ethermint/evm/v1/balances/{address}: + get: + summary: |- + Balance queries the balance of a the EVM denomination for a single + EthAccount. + operationId: Balance + responses: + '200': + description: A successful response. + schema: + type: object + properties: + balance: + type: string + description: balance is the balance of the EVM denomination. + description: >- + QueryBalanceResponse is the response type for the Query/Balance RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - description: >- - QueryAccountResponse is the response type for the Query/Account RPC method. - cosmos.auth.v1beta1.QueryAccountsResponse: - type: object - properties: - accounts: - type: array - items: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized + protocol buffer message. This string must contain at least - protocol buffer message. This string must contain at least + one "/" character. The last segment of the URL's path must represent - one "/" character. The last segment of the URL's path must represent + the fully qualified name of the type (as in - the fully qualified name of the type (as in + `path/google.protobuf.Duration`). The name should be in a canonical form - `path/google.protobuf.Duration`). The name should be in a canonical form + (e.g., leading "." is not accepted). - (e.g., leading "." is not accepted). + In practice, teams usually precompile into the binary all types that they - In practice, teams usually precompile into the binary all types that they + expect it to use in the context of Any. However, for URLs which use the - expect it to use in the context of Any. However, for URLs which use the + scheme `http`, `https`, or no scheme, one can optionally set up a type - scheme `http`, `https`, or no scheme, one can optionally set up a type + server that maps type URLs to message definitions as follows: - server that maps type URLs to message definitions as follows: + * If no scheme is provided, `https` is assumed. - * If no scheme is provided, `https` is assumed. + * An HTTP GET on the URL must yield a [google.protobuf.Type][] - * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + Note: this functionality is not currently available in the official - Note: this functionality is not currently available in the official + protobuf release, and it is not used for type URLs beginning with - protobuf release, and it is not used for type URLs beginning with + type.googleapis.com. - type.googleapis.com. + Schemes other than `http`, `https` (or the empty scheme) might be - Schemes other than `http`, `https` (or the empty scheme) might be + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a + URL that describes the type of the serialized message. - URL that describes the type of the serialized message. + Protobuf library provides support to pack/unpack Any values in the form - Protobuf library provides support to pack/unpack Any values in the form + of utility functions or additional generated methods of the Any type. - of utility functions or additional generated methods of the Any type. + Example 1: Pack and unpack a message in C++. - Example 1: Pack and unpack a message in C++. + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + Example 2: Pack and unpack a message in Java. - Example 2: Pack and unpack a message in Java. + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } + Example 3: Pack and unpack a message in Python. - Example 3: Pack and unpack a message in Python. + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + Example 4: Pack and unpack a message in Go - Example 4: Pack and unpack a message in Go + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } + The pack methods provided by protobuf library will by default use - The pack methods provided by protobuf library will by default use + 'type.googleapis.com/full.type.name' as the type URL and the unpack - 'type.googleapis.com/full.type.name' as the type URL and the unpack + methods only use the fully qualified type name after the last '/' - methods only use the fully qualified type name after the last '/' + in the type URL, for example "foo.bar.com/x/y.z" will yield type - in the type URL, for example "foo.bar.com/x/y.z" will yield type + name "y.z". - name "y.z". + JSON - JSON + ==== - ==== + The JSON representation of an `Any` value uses the regular - The JSON representation of an `Any` value uses the regular + representation of the deserialized, embedded message, with an - representation of the deserialized, embedded message, with an + additional field `@type` which contains the type URL. Example: - additional field `@type` which contains the type URL. Example: + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + If the embedded message type is well-known and has a custom JSON - If the embedded message type is well-known and has a custom JSON + representation, that representation will be embedded adding a field - representation, that representation will be embedded adding a field + `value` which holds the custom JSON in addition to the `@type` - `value` which holds the custom JSON in addition to the `@type` + field. Example (for message [google.protobuf.Duration][]): - field. Example (for message [google.protobuf.Duration][]): - - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - title: accounts are the existing accounts - pagination: - description: pagination defines the pagination in the response. - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: >- - total is total number of results available if PageRequest.count_total - - was set, its value is undefined otherwise - description: >- - QueryAccountsResponse is the response type for the Query/Accounts RPC method. + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: address + description: address is the ethereum hex address to query the balance for. + in: path + required: true + type: string + tags: + - Query + /ethermint/evm/v1/base_fee: + get: + summary: >- + BaseFee queries the base fee of the parent block of the current block, - Since: cosmos-sdk 0.43 - cosmos.auth.v1beta1.QueryModuleAccountByNameResponse: - type: object - properties: - account: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized + it's similar to feemarket module's method, but also checks london hardfork status. + operationId: BaseFee + responses: + '200': + description: A successful response. + schema: + type: object + properties: + base_fee: + type: string + description: BaseFeeResponse returns the EIP1559 base fee. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized - protocol buffer message. This string must contain at least + protocol buffer message. This string must contain at least - one "/" character. The last segment of the URL's path must represent + one "/" character. The last segment of the URL's path must represent - the fully qualified name of the type (as in + the fully qualified name of the type (as in - `path/google.protobuf.Duration`). The name should be in a canonical form + `path/google.protobuf.Duration`). The name should be in a canonical form - (e.g., leading "." is not accepted). + (e.g., leading "." is not accepted). - In practice, teams usually precompile into the binary all types that they + In practice, teams usually precompile into the binary all types that they - expect it to use in the context of Any. However, for URLs which use the + expect it to use in the context of Any. However, for URLs which use the - scheme `http`, `https`, or no scheme, one can optionally set up a type + scheme `http`, `https`, or no scheme, one can optionally set up a type - server that maps type URLs to message definitions as follows: + server that maps type URLs to message definitions as follows: - * If no scheme is provided, `https` is assumed. + * If no scheme is provided, `https` is assumed. - * An HTTP GET on the URL must yield a [google.protobuf.Type][] + * An HTTP GET on the URL must yield a [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - Note: this functionality is not currently available in the official + Note: this functionality is not currently available in the official - protobuf release, and it is not used for type URLs beginning with + protobuf release, and it is not used for type URLs beginning with - type.googleapis.com. + type.googleapis.com. - Schemes other than `http`, `https` (or the empty scheme) might be + Schemes other than `http`, `https` (or the empty scheme) might be - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a - URL that describes the type of the serialized message. + URL that describes the type of the serialized message. - Protobuf library provides support to pack/unpack Any values in the form + Protobuf library provides support to pack/unpack Any values in the form - of utility functions or additional generated methods of the Any type. + of utility functions or additional generated methods of the Any type. - Example 1: Pack and unpack a message in C++. + Example 1: Pack and unpack a message in C++. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - Example 2: Pack and unpack a message in Java. + Example 2: Pack and unpack a message in Java. - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } - Example 3: Pack and unpack a message in Python. + Example 3: Pack and unpack a message in Python. - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - Example 4: Pack and unpack a message in Go + Example 4: Pack and unpack a message in Go - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - The pack methods provided by protobuf library will by default use + The pack methods provided by protobuf library will by default use - 'type.googleapis.com/full.type.name' as the type URL and the unpack + 'type.googleapis.com/full.type.name' as the type URL and the unpack - methods only use the fully qualified type name after the last '/' + methods only use the fully qualified type name after the last '/' - in the type URL, for example "foo.bar.com/x/y.z" will yield type + in the type URL, for example "foo.bar.com/x/y.z" will yield type - name "y.z". + name "y.z". - JSON + JSON - ==== + ==== - The JSON representation of an `Any` value uses the regular + The JSON representation of an `Any` value uses the regular - representation of the deserialized, embedded message, with an + representation of the deserialized, embedded message, with an - additional field `@type` which contains the type URL. Example: + additional field `@type` which contains the type URL. Example: - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - If the embedded message type is well-known and has a custom JSON + If the embedded message type is well-known and has a custom JSON - representation, that representation will be embedded adding a field + representation, that representation will be embedded adding a field - `value` which holds the custom JSON in addition to the `@type` + `value` which holds the custom JSON in addition to the `@type` - field. Example (for message [google.protobuf.Duration][]): + field. Example (for message [google.protobuf.Duration][]): - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - description: >- - QueryModuleAccountByNameResponse is the response type for the Query/ModuleAccountByName RPC method. - cosmos.auth.v1beta1.QueryModuleAccountsResponse: - type: object - properties: - accounts: - type: array - items: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized - - protocol buffer message. This string must contain at least + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + tags: + - Query + /ethermint/evm/v1/codes/{address}: + get: + summary: Code queries the balance of all coins for a single account. + operationId: Code + responses: + '200': + description: A successful response. + schema: + type: object + properties: + code: + type: string + format: byte + description: code represents the code bytes from an ethereum address. + description: |- + QueryCodeResponse is the response type for the Query/Code RPC + method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized - one "/" character. The last segment of the URL's path must represent + protocol buffer message. This string must contain at least - the fully qualified name of the type (as in + one "/" character. The last segment of the URL's path must represent - `path/google.protobuf.Duration`). The name should be in a canonical form + the fully qualified name of the type (as in - (e.g., leading "." is not accepted). + `path/google.protobuf.Duration`). The name should be in a canonical form - In practice, teams usually precompile into the binary all types that they + (e.g., leading "." is not accepted). - expect it to use in the context of Any. However, for URLs which use the + In practice, teams usually precompile into the binary all types that they - scheme `http`, `https`, or no scheme, one can optionally set up a type + expect it to use in the context of Any. However, for URLs which use the - server that maps type URLs to message definitions as follows: + scheme `http`, `https`, or no scheme, one can optionally set up a type - * If no scheme is provided, `https` is assumed. + server that maps type URLs to message definitions as follows: - * An HTTP GET on the URL must yield a [google.protobuf.Type][] + * If no scheme is provided, `https` is assumed. - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the + * An HTTP GET on the URL must yield a [google.protobuf.Type][] - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the - Note: this functionality is not currently available in the official + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - protobuf release, and it is not used for type URLs beginning with + Note: this functionality is not currently available in the official - type.googleapis.com. + protobuf release, and it is not used for type URLs beginning with - Schemes other than `http`, `https` (or the empty scheme) might be + type.googleapis.com. - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a + Schemes other than `http`, `https` (or the empty scheme) might be - URL that describes the type of the serialized message. + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a - Protobuf library provides support to pack/unpack Any values in the form + URL that describes the type of the serialized message. - of utility functions or additional generated methods of the Any type. + Protobuf library provides support to pack/unpack Any values in the form - Example 1: Pack and unpack a message in C++. + of utility functions or additional generated methods of the Any type. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + Example 1: Pack and unpack a message in C++. - Example 2: Pack and unpack a message in Java. + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } + Example 2: Pack and unpack a message in Java. - Example 3: Pack and unpack a message in Python. + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + Example 3: Pack and unpack a message in Python. - Example 4: Pack and unpack a message in Go + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } + Example 4: Pack and unpack a message in Go - The pack methods provided by protobuf library will by default use + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - 'type.googleapis.com/full.type.name' as the type URL and the unpack + The pack methods provided by protobuf library will by default use - methods only use the fully qualified type name after the last '/' + 'type.googleapis.com/full.type.name' as the type URL and the unpack - in the type URL, for example "foo.bar.com/x/y.z" will yield type + methods only use the fully qualified type name after the last '/' - name "y.z". + in the type URL, for example "foo.bar.com/x/y.z" will yield type - JSON + name "y.z". - ==== + JSON - The JSON representation of an `Any` value uses the regular + ==== - representation of the deserialized, embedded message, with an + The JSON representation of an `Any` value uses the regular - additional field `@type` which contains the type URL. Example: + representation of the deserialized, embedded message, with an - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + additional field `@type` which contains the type URL. Example: - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - If the embedded message type is well-known and has a custom JSON + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - representation, that representation will be embedded adding a field + If the embedded message type is well-known and has a custom JSON - `value` which holds the custom JSON in addition to the `@type` + representation, that representation will be embedded adding a field - field. Example (for message [google.protobuf.Duration][]): + `value` which holds the custom JSON in addition to the `@type` - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - description: >- - QueryModuleAccountsResponse is the response type for the Query/ModuleAccounts RPC method. + field. Example (for message [google.protobuf.Duration][]): - Since: cosmos-sdk 0.46 - cosmos.auth.v1beta1.QueryParamsResponse: - type: object - properties: - params: - description: params defines the parameters of the module. - type: object - properties: - max_memo_characters: - type: string - format: uint64 - tx_sig_limit: - type: string - format: uint64 - tx_size_cost_per_byte: - type: string - format: uint64 - sig_verify_cost_ed25519: - type: string - format: uint64 - sig_verify_cost_secp256k1: - type: string - format: uint64 - description: QueryParamsResponse is the response type for the Query/Params RPC method. - cosmos.base.query.v1beta1.PageRequest: - type: object - properties: - key: - type: string - format: byte - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - offset: - type: string - format: uint64 - description: |- - offset is a numeric offset that can be used when key is unavailable. - It is less efficient than using key. Only one of offset or key should - be set. - limit: - type: string - format: uint64 - description: >- - limit is the total number of results to be returned in the result page. + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: address + description: address is the ethereum hex address to query the code for. + in: path + required: true + type: string + tags: + - Query + /ethermint/evm/v1/cosmos_account/{address}: + get: + summary: CosmosAccount queries an Ethereum account's Cosmos Address. + operationId: CosmosAccount + responses: + '200': + description: A successful response. + schema: + type: object + properties: + cosmos_address: + type: string + description: cosmos_address is the cosmos address of the account. + sequence: + type: string + format: uint64 + description: sequence is the account's sequence number. + account_number: + type: string + format: uint64 + title: account_number is the account numbert + description: >- + QueryCosmosAccountResponse is the response type for the Query/CosmosAccount - If left empty it will default to a value to be set by each app. - count_total: - type: boolean - description: >- - count_total is set to true to indicate that the result set should include + RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized - a count of the total number of items available for pagination in UIs. + protocol buffer message. This string must contain at least - count_total is only respected when offset is used. It is ignored when key + one "/" character. The last segment of the URL's path must represent - is set. - reverse: - type: boolean - description: >- - reverse is set to true if results are to be returned in the descending order. + the fully qualified name of the type (as in - Since: cosmos-sdk 0.43 - description: |- - message SomeRequest { - Foo some_parameter = 1; - PageRequest pagination = 2; - } - title: |- - PageRequest is to be embedded in gRPC request messages for efficient - pagination. Ex: - cosmos.base.query.v1beta1.PageResponse: - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: |- - total is total number of results available if PageRequest.count_total - was set, its value is undefined otherwise - description: |- - PageResponse is to be embedded in gRPC response messages where the - corresponding request message has used PageRequest. + `path/google.protobuf.Duration`). The name should be in a canonical form - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - google.protobuf.Any: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized + (e.g., leading "." is not accepted). - protocol buffer message. This string must contain at least + In practice, teams usually precompile into the binary all types that they - one "/" character. The last segment of the URL's path must represent + expect it to use in the context of Any. However, for URLs which use the - the fully qualified name of the type (as in + scheme `http`, `https`, or no scheme, one can optionally set up a type - `path/google.protobuf.Duration`). The name should be in a canonical form + server that maps type URLs to message definitions as follows: - (e.g., leading "." is not accepted). + * If no scheme is provided, `https` is assumed. - In practice, teams usually precompile into the binary all types that they + * An HTTP GET on the URL must yield a [google.protobuf.Type][] - expect it to use in the context of Any. However, for URLs which use the + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the - scheme `http`, `https`, or no scheme, one can optionally set up a type + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - server that maps type URLs to message definitions as follows: + Note: this functionality is not currently available in the official - * If no scheme is provided, `https` is assumed. + protobuf release, and it is not used for type URLs beginning with - * An HTTP GET on the URL must yield a [google.protobuf.Type][] + type.googleapis.com. - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the + Schemes other than `http`, `https` (or the empty scheme) might be - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a - Note: this functionality is not currently available in the official + URL that describes the type of the serialized message. - protobuf release, and it is not used for type URLs beginning with + Protobuf library provides support to pack/unpack Any values in the form - type.googleapis.com. + of utility functions or additional generated methods of the Any type. - Schemes other than `http`, `https` (or the empty scheme) might be + Example 1: Pack and unpack a message in C++. - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - URL that describes the type of the serialized message. + Example 2: Pack and unpack a message in Java. - Protobuf library provides support to pack/unpack Any values in the form + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } - of utility functions or additional generated methods of the Any type. + Example 3: Pack and unpack a message in Python. - Example 1: Pack and unpack a message in C++. + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + Example 4: Pack and unpack a message in Go - Example 2: Pack and unpack a message in Java. + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } + The pack methods provided by protobuf library will by default use - Example 3: Pack and unpack a message in Python. + 'type.googleapis.com/full.type.name' as the type URL and the unpack - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + methods only use the fully qualified type name after the last '/' - Example 4: Pack and unpack a message in Go + in the type URL, for example "foo.bar.com/x/y.z" will yield type - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } + name "y.z". - The pack methods provided by protobuf library will by default use + JSON - 'type.googleapis.com/full.type.name' as the type URL and the unpack + ==== - methods only use the fully qualified type name after the last '/' + The JSON representation of an `Any` value uses the regular - in the type URL, for example "foo.bar.com/x/y.z" will yield type + representation of the deserialized, embedded message, with an - name "y.z". + additional field `@type` which contains the type URL. Example: - JSON + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - ==== + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - The JSON representation of an `Any` value uses the regular + If the embedded message type is well-known and has a custom JSON - representation of the deserialized, embedded message, with an + representation, that representation will be embedded adding a field - additional field `@type` which contains the type URL. Example: + `value` which holds the custom JSON in addition to the `@type` - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + field. Example (for message [google.protobuf.Duration][]): - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: address + description: address is the ethereum hex address to query the account for. + in: path + required: true + type: string + tags: + - Query + /ethermint/evm/v1/estimate_gas: + get: + summary: EstimateGas implements the `eth_estimateGas` rpc api + operationId: EstimateGas + responses: + '200': + description: A successful response. + schema: + type: object + properties: + gas: + type: string + format: uint64 + title: the estimated gas + title: EstimateGasResponse defines EstimateGas response + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized - If the embedded message type is well-known and has a custom JSON + protocol buffer message. This string must contain at least - representation, that representation will be embedded adding a field + one "/" character. The last segment of the URL's path must represent - `value` which holds the custom JSON in addition to the `@type` + the fully qualified name of the type (as in - field. Example (for message [google.protobuf.Duration][]): + `path/google.protobuf.Duration`). The name should be in a canonical form - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - grpc.gateway.runtime.Error: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized + (e.g., leading "." is not accepted). - protocol buffer message. This string must contain at least + In practice, teams usually precompile into the binary all types that they - one "/" character. The last segment of the URL's path must represent + expect it to use in the context of Any. However, for URLs which use the - the fully qualified name of the type (as in + scheme `http`, `https`, or no scheme, one can optionally set up a type - `path/google.protobuf.Duration`). The name should be in a canonical form + server that maps type URLs to message definitions as follows: - (e.g., leading "." is not accepted). + * If no scheme is provided, `https` is assumed. - In practice, teams usually precompile into the binary all types that they + * An HTTP GET on the URL must yield a [google.protobuf.Type][] - expect it to use in the context of Any. However, for URLs which use the + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the - scheme `http`, `https`, or no scheme, one can optionally set up a type + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - server that maps type URLs to message definitions as follows: + Note: this functionality is not currently available in the official - * If no scheme is provided, `https` is assumed. + protobuf release, and it is not used for type URLs beginning with - * An HTTP GET on the URL must yield a [google.protobuf.Type][] + type.googleapis.com. - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the + Schemes other than `http`, `https` (or the empty scheme) might be - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a - Note: this functionality is not currently available in the official + URL that describes the type of the serialized message. - protobuf release, and it is not used for type URLs beginning with + Protobuf library provides support to pack/unpack Any values in the form - type.googleapis.com. + of utility functions or additional generated methods of the Any type. - Schemes other than `http`, `https` (or the empty scheme) might be + Example 1: Pack and unpack a message in C++. - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - URL that describes the type of the serialized message. + Example 2: Pack and unpack a message in Java. - Protobuf library provides support to pack/unpack Any values in the form + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } - of utility functions or additional generated methods of the Any type. + Example 3: Pack and unpack a message in Python. - Example 1: Pack and unpack a message in C++. + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + Example 4: Pack and unpack a message in Go - Example 2: Pack and unpack a message in Java. + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } + The pack methods provided by protobuf library will by default use - Example 3: Pack and unpack a message in Python. + 'type.googleapis.com/full.type.name' as the type URL and the unpack - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + methods only use the fully qualified type name after the last '/' - Example 4: Pack and unpack a message in Go + in the type URL, for example "foo.bar.com/x/y.z" will yield type - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } + name "y.z". - The pack methods provided by protobuf library will by default use + JSON - 'type.googleapis.com/full.type.name' as the type URL and the unpack + ==== - methods only use the fully qualified type name after the last '/' + The JSON representation of an `Any` value uses the regular - in the type URL, for example "foo.bar.com/x/y.z" will yield type + representation of the deserialized, embedded message, with an - name "y.z". + additional field `@type` which contains the type URL. Example: - JSON + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - ==== + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - The JSON representation of an `Any` value uses the regular + If the embedded message type is well-known and has a custom JSON - representation of the deserialized, embedded message, with an + representation, that representation will be embedded adding a field - additional field `@type` which contains the type URL. Example: + `value` which holds the custom JSON in addition to the `@type` - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + field. Example (for message [google.protobuf.Duration][]): - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: args + description: same json format as the json rpc api. + in: query + required: false + type: string + format: byte + - name: gas_cap + description: the default gas cap to be used. + in: query + required: false + type: string + format: uint64 + - name: proposer_address + description: the proposer of the requested block. + in: query + required: false + type: string + format: byte + - name: chain_id + description: the eip155 chain id parsed from the requested block header. + in: query + required: false + type: string + format: int64 + tags: + - Query + /ethermint/evm/v1/eth_call: + get: + summary: EthCall implements the `eth_call` rpc api + operationId: EthCall + responses: + '200': + description: A successful response. + schema: + type: object + properties: + hash: + type: string + title: >- + ethereum transaction hash in hex format. This hash differs from the - If the embedded message type is well-known and has a custom JSON + Tendermint sha256 hash of the transaction bytes. See - representation, that representation will be embedded adding a field + https://github.com/tendermint/tendermint/issues/6539 for reference + logs: + type: array + items: + type: object + properties: + address: + type: string + title: address of the contract that generated the event + topics: + type: array + items: + type: string + description: list of topics provided by the contract. + data: + type: string + format: byte + title: supplied by the contract, usually ABI-encoded + block_number: + type: string + format: uint64 + title: block in which the transaction was included + tx_hash: + type: string + title: hash of the transaction + tx_index: + type: string + format: uint64 + title: index of the transaction in the block + block_hash: + type: string + title: hash of the block in which the transaction was included + index: + type: string + format: uint64 + title: index of the log in the block + removed: + type: boolean + description: >- + The Removed field is true if this log was reverted due to a chain - `value` which holds the custom JSON in addition to the `@type` + reorganisation. You must pay attention to this field if you receive logs - field. Example (for message [google.protobuf.Duration][]): + through a filter query. + description: >- + Log represents an protobuf compatible Ethereum Log that defines a contract - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - cosmos.bank.v1beta1.DenomOwner: - type: object - properties: - address: - type: string - description: address defines the address that owns a particular denomination. - balance: - type: object - properties: - denom: - type: string - amount: - type: string - description: |- - Coin defines a token with a denomination and an amount. + log event. These events are generated by the LOG opcode and stored/indexed by - NOTE: The amount field is an Int which implements the custom method - signatures required by gogoproto. - description: |- - DenomOwner defines structure representing an account that owns or holds a - particular denominated token. It contains the account address and account - balance of the denominated token. + the node. + description: >- + logs contains the transaction hash and the proto-compatible ethereum - Since: cosmos-sdk 0.46 - cosmos.bank.v1beta1.DenomUnit: - type: object - properties: - denom: - type: string - description: denom represents the string name of the given denom unit (e.g uatom). - exponent: - type: integer - format: int64 - description: >- - exponent represents power of 10 exponent that one must + logs. + ret: + type: string + format: byte + title: >- + returned data from evm function (result or data supplied with revert - raise the base_denom to in order to equal the given DenomUnit's denom + opcode) + vm_error: + type: string + title: vm error is the error returned by vm execution + gas_used: + type: string + format: uint64 + title: gas consumed by the transaction + description: MsgEthereumTxResponse defines the Msg/EthereumTx response type. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized - 1 denom = 10^exponent base_denom + protocol buffer message. This string must contain at least - (e.g. with a base_denom of uatom, one can create a DenomUnit of 'atom' with + one "/" character. The last segment of the URL's path must represent - exponent = 6, thus: 1 atom = 10^6 uatom). - aliases: - type: array - items: - type: string - title: aliases is a list of string aliases for the given denom - description: |- - DenomUnit represents a struct that describes a given - denomination unit of the basic token. - cosmos.bank.v1beta1.Metadata: - type: object - properties: - description: - type: string - denom_units: - type: array - items: - type: object - properties: - denom: - type: string - description: >- - denom represents the string name of the given denom unit (e.g uatom). - exponent: - type: integer - format: int64 - description: >- - exponent represents power of 10 exponent that one must + the fully qualified name of the type (as in - raise the base_denom to in order to equal the given DenomUnit's denom + `path/google.protobuf.Duration`). The name should be in a canonical form - 1 denom = 10^exponent base_denom + (e.g., leading "." is not accepted). - (e.g. with a base_denom of uatom, one can create a DenomUnit of 'atom' with + In practice, teams usually precompile into the binary all types that they - exponent = 6, thus: 1 atom = 10^6 uatom). - aliases: - type: array - items: - type: string - title: aliases is a list of string aliases for the given denom - description: |- - DenomUnit represents a struct that describes a given - denomination unit of the basic token. - title: denom_units represents the list of DenomUnit's for a given coin - base: - type: string - description: >- - base represents the base denom (should be the DenomUnit with exponent = 0). - display: - type: string - description: |- - display indicates the suggested denom that should be - displayed in clients. - name: - type: string - description: 'Since: cosmos-sdk 0.43' - title: 'name defines the name of the token (eg: Cosmos Atom)' - symbol: - type: string - description: >- - symbol is the token symbol usually shown on exchanges (eg: ATOM). This can + expect it to use in the context of Any. However, for URLs which use the - be the same as the display. + scheme `http`, `https`, or no scheme, one can optionally set up a type - Since: cosmos-sdk 0.43 - uri: - type: string - description: >- - URI to a document (on or off-chain) that contains additional information. Optional. + server that maps type URLs to message definitions as follows: - Since: cosmos-sdk 0.46 - uri_hash: - type: string - description: >- - URIHash is a sha256 hash of a document pointed by URI. It's used to verify that + * If no scheme is provided, `https` is assumed. - the document didn't change. Optional. + * An HTTP GET on the URL must yield a [google.protobuf.Type][] - Since: cosmos-sdk 0.46 - description: |- - Metadata represents a struct that describes - a basic token. - cosmos.bank.v1beta1.Params: - type: object - properties: - send_enabled: - type: array - items: - type: object - properties: - denom: - type: string - enabled: - type: boolean - description: >- - SendEnabled maps coin denom to a send_enabled status (whether a denom is + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the - sendable). - default_send_enabled: - type: boolean - description: Params defines the parameters for the bank module. - cosmos.bank.v1beta1.QueryAllBalancesResponse: - type: object - properties: - balances: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string - description: |- - Coin defines a token with a denomination and an amount. + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - NOTE: The amount field is an Int which implements the custom method - signatures required by gogoproto. - description: balances is the balances of all the coins. - pagination: - description: pagination defines the pagination in the response. - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: >- - total is total number of results available if PageRequest.count_total + Note: this functionality is not currently available in the official - was set, its value is undefined otherwise - description: >- - QueryAllBalancesResponse is the response type for the Query/AllBalances RPC + protobuf release, and it is not used for type URLs beginning with - method. - cosmos.bank.v1beta1.QueryBalanceResponse: - type: object - properties: - balance: - type: object - properties: - denom: - type: string - amount: - type: string - description: |- - Coin defines a token with a denomination and an amount. + type.googleapis.com. - NOTE: The amount field is an Int which implements the custom method - signatures required by gogoproto. - description: >- - QueryBalanceResponse is the response type for the Query/Balance RPC method. - cosmos.bank.v1beta1.QueryDenomMetadataResponse: - type: object - properties: - metadata: - type: object - properties: - description: - type: string - denom_units: - type: array - items: - type: object - properties: - denom: - type: string - description: >- - denom represents the string name of the given denom unit (e.g uatom). - exponent: - type: integer - format: int64 + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. description: >- - exponent represents power of 10 exponent that one must + `Any` contains an arbitrary serialized protocol buffer message along with a - raise the base_denom to in order to equal the given DenomUnit's denom + URL that describes the type of the serialized message. - 1 denom = 10^exponent base_denom + Protobuf library provides support to pack/unpack Any values in the form - (e.g. with a base_denom of uatom, one can create a DenomUnit of 'atom' with + of utility functions or additional generated methods of the Any type. - exponent = 6, thus: 1 atom = 10^6 uatom). - aliases: - type: array - items: - type: string - title: aliases is a list of string aliases for the given denom - description: |- - DenomUnit represents a struct that describes a given - denomination unit of the basic token. - title: denom_units represents the list of DenomUnit's for a given coin - base: - type: string - description: >- - base represents the base denom (should be the DenomUnit with exponent = 0). - display: - type: string - description: |- - display indicates the suggested denom that should be - displayed in clients. - name: - type: string - description: 'Since: cosmos-sdk 0.43' - title: 'name defines the name of the token (eg: Cosmos Atom)' - symbol: - type: string - description: >- - symbol is the token symbol usually shown on exchanges (eg: ATOM). This can + Example 1: Pack and unpack a message in C++. - be the same as the display. + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - Since: cosmos-sdk 0.43 - uri: - type: string - description: >- - URI to a document (on or off-chain) that contains additional information. Optional. + Example 2: Pack and unpack a message in Java. - Since: cosmos-sdk 0.46 - uri_hash: - type: string - description: >- - URIHash is a sha256 hash of a document pointed by URI. It's used to verify that + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } - the document didn't change. Optional. + Example 3: Pack and unpack a message in Python. - Since: cosmos-sdk 0.46 - description: |- - Metadata represents a struct that describes - a basic token. - description: >- - QueryDenomMetadataResponse is the response type for the Query/DenomMetadata RPC + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - method. - cosmos.bank.v1beta1.QueryDenomOwnersResponse: - type: object - properties: - denom_owners: - type: array - items: - type: object - properties: - address: - type: string - description: address defines the address that owns a particular denomination. - balance: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. + Example 4: Pack and unpack a message in Go - NOTE: The amount field is an Int which implements the custom method + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - signatures required by gogoproto. - description: >- - DenomOwner defines structure representing an account that owns or holds a + The pack methods provided by protobuf library will by default use - particular denominated token. It contains the account address and account + 'type.googleapis.com/full.type.name' as the type URL and the unpack - balance of the denominated token. + methods only use the fully qualified type name after the last '/' - Since: cosmos-sdk 0.46 - pagination: - description: pagination defines the pagination in the response. - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: >- - total is total number of results available if PageRequest.count_total + in the type URL, for example "foo.bar.com/x/y.z" will yield type - was set, its value is undefined otherwise - description: >- - QueryDenomOwnersResponse defines the RPC response of a DenomOwners RPC query. + name "y.z". - Since: cosmos-sdk 0.46 - cosmos.bank.v1beta1.QueryDenomsMetadataResponse: - type: object - properties: - metadatas: - type: array - items: - type: object - properties: - description: - type: string - denom_units: - type: array - items: - type: object - properties: - denom: - type: string - description: >- - denom represents the string name of the given denom unit (e.g uatom). - exponent: - type: integer - format: int64 - description: >- - exponent represents power of 10 exponent that one must + JSON - raise the base_denom to in order to equal the given DenomUnit's denom + ==== - 1 denom = 10^exponent base_denom + The JSON representation of an `Any` value uses the regular - (e.g. with a base_denom of uatom, one can create a DenomUnit of 'atom' with + representation of the deserialized, embedded message, with an - exponent = 6, thus: 1 atom = 10^6 uatom). - aliases: - type: array - items: - type: string - title: aliases is a list of string aliases for the given denom - description: |- - DenomUnit represents a struct that describes a given + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: args + description: same json format as the json rpc api. + in: query + required: false + type: string + format: byte + - name: gas_cap + description: the default gas cap to be used. + in: query + required: false + type: string + format: uint64 + - name: proposer_address + description: the proposer of the requested block. + in: query + required: false + type: string + format: byte + - name: chain_id + description: the eip155 chain id parsed from the requested block header. + in: query + required: false + type: string + format: int64 + tags: + - Query + /ethermint/evm/v1/params: + get: + summary: Params queries the parameters of x/evm module. + operationId: EvmParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + description: params define the evm module parameters. + type: object + properties: + evm_denom: + type: string + description: >- + evm denom represents the token denomination used to run the EVM state + + transitions. + enable_create: + type: boolean + title: >- + enable create toggles state transitions that use the vm.Create function + enable_call: + type: boolean + title: >- + enable call toggles state transitions that use the vm.Call function + extra_eips: + type: array + items: + type: string + format: int64 + title: extra eips defines the additional EIPs for the vm.Config + chain_config: + title: >- + chain config defines the EVM chain configuration parameters + type: object + properties: + homestead_block: + type: string + title: >- + Homestead switch block (nil no fork, 0 = already homestead) + dao_fork_block: + type: string + title: TheDAO hard-fork switch block (nil no fork) + dao_fork_support: + type: boolean + title: >- + Whether the nodes supports or opposes the DAO hard-fork + eip150_block: + type: string + title: >- + EIP150 implements the Gas price changes + + (https://github.com/ethereum/EIPs/issues/150) EIP150 HF block (nil no fork) + eip150_hash: + type: string + title: >- + EIP150 HF hash (needed for header only clients as only gas pricing changed) + eip155_block: + type: string + title: EIP155Block HF block + eip158_block: + type: string + title: EIP158 HF block + byzantium_block: + type: string + title: >- + Byzantium switch block (nil no fork, 0 = already on byzantium) + constantinople_block: + type: string + title: >- + Constantinople switch block (nil no fork, 0 = already activated) + petersburg_block: + type: string + title: Petersburg switch block (nil same as Constantinople) + istanbul_block: + type: string + title: >- + Istanbul switch block (nil no fork, 0 = already on istanbul) + muir_glacier_block: + type: string + title: >- + Eip-2384 (bomb delay) switch block (nil no fork, 0 = already activated) + berlin_block: + type: string + title: >- + Berlin switch block (nil = no fork, 0 = already on berlin) + london_block: + type: string + title: >- + London switch block (nil = no fork, 0 = already on london) + arrow_glacier_block: + type: string + title: >- + Eip-4345 (bomb delay) switch block (nil = no fork, 0 = already activated) + gray_glacier_block: + type: string + title: >- + EIP-5133 (bomb delay) switch block (nil = no fork, 0 = already activated) + merge_netsplit_block: + type: string + title: >- + Virtual fork after The Merge to use as a network splitter + description: >- + ChainConfig defines the Ethereum ChainConfig parameters using *sdk.Int values + + instead of *big.Int. + allow_unprotected_txs: + type: boolean + description: >- + Allow unprotected transactions defines if replay-protected (i.e non EIP155 + + signed) transactions can be executed on the state machine. + title: Params defines the EVM module parameters + description: >- + QueryParamsResponse defines the response type for querying x/evm parameters. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + tags: + - Query + /ethermint/evm/v1/storage/{address}/{key}: + get: + summary: Storage queries the balance of all coins for a single account. + operationId: Storage + responses: + '200': + description: A successful response. + schema: + type: object + properties: + value: + type: string + description: >- + key defines the storage state value hash associated with the given key. + description: >- + QueryStorageResponse is the response type for the Query/Storage RPC + + method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: address + description: >- + / address is the ethereum hex address to query the storage state for. + in: path + required: true + type: string + - name: key + description: key defines the key of the storage state + in: path + required: true + type: string + tags: + - Query + /ethermint/evm/v1/trace_block: + get: + summary: >- + TraceBlock implements the `debug_traceBlockByNumber` and `debug_traceBlockByHash` rpc api + operationId: TraceBlock + responses: + '200': + description: A successful response. + schema: + type: object + properties: + data: + type: string + format: byte + title: QueryTraceBlockResponse defines TraceBlock response + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: trace_config.tracer + description: custom javascript tracer. + in: query + required: false + type: string + - name: trace_config.timeout + description: >- + overrides the default timeout of 5 seconds for JavaScript-based tracing + + calls. + in: query + required: false + type: string + - name: trace_config.reexec + description: number of blocks the tracer is willing to go back. + in: query + required: false + type: string + format: uint64 + - name: trace_config.disable_stack + description: disable stack capture. + in: query + required: false + type: boolean + - name: trace_config.disable_storage + description: disable storage capture. + in: query + required: false + type: boolean + - name: trace_config.debug + description: print output during capture end. + in: query + required: false + type: boolean + - name: trace_config.limit + description: maximum length of output, but zero means unlimited. + in: query + required: false + type: integer + format: int32 + - name: trace_config.overrides.homestead_block + description: Homestead switch block (nil no fork, 0 = already homestead). + in: query + required: false + type: string + - name: trace_config.overrides.dao_fork_block + description: TheDAO hard-fork switch block (nil no fork). + in: query + required: false + type: string + - name: trace_config.overrides.dao_fork_support + description: Whether the nodes supports or opposes the DAO hard-fork. + in: query + required: false + type: boolean + - name: trace_config.overrides.eip150_block + description: >- + EIP150 implements the Gas price changes + + (https://github.com/ethereum/EIPs/issues/150) EIP150 HF block (nil no fork). + in: query + required: false + type: string + - name: trace_config.overrides.eip150_hash + description: >- + EIP150 HF hash (needed for header only clients as only gas pricing changed). + in: query + required: false + type: string + - name: trace_config.overrides.eip155_block + description: EIP155Block HF block. + in: query + required: false + type: string + - name: trace_config.overrides.eip158_block + description: EIP158 HF block. + in: query + required: false + type: string + - name: trace_config.overrides.byzantium_block + description: Byzantium switch block (nil no fork, 0 = already on byzantium). + in: query + required: false + type: string + - name: trace_config.overrides.constantinople_block + description: Constantinople switch block (nil no fork, 0 = already activated). + in: query + required: false + type: string + - name: trace_config.overrides.petersburg_block + description: Petersburg switch block (nil same as Constantinople). + in: query + required: false + type: string + - name: trace_config.overrides.istanbul_block + description: Istanbul switch block (nil no fork, 0 = already on istanbul). + in: query + required: false + type: string + - name: trace_config.overrides.muir_glacier_block + description: >- + Eip-2384 (bomb delay) switch block (nil no fork, 0 = already activated). + in: query + required: false + type: string + - name: trace_config.overrides.berlin_block + description: Berlin switch block (nil = no fork, 0 = already on berlin). + in: query + required: false + type: string + - name: trace_config.overrides.london_block + description: London switch block (nil = no fork, 0 = already on london). + in: query + required: false + type: string + - name: trace_config.overrides.arrow_glacier_block + description: >- + Eip-4345 (bomb delay) switch block (nil = no fork, 0 = already activated). + in: query + required: false + type: string + - name: trace_config.overrides.gray_glacier_block + description: >- + EIP-5133 (bomb delay) switch block (nil = no fork, 0 = already activated). + in: query + required: false + type: string + - name: trace_config.overrides.merge_netsplit_block + description: Virtual fork after The Merge to use as a network splitter. + in: query + required: false + type: string + - name: trace_config.enable_memory + description: enable memory capture. + in: query + required: false + type: boolean + - name: trace_config.enable_return_data + description: enable return data capture. + in: query + required: false + type: boolean + - name: trace_config.tracer_json_config + description: tracer config. + in: query + required: false + type: string + - name: block_number + description: block number. + in: query + required: false + type: string + format: int64 + - name: block_hash + description: block hex hash. + in: query + required: false + type: string + - name: block_time + description: block time. + in: query + required: false + type: string + format: date-time + - name: proposer_address + description: the proposer of the requested block. + in: query + required: false + type: string + format: byte + - name: chain_id + description: the eip155 chain id parsed from the requested block header. + in: query + required: false + type: string + format: int64 + tags: + - Query + /ethermint/evm/v1/trace_tx: + get: + summary: TraceTx implements the `debug_traceTransaction` rpc api + operationId: TraceTx + responses: + '200': + description: A successful response. + schema: + type: object + properties: + data: + type: string + format: byte + title: response serialized in bytes + title: QueryTraceTxResponse defines TraceTx response + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: msg.data.type_url + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + in: query + required: false + type: string + - name: msg.data.value + description: >- + Must be a valid serialized protocol buffer of the above specified type. + in: query + required: false + type: string + format: byte + - name: msg.size + description: 'DEPRECATED: encoded storage size of the transaction.' + in: query + required: false + type: number + format: double + - name: msg.hash + description: transaction hash in hex format. + in: query + required: false + type: string + - name: msg.from + description: |- + ethereum signer address in hex format. This address value is checked + against the address derived from the signature (V, R, S) using the + secp256k1 elliptic curve. + in: query + required: false + type: string + - name: trace_config.tracer + description: custom javascript tracer. + in: query + required: false + type: string + - name: trace_config.timeout + description: >- + overrides the default timeout of 5 seconds for JavaScript-based tracing + + calls. + in: query + required: false + type: string + - name: trace_config.reexec + description: number of blocks the tracer is willing to go back. + in: query + required: false + type: string + format: uint64 + - name: trace_config.disable_stack + description: disable stack capture. + in: query + required: false + type: boolean + - name: trace_config.disable_storage + description: disable storage capture. + in: query + required: false + type: boolean + - name: trace_config.debug + description: print output during capture end. + in: query + required: false + type: boolean + - name: trace_config.limit + description: maximum length of output, but zero means unlimited. + in: query + required: false + type: integer + format: int32 + - name: trace_config.overrides.homestead_block + description: Homestead switch block (nil no fork, 0 = already homestead). + in: query + required: false + type: string + - name: trace_config.overrides.dao_fork_block + description: TheDAO hard-fork switch block (nil no fork). + in: query + required: false + type: string + - name: trace_config.overrides.dao_fork_support + description: Whether the nodes supports or opposes the DAO hard-fork. + in: query + required: false + type: boolean + - name: trace_config.overrides.eip150_block + description: >- + EIP150 implements the Gas price changes + + (https://github.com/ethereum/EIPs/issues/150) EIP150 HF block (nil no fork). + in: query + required: false + type: string + - name: trace_config.overrides.eip150_hash + description: >- + EIP150 HF hash (needed for header only clients as only gas pricing changed). + in: query + required: false + type: string + - name: trace_config.overrides.eip155_block + description: EIP155Block HF block. + in: query + required: false + type: string + - name: trace_config.overrides.eip158_block + description: EIP158 HF block. + in: query + required: false + type: string + - name: trace_config.overrides.byzantium_block + description: Byzantium switch block (nil no fork, 0 = already on byzantium). + in: query + required: false + type: string + - name: trace_config.overrides.constantinople_block + description: Constantinople switch block (nil no fork, 0 = already activated). + in: query + required: false + type: string + - name: trace_config.overrides.petersburg_block + description: Petersburg switch block (nil same as Constantinople). + in: query + required: false + type: string + - name: trace_config.overrides.istanbul_block + description: Istanbul switch block (nil no fork, 0 = already on istanbul). + in: query + required: false + type: string + - name: trace_config.overrides.muir_glacier_block + description: >- + Eip-2384 (bomb delay) switch block (nil no fork, 0 = already activated). + in: query + required: false + type: string + - name: trace_config.overrides.berlin_block + description: Berlin switch block (nil = no fork, 0 = already on berlin). + in: query + required: false + type: string + - name: trace_config.overrides.london_block + description: London switch block (nil = no fork, 0 = already on london). + in: query + required: false + type: string + - name: trace_config.overrides.arrow_glacier_block + description: >- + Eip-4345 (bomb delay) switch block (nil = no fork, 0 = already activated). + in: query + required: false + type: string + - name: trace_config.overrides.gray_glacier_block + description: >- + EIP-5133 (bomb delay) switch block (nil = no fork, 0 = already activated). + in: query + required: false + type: string + - name: trace_config.overrides.merge_netsplit_block + description: Virtual fork after The Merge to use as a network splitter. + in: query + required: false + type: string + - name: trace_config.enable_memory + description: enable memory capture. + in: query + required: false + type: boolean + - name: trace_config.enable_return_data + description: enable return data capture. + in: query + required: false + type: boolean + - name: trace_config.tracer_json_config + description: tracer config. + in: query + required: false + type: string + - name: block_number + description: block number of requested transaction. + in: query + required: false + type: string + format: int64 + - name: block_hash + description: block hex hash of requested transaction. + in: query + required: false + type: string + - name: block_time + description: block time of requested transaction. + in: query + required: false + type: string + format: date-time + - name: proposer_address + description: the proposer of the requested block. + in: query + required: false + type: string + format: byte + - name: chain_id + description: the eip155 chain id parsed from the requested block header. + in: query + required: false + type: string + format: int64 + tags: + - Query + /ethermint/evm/v1/validator_account/{cons_address}: + get: + summary: >- + ValidatorAccount queries an Ethereum account's from a validator consensus + + Address. + operationId: ValidatorAccount + responses: + '200': + description: A successful response. + schema: + type: object + properties: + account_address: + type: string + description: >- + account_address is the cosmos address of the account in bech32 format. + sequence: + type: string + format: uint64 + description: sequence is the account's sequence number. + account_number: + type: string + format: uint64 + title: account_number is the account number + description: |- + QueryValidatorAccountResponse is the response type for the + Query/ValidatorAccount RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: cons_address + description: cons_address is the validator cons address to query the account for. + in: path + required: true + type: string + tags: + - Query +definitions: + cosmos.auth.v1beta1.AddressBytesToStringResponse: + type: object + properties: + address_string: + type: string + description: >- + AddressBytesToStringResponse is the response type for AddressString rpc method. + + Since: cosmos-sdk 0.46 + cosmos.auth.v1beta1.AddressStringToBytesResponse: + type: object + properties: + address_bytes: + type: string + format: byte + description: >- + AddressStringToBytesResponse is the response type for AddressBytes rpc method. + + Since: cosmos-sdk 0.46 + cosmos.auth.v1beta1.Bech32PrefixResponse: + type: object + properties: + bech32_prefix: + type: string + description: |- + Bech32PrefixResponse is the response type for Bech32Prefix rpc method. + + Since: cosmos-sdk 0.46 + cosmos.auth.v1beta1.Params: + type: object + properties: + max_memo_characters: + type: string + format: uint64 + tx_sig_limit: + type: string + format: uint64 + tx_size_cost_per_byte: + type: string + format: uint64 + sig_verify_cost_ed25519: + type: string + format: uint64 + sig_verify_cost_secp256k1: + type: string + format: uint64 + description: Params defines the parameters for the auth module. + cosmos.auth.v1beta1.QueryAccountAddressByIDResponse: + type: object + properties: + account_address: + type: string + description: 'Since: cosmos-sdk 0.46.2' + title: >- + QueryAccountAddressByIDResponse is the response type for AccountAddressByID rpc method + cosmos.auth.v1beta1.QueryAccountResponse: + type: object + properties: + account: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: >- + QueryAccountResponse is the response type for the Query/Account RPC method. + cosmos.auth.v1beta1.QueryAccountsResponse: + type: object + properties: + accounts: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: accounts are the existing accounts + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryAccountsResponse is the response type for the Query/Accounts RPC method. + + Since: cosmos-sdk 0.43 + cosmos.auth.v1beta1.QueryModuleAccountByNameResponse: + type: object + properties: + account: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: >- + QueryModuleAccountByNameResponse is the response type for the Query/ModuleAccountByName RPC method. + cosmos.auth.v1beta1.QueryModuleAccountsResponse: + type: object + properties: + accounts: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: >- + QueryModuleAccountsResponse is the response type for the Query/ModuleAccounts RPC method. + + Since: cosmos-sdk 0.46 + cosmos.auth.v1beta1.QueryParamsResponse: + type: object + properties: + params: + description: params defines the parameters of the module. + type: object + properties: + max_memo_characters: + type: string + format: uint64 + tx_sig_limit: + type: string + format: uint64 + tx_size_cost_per_byte: + type: string + format: uint64 + sig_verify_cost_ed25519: + type: string + format: uint64 + sig_verify_cost_secp256k1: + type: string + format: uint64 + description: QueryParamsResponse is the response type for the Query/Params RPC method. + cosmos.base.query.v1beta1.PageRequest: + type: object + properties: + key: + type: string + format: byte + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + offset: + type: string + format: uint64 + description: |- + offset is a numeric offset that can be used when key is unavailable. + It is less efficient than using key. Only one of offset or key should + be set. + limit: + type: string + format: uint64 + description: >- + limit is the total number of results to be returned in the result page. + + If left empty it will default to a value to be set by each app. + count_total: + type: boolean + description: >- + count_total is set to true to indicate that the result set should include + + a count of the total number of items available for pagination in UIs. + + count_total is only respected when offset is used. It is ignored when key + + is set. + reverse: + type: boolean + description: >- + reverse is set to true if results are to be returned in the descending order. + + Since: cosmos-sdk 0.43 + description: |- + message SomeRequest { + Foo some_parameter = 1; + PageRequest pagination = 2; + } + title: |- + PageRequest is to be embedded in gRPC request messages for efficient + pagination. Ex: + cosmos.base.query.v1beta1.PageResponse: + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: |- + total is total number of results available if PageRequest.count_total + was set, its value is undefined otherwise + description: |- + PageResponse is to be embedded in gRPC response messages where the + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + google.protobuf.Any: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + grpc.gateway.runtime.Error: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + cosmos.bank.v1beta1.DenomOwner: + type: object + properties: + address: + type: string + description: address defines the address that owns a particular denomination. + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + description: |- + DenomOwner defines structure representing an account that owns or holds a + particular denominated token. It contains the account address and account + balance of the denominated token. + + Since: cosmos-sdk 0.46 + cosmos.bank.v1beta1.DenomUnit: + type: object + properties: + denom: + type: string + description: denom represents the string name of the given denom unit (e.g uatom). + exponent: + type: integer + format: int64 + description: >- + exponent represents power of 10 exponent that one must + + raise the base_denom to in order to equal the given DenomUnit's denom + + 1 denom = 10^exponent base_denom + + (e.g. with a base_denom of uatom, one can create a DenomUnit of 'atom' with + + exponent = 6, thus: 1 atom = 10^6 uatom). + aliases: + type: array + items: + type: string + title: aliases is a list of string aliases for the given denom + description: |- + DenomUnit represents a struct that describes a given + denomination unit of the basic token. + cosmos.bank.v1beta1.Metadata: + type: object + properties: + description: + type: string + denom_units: + type: array + items: + type: object + properties: + denom: + type: string + description: >- + denom represents the string name of the given denom unit (e.g uatom). + exponent: + type: integer + format: int64 + description: >- + exponent represents power of 10 exponent that one must + + raise the base_denom to in order to equal the given DenomUnit's denom + + 1 denom = 10^exponent base_denom + + (e.g. with a base_denom of uatom, one can create a DenomUnit of 'atom' with + + exponent = 6, thus: 1 atom = 10^6 uatom). + aliases: + type: array + items: + type: string + title: aliases is a list of string aliases for the given denom + description: |- + DenomUnit represents a struct that describes a given + denomination unit of the basic token. + title: denom_units represents the list of DenomUnit's for a given coin + base: + type: string + description: >- + base represents the base denom (should be the DenomUnit with exponent = 0). + display: + type: string + description: |- + display indicates the suggested denom that should be + displayed in clients. + name: + type: string + description: 'Since: cosmos-sdk 0.43' + title: 'name defines the name of the token (eg: Cosmos Atom)' + symbol: + type: string + description: >- + symbol is the token symbol usually shown on exchanges (eg: ATOM). This can + + be the same as the display. + + Since: cosmos-sdk 0.43 + uri: + type: string + description: >- + URI to a document (on or off-chain) that contains additional information. Optional. + + Since: cosmos-sdk 0.46 + uri_hash: + type: string + description: >- + URIHash is a sha256 hash of a document pointed by URI. It's used to verify that + + the document didn't change. Optional. + + Since: cosmos-sdk 0.46 + description: |- + Metadata represents a struct that describes + a basic token. + cosmos.bank.v1beta1.Params: + type: object + properties: + send_enabled: + type: array + items: + type: object + properties: + denom: + type: string + enabled: + type: boolean + description: >- + SendEnabled maps coin denom to a send_enabled status (whether a denom is + + sendable). + default_send_enabled: + type: boolean + description: Params defines the parameters for the bank module. + cosmos.bank.v1beta1.QueryAllBalancesResponse: + type: object + properties: + balances: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + description: balances is the balances of all the coins. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryAllBalancesResponse is the response type for the Query/AllBalances RPC + + method. + cosmos.bank.v1beta1.QueryBalanceResponse: + type: object + properties: + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + description: >- + QueryBalanceResponse is the response type for the Query/Balance RPC method. + cosmos.bank.v1beta1.QueryDenomMetadataResponse: + type: object + properties: + metadata: + type: object + properties: + description: + type: string + denom_units: + type: array + items: + type: object + properties: + denom: + type: string + description: >- + denom represents the string name of the given denom unit (e.g uatom). + exponent: + type: integer + format: int64 + description: >- + exponent represents power of 10 exponent that one must + + raise the base_denom to in order to equal the given DenomUnit's denom + + 1 denom = 10^exponent base_denom + + (e.g. with a base_denom of uatom, one can create a DenomUnit of 'atom' with + + exponent = 6, thus: 1 atom = 10^6 uatom). + aliases: + type: array + items: + type: string + title: aliases is a list of string aliases for the given denom + description: |- + DenomUnit represents a struct that describes a given + denomination unit of the basic token. + title: denom_units represents the list of DenomUnit's for a given coin + base: + type: string + description: >- + base represents the base denom (should be the DenomUnit with exponent = 0). + display: + type: string + description: |- + display indicates the suggested denom that should be + displayed in clients. + name: + type: string + description: 'Since: cosmos-sdk 0.43' + title: 'name defines the name of the token (eg: Cosmos Atom)' + symbol: + type: string + description: >- + symbol is the token symbol usually shown on exchanges (eg: ATOM). This can + + be the same as the display. + + Since: cosmos-sdk 0.43 + uri: + type: string + description: >- + URI to a document (on or off-chain) that contains additional information. Optional. + + Since: cosmos-sdk 0.46 + uri_hash: + type: string + description: >- + URIHash is a sha256 hash of a document pointed by URI. It's used to verify that + + the document didn't change. Optional. + + Since: cosmos-sdk 0.46 + description: |- + Metadata represents a struct that describes + a basic token. + description: >- + QueryDenomMetadataResponse is the response type for the Query/DenomMetadata RPC + + method. + cosmos.bank.v1beta1.QueryDenomOwnersResponse: + type: object + properties: + denom_owners: + type: array + items: + type: object + properties: + address: + type: string + description: address defines the address that owns a particular denomination. + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + + signatures required by gogoproto. + description: >- + DenomOwner defines structure representing an account that owns or holds a + + particular denominated token. It contains the account address and account + + balance of the denominated token. + + Since: cosmos-sdk 0.46 + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryDenomOwnersResponse defines the RPC response of a DenomOwners RPC query. + + Since: cosmos-sdk 0.46 + cosmos.bank.v1beta1.QueryDenomsMetadataResponse: + type: object + properties: + metadatas: + type: array + items: + type: object + properties: + description: + type: string + denom_units: + type: array + items: + type: object + properties: + denom: + type: string + description: >- + denom represents the string name of the given denom unit (e.g uatom). + exponent: + type: integer + format: int64 + description: >- + exponent represents power of 10 exponent that one must + + raise the base_denom to in order to equal the given DenomUnit's denom + + 1 denom = 10^exponent base_denom + + (e.g. with a base_denom of uatom, one can create a DenomUnit of 'atom' with + + exponent = 6, thus: 1 atom = 10^6 uatom). + aliases: + type: array + items: + type: string + title: aliases is a list of string aliases for the given denom + description: |- + DenomUnit represents a struct that describes a given denomination unit of the basic token. title: denom_units represents the list of DenomUnit's for a given coin base: @@ -51735,184 +54607,946 @@ definitions: properties: voter_address: type: string - vote_type: - $ref: '#/definitions/observerVoteType' - protobufAny: + vote_type: + $ref: '#/definitions/observerVoteType' + protobufAny: + type: object + properties: + '@type': + type: string + additionalProperties: {} + v1beta1PageRequest: + type: object + properties: + key: + type: string + format: byte + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + offset: + type: string + format: uint64 + description: |- + offset is a numeric offset that can be used when key is unavailable. + It is less efficient than using key. Only one of offset or key should + be set. + limit: + type: string + format: uint64 + description: |- + limit is the total number of results to be returned in the result page. + If left empty it will default to a value to be set by each app. + count_total: + type: boolean + description: |- + count_total is set to true to indicate that the result set should include + a count of the total number of items available for pagination in UIs. + count_total is only respected when offset is used. It is ignored when key + is set. + reverse: + type: boolean + description: |- + reverse is set to true if results are to be returned in the descending order. + + Since: cosmos-sdk 0.43 + description: |- + message SomeRequest { + Foo some_parameter = 1; + PageRequest pagination = 2; + } + title: |- + PageRequest is to be embedded in gRPC request messages for efficient + pagination. Ex: + v1beta1PageResponse: + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: |- + total is total number of results available if PageRequest.count_total + was set, its value is undefined otherwise + description: |- + PageResponse is to be embedded in gRPC response messages where the + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + zetacorecrosschainParams: + type: object + properties: + enabled: + type: boolean + description: Params defines the parameters for the module. + zetacorecrosschainQueryGetTssAddressResponse: + type: object + properties: + eth: + type: string + btc: + type: string + title: |- + Deprecated: Moved to observer + TODO: remove after v12 once upgrade testing is no longer needed with v11 + https://github.com/zeta-chain/node/issues/1547 + zetacorecrosschainQueryParamsResponse: + type: object + properties: + params: + $ref: '#/definitions/zetacorecrosschainParams' + description: params holds all the parameters of this module. + description: QueryParamsResponse is response type for the Query/Params RPC method. + zetacorecrosschainStatus: + type: object + properties: + status: + $ref: '#/definitions/crosschainCctxStatus' + status_message: + type: string + lastUpdate_timestamp: + type: string + format: int64 + zetacoreemissionsParams: + type: object + properties: + max_bond_factor: + type: string + min_bond_factor: + type: string + avg_block_time: + type: string + target_bond_ratio: + type: string + validator_emission_percentage: + type: string + observer_emission_percentage: + type: string + tss_signer_emission_percentage: + type: string + duration_factor_constant: + type: string + observer_slash_amount: + type: string + description: Params defines the parameters for the module. + zetacoreemissionsQueryParamsResponse: + type: object + properties: + params: + $ref: '#/definitions/zetacoreemissionsParams' + description: params holds all the parameters of this module. + description: QueryParamsResponse is response type for the Query/Params RPC method. + zetacorefungibleParams: + type: object + description: Params defines the parameters for the module. + zetacorefungibleQueryParamsResponse: + type: object + properties: + params: + $ref: '#/definitions/zetacorefungibleParams' + description: params holds all the parameters of this module. + description: QueryParamsResponse is response type for the Query/Params RPC method. + zetacoreobserverParams: + type: object + properties: + observer_params: + type: array + items: + type: object + $ref: '#/definitions/observerObserverParams' + title: 'Deprecated: Use ChainParamsList' + admin_policy: + type: array + items: + type: object + $ref: '#/definitions/observerAdmin_Policy' + ballot_maturity_blocks: + type: string + format: int64 + description: Params defines the parameters for the module. + zetacoreobserverQueryGetTssAddressResponse: + type: object + properties: + eth: + type: string + btc: + type: string + zetacoreobserverQueryParamsResponse: + type: object + properties: + params: + $ref: '#/definitions/zetacoreobserverParams' + description: params holds all the parameters of this module. + description: QueryParamsResponse is response type for the Query/Params RPC method. + ethermint.evm.v1.ChainConfig: + type: object + properties: + homestead_block: + type: string + title: Homestead switch block (nil no fork, 0 = already homestead) + dao_fork_block: + type: string + title: TheDAO hard-fork switch block (nil no fork) + dao_fork_support: + type: boolean + title: Whether the nodes supports or opposes the DAO hard-fork + eip150_block: + type: string + title: >- + EIP150 implements the Gas price changes + + (https://github.com/ethereum/EIPs/issues/150) EIP150 HF block (nil no fork) + eip150_hash: + type: string + title: >- + EIP150 HF hash (needed for header only clients as only gas pricing changed) + eip155_block: + type: string + title: EIP155Block HF block + eip158_block: + type: string + title: EIP158 HF block + byzantium_block: + type: string + title: Byzantium switch block (nil no fork, 0 = already on byzantium) + constantinople_block: + type: string + title: Constantinople switch block (nil no fork, 0 = already activated) + petersburg_block: + type: string + title: Petersburg switch block (nil same as Constantinople) + istanbul_block: + type: string + title: Istanbul switch block (nil no fork, 0 = already on istanbul) + muir_glacier_block: + type: string + title: >- + Eip-2384 (bomb delay) switch block (nil no fork, 0 = already activated) + berlin_block: + type: string + title: Berlin switch block (nil = no fork, 0 = already on berlin) + london_block: + type: string + title: London switch block (nil = no fork, 0 = already on london) + arrow_glacier_block: + type: string + title: >- + Eip-4345 (bomb delay) switch block (nil = no fork, 0 = already activated) + gray_glacier_block: + type: string + title: >- + EIP-5133 (bomb delay) switch block (nil = no fork, 0 = already activated) + merge_netsplit_block: + type: string + title: Virtual fork after The Merge to use as a network splitter + description: >- + ChainConfig defines the Ethereum ChainConfig parameters using *sdk.Int values + + instead of *big.Int. + ethermint.evm.v1.EstimateGasResponse: type: object properties: - '@type': + gas: type: string - additionalProperties: {} - v1beta1PageRequest: + format: uint64 + title: the estimated gas + title: EstimateGasResponse defines EstimateGas response + ethermint.evm.v1.Log: type: object properties: - key: + address: + type: string + title: address of the contract that generated the event + topics: + type: array + items: + type: string + description: list of topics provided by the contract. + data: type: string format: byte - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - offset: + title: supplied by the contract, usually ABI-encoded + block_number: type: string format: uint64 - description: |- - offset is a numeric offset that can be used when key is unavailable. - It is less efficient than using key. Only one of offset or key should - be set. - limit: + title: block in which the transaction was included + tx_hash: + type: string + title: hash of the transaction + tx_index: type: string format: uint64 - description: |- - limit is the total number of results to be returned in the result page. - If left empty it will default to a value to be set by each app. - count_total: - type: boolean - description: |- - count_total is set to true to indicate that the result set should include - a count of the total number of items available for pagination in UIs. - count_total is only respected when offset is used. It is ignored when key - is set. - reverse: + title: index of the transaction in the block + block_hash: + type: string + title: hash of the block in which the transaction was included + index: + type: string + format: uint64 + title: index of the log in the block + removed: type: boolean - description: |- - reverse is set to true if results are to be returned in the descending order. + description: >- + The Removed field is true if this log was reverted due to a chain - Since: cosmos-sdk 0.43 - description: |- - message SomeRequest { - Foo some_parameter = 1; - PageRequest pagination = 2; - } - title: |- - PageRequest is to be embedded in gRPC request messages for efficient - pagination. Ex: - v1beta1PageResponse: + reorganisation. You must pay attention to this field if you receive logs + + through a filter query. + description: >- + Log represents an protobuf compatible Ethereum Log that defines a contract + + log event. These events are generated by the LOG opcode and stored/indexed by + + the node. + ethermint.evm.v1.MsgEthereumTx: + type: object + properties: + data: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: inner transaction data + size: + type: number + format: double + title: 'DEPRECATED: encoded storage size of the transaction' + hash: + type: string + title: transaction hash in hex format + from: + type: string + title: |- + ethereum signer address in hex format. This address value is checked + against the address derived from the signature (V, R, S) using the + secp256k1 elliptic curve + description: MsgEthereumTx encapsulates an Ethereum transaction as an SDK message. + ethermint.evm.v1.MsgEthereumTxResponse: + type: object + properties: + hash: + type: string + title: |- + ethereum transaction hash in hex format. This hash differs from the + Tendermint sha256 hash of the transaction bytes. See + https://github.com/tendermint/tendermint/issues/6539 for reference + logs: + type: array + items: + type: object + properties: + address: + type: string + title: address of the contract that generated the event + topics: + type: array + items: + type: string + description: list of topics provided by the contract. + data: + type: string + format: byte + title: supplied by the contract, usually ABI-encoded + block_number: + type: string + format: uint64 + title: block in which the transaction was included + tx_hash: + type: string + title: hash of the transaction + tx_index: + type: string + format: uint64 + title: index of the transaction in the block + block_hash: + type: string + title: hash of the block in which the transaction was included + index: + type: string + format: uint64 + title: index of the log in the block + removed: + type: boolean + description: >- + The Removed field is true if this log was reverted due to a chain + + reorganisation. You must pay attention to this field if you receive logs + + through a filter query. + description: >- + Log represents an protobuf compatible Ethereum Log that defines a contract + + log event. These events are generated by the LOG opcode and stored/indexed by + + the node. + description: |- + logs contains the transaction hash and the proto-compatible ethereum + logs. + ret: + type: string + format: byte + title: |- + returned data from evm function (result or data supplied with revert + opcode) + vm_error: + type: string + title: vm error is the error returned by vm execution + gas_used: + type: string + format: uint64 + title: gas consumed by the transaction + description: MsgEthereumTxResponse defines the Msg/EthereumTx response type. + ethermint.evm.v1.Params: type: object properties: - next_key: + evm_denom: type: string - format: byte description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: |- - total is total number of results available if PageRequest.count_total - was set, its value is undefined otherwise - description: |- - PageResponse is to be embedded in gRPC response messages where the - corresponding request message has used PageRequest. + evm denom represents the token denomination used to run the EVM state + transitions. + enable_create: + type: boolean + title: >- + enable create toggles state transitions that use the vm.Create function + enable_call: + type: boolean + title: enable call toggles state transitions that use the vm.Call function + extra_eips: + type: array + items: + type: string + format: int64 + title: extra eips defines the additional EIPs for the vm.Config + chain_config: + title: chain config defines the EVM chain configuration parameters + type: object + properties: + homestead_block: + type: string + title: Homestead switch block (nil no fork, 0 = already homestead) + dao_fork_block: + type: string + title: TheDAO hard-fork switch block (nil no fork) + dao_fork_support: + type: boolean + title: Whether the nodes supports or opposes the DAO hard-fork + eip150_block: + type: string + title: >- + EIP150 implements the Gas price changes - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - zetacorecrosschainParams: - type: object - properties: - enabled: + (https://github.com/ethereum/EIPs/issues/150) EIP150 HF block (nil no fork) + eip150_hash: + type: string + title: >- + EIP150 HF hash (needed for header only clients as only gas pricing changed) + eip155_block: + type: string + title: EIP155Block HF block + eip158_block: + type: string + title: EIP158 HF block + byzantium_block: + type: string + title: Byzantium switch block (nil no fork, 0 = already on byzantium) + constantinople_block: + type: string + title: Constantinople switch block (nil no fork, 0 = already activated) + petersburg_block: + type: string + title: Petersburg switch block (nil same as Constantinople) + istanbul_block: + type: string + title: Istanbul switch block (nil no fork, 0 = already on istanbul) + muir_glacier_block: + type: string + title: >- + Eip-2384 (bomb delay) switch block (nil no fork, 0 = already activated) + berlin_block: + type: string + title: Berlin switch block (nil = no fork, 0 = already on berlin) + london_block: + type: string + title: London switch block (nil = no fork, 0 = already on london) + arrow_glacier_block: + type: string + title: >- + Eip-4345 (bomb delay) switch block (nil = no fork, 0 = already activated) + gray_glacier_block: + type: string + title: >- + EIP-5133 (bomb delay) switch block (nil = no fork, 0 = already activated) + merge_netsplit_block: + type: string + title: Virtual fork after The Merge to use as a network splitter + description: >- + ChainConfig defines the Ethereum ChainConfig parameters using *sdk.Int values + + instead of *big.Int. + allow_unprotected_txs: type: boolean - description: Params defines the parameters for the module. - zetacorecrosschainQueryGetTssAddressResponse: + description: >- + Allow unprotected transactions defines if replay-protected (i.e non EIP155 + + signed) transactions can be executed on the state machine. + title: Params defines the EVM module parameters + ethermint.evm.v1.QueryAccountResponse: type: object properties: - eth: + balance: type: string - btc: + description: balance is the balance of the EVM denomination. + code_hash: type: string - title: |- - Deprecated: Moved to observer - TODO: remove after v12 once upgrade testing is no longer needed with v11 - https://github.com/zeta-chain/node/issues/1547 - zetacorecrosschainQueryParamsResponse: + description: code hash is the hex-formatted code bytes from the EOA. + nonce: + type: string + format: uint64 + description: nonce is the account's sequence number. + description: >- + QueryAccountResponse is the response type for the Query/Account RPC method. + ethermint.evm.v1.QueryBalanceResponse: type: object properties: - params: - $ref: '#/definitions/zetacorecrosschainParams' - description: params holds all the parameters of this module. - description: QueryParamsResponse is response type for the Query/Params RPC method. - zetacorecrosschainStatus: + balance: + type: string + description: balance is the balance of the EVM denomination. + description: >- + QueryBalanceResponse is the response type for the Query/Balance RPC method. + ethermint.evm.v1.QueryBaseFeeResponse: type: object properties: - status: - $ref: '#/definitions/crosschainCctxStatus' - status_message: - type: string - lastUpdate_timestamp: + base_fee: type: string - format: int64 - zetacoreemissionsParams: + description: BaseFeeResponse returns the EIP1559 base fee. + ethermint.evm.v1.QueryCodeResponse: type: object properties: - max_bond_factor: - type: string - min_bond_factor: - type: string - avg_block_time: - type: string - target_bond_ratio: - type: string - validator_emission_percentage: - type: string - observer_emission_percentage: + code: type: string - tss_signer_emission_percentage: + format: byte + description: code represents the code bytes from an ethereum address. + description: |- + QueryCodeResponse is the response type for the Query/Code RPC + method. + ethermint.evm.v1.QueryCosmosAccountResponse: + type: object + properties: + cosmos_address: type: string - duration_factor_constant: + description: cosmos_address is the cosmos address of the account. + sequence: type: string - observer_slash_amount: + format: uint64 + description: sequence is the account's sequence number. + account_number: type: string - description: Params defines the parameters for the module. - zetacoreemissionsQueryParamsResponse: + format: uint64 + title: account_number is the account numbert + description: >- + QueryCosmosAccountResponse is the response type for the Query/CosmosAccount + + RPC method. + ethermint.evm.v1.QueryParamsResponse: type: object properties: params: - $ref: '#/definitions/zetacoreemissionsParams' - description: params holds all the parameters of this module. - description: QueryParamsResponse is response type for the Query/Params RPC method. - zetacorefungibleParams: + description: params define the evm module parameters. + type: object + properties: + evm_denom: + type: string + description: >- + evm denom represents the token denomination used to run the EVM state + + transitions. + enable_create: + type: boolean + title: >- + enable create toggles state transitions that use the vm.Create function + enable_call: + type: boolean + title: >- + enable call toggles state transitions that use the vm.Call function + extra_eips: + type: array + items: + type: string + format: int64 + title: extra eips defines the additional EIPs for the vm.Config + chain_config: + title: chain config defines the EVM chain configuration parameters + type: object + properties: + homestead_block: + type: string + title: Homestead switch block (nil no fork, 0 = already homestead) + dao_fork_block: + type: string + title: TheDAO hard-fork switch block (nil no fork) + dao_fork_support: + type: boolean + title: Whether the nodes supports or opposes the DAO hard-fork + eip150_block: + type: string + title: >- + EIP150 implements the Gas price changes + + (https://github.com/ethereum/EIPs/issues/150) EIP150 HF block (nil no fork) + eip150_hash: + type: string + title: >- + EIP150 HF hash (needed for header only clients as only gas pricing changed) + eip155_block: + type: string + title: EIP155Block HF block + eip158_block: + type: string + title: EIP158 HF block + byzantium_block: + type: string + title: Byzantium switch block (nil no fork, 0 = already on byzantium) + constantinople_block: + type: string + title: >- + Constantinople switch block (nil no fork, 0 = already activated) + petersburg_block: + type: string + title: Petersburg switch block (nil same as Constantinople) + istanbul_block: + type: string + title: Istanbul switch block (nil no fork, 0 = already on istanbul) + muir_glacier_block: + type: string + title: >- + Eip-2384 (bomb delay) switch block (nil no fork, 0 = already activated) + berlin_block: + type: string + title: Berlin switch block (nil = no fork, 0 = already on berlin) + london_block: + type: string + title: London switch block (nil = no fork, 0 = already on london) + arrow_glacier_block: + type: string + title: >- + Eip-4345 (bomb delay) switch block (nil = no fork, 0 = already activated) + gray_glacier_block: + type: string + title: >- + EIP-5133 (bomb delay) switch block (nil = no fork, 0 = already activated) + merge_netsplit_block: + type: string + title: Virtual fork after The Merge to use as a network splitter + description: >- + ChainConfig defines the Ethereum ChainConfig parameters using *sdk.Int values + + instead of *big.Int. + allow_unprotected_txs: + type: boolean + description: >- + Allow unprotected transactions defines if replay-protected (i.e non EIP155 + + signed) transactions can be executed on the state machine. + title: Params defines the EVM module parameters + description: >- + QueryParamsResponse defines the response type for querying x/evm parameters. + ethermint.evm.v1.QueryStorageResponse: type: object - description: Params defines the parameters for the module. - zetacorefungibleQueryParamsResponse: + properties: + value: + type: string + description: >- + key defines the storage state value hash associated with the given key. + description: |- + QueryStorageResponse is the response type for the Query/Storage RPC + method. + ethermint.evm.v1.QueryTraceBlockResponse: type: object properties: - params: - $ref: '#/definitions/zetacorefungibleParams' - description: params holds all the parameters of this module. - description: QueryParamsResponse is response type for the Query/Params RPC method. - zetacoreobserverParams: + data: + type: string + format: byte + title: QueryTraceBlockResponse defines TraceBlock response + ethermint.evm.v1.QueryTraceTxResponse: type: object properties: - observer_params: - type: array - items: - type: object - $ref: '#/definitions/observerObserverParams' - title: 'Deprecated: Use ChainParamsList' - admin_policy: - type: array - items: - type: object - $ref: '#/definitions/observerAdmin_Policy' - ballot_maturity_blocks: + data: type: string - format: int64 - description: Params defines the parameters for the module. - zetacoreobserverQueryGetTssAddressResponse: + format: byte + title: response serialized in bytes + title: QueryTraceTxResponse defines TraceTx response + ethermint.evm.v1.QueryValidatorAccountResponse: type: object properties: - eth: + account_address: type: string - btc: + description: account_address is the cosmos address of the account in bech32 format. + sequence: type: string - zetacoreobserverQueryParamsResponse: + format: uint64 + description: sequence is the account's sequence number. + account_number: + type: string + format: uint64 + title: account_number is the account number + description: |- + QueryValidatorAccountResponse is the response type for the + Query/ValidatorAccount RPC method. + ethermint.evm.v1.TraceConfig: type: object properties: - params: - $ref: '#/definitions/zetacoreobserverParams' - description: params holds all the parameters of this module. - description: QueryParamsResponse is response type for the Query/Params RPC method. + tracer: + type: string + title: custom javascript tracer + timeout: + type: string + title: >- + overrides the default timeout of 5 seconds for JavaScript-based tracing + + calls + reexec: + type: string + format: uint64 + title: number of blocks the tracer is willing to go back + disable_stack: + type: boolean + title: disable stack capture + disable_storage: + type: boolean + title: disable storage capture + debug: + type: boolean + title: print output during capture end + limit: + type: integer + format: int32 + title: maximum length of output, but zero means unlimited + overrides: + title: >- + Chain overrides, can be used to execute a trace using future fork rules + type: object + properties: + homestead_block: + type: string + title: Homestead switch block (nil no fork, 0 = already homestead) + dao_fork_block: + type: string + title: TheDAO hard-fork switch block (nil no fork) + dao_fork_support: + type: boolean + title: Whether the nodes supports or opposes the DAO hard-fork + eip150_block: + type: string + title: >- + EIP150 implements the Gas price changes + + (https://github.com/ethereum/EIPs/issues/150) EIP150 HF block (nil no fork) + eip150_hash: + type: string + title: >- + EIP150 HF hash (needed for header only clients as only gas pricing changed) + eip155_block: + type: string + title: EIP155Block HF block + eip158_block: + type: string + title: EIP158 HF block + byzantium_block: + type: string + title: Byzantium switch block (nil no fork, 0 = already on byzantium) + constantinople_block: + type: string + title: Constantinople switch block (nil no fork, 0 = already activated) + petersburg_block: + type: string + title: Petersburg switch block (nil same as Constantinople) + istanbul_block: + type: string + title: Istanbul switch block (nil no fork, 0 = already on istanbul) + muir_glacier_block: + type: string + title: >- + Eip-2384 (bomb delay) switch block (nil no fork, 0 = already activated) + berlin_block: + type: string + title: Berlin switch block (nil = no fork, 0 = already on berlin) + london_block: + type: string + title: London switch block (nil = no fork, 0 = already on london) + arrow_glacier_block: + type: string + title: >- + Eip-4345 (bomb delay) switch block (nil = no fork, 0 = already activated) + gray_glacier_block: + type: string + title: >- + EIP-5133 (bomb delay) switch block (nil = no fork, 0 = already activated) + merge_netsplit_block: + type: string + title: Virtual fork after The Merge to use as a network splitter + description: >- + ChainConfig defines the Ethereum ChainConfig parameters using *sdk.Int values + + instead of *big.Int. + enable_memory: + type: boolean + title: enable memory capture + enable_return_data: + type: boolean + title: enable return data capture + tracer_json_config: + type: string + title: tracer config + description: TraceConfig holds extra parameters to trace functions. diff --git a/scripts/protoc-gen-openapi.sh b/scripts/protoc-gen-openapi.sh index 0889b5a43b..994f107b19 100755 --- a/scripts/protoc-gen-openapi.sh +++ b/scripts/protoc-gen-openapi.sh @@ -5,6 +5,7 @@ go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@v2.16. go mod download COSMOS_SDK="github.com/cosmos/cosmos-sdk" +ETHERMINT="github.com/evmos/ethermint" PROTO_TEMPLATE="proto/buf.openapi.yaml" OUTPUT_DIR="./docs/openapi" MERGED_SWAGGER_FILE="openapi.swagger.yaml" @@ -19,9 +20,13 @@ info: # Get the directory path for the cosmos-sdk DIR_PATH=$(go list -f '{{ .Dir }}' -m $COSMOS_SDK) +DIR_PATH_ETHERMINT=$(go list -f '{{ .Dir }}' -m $ETHERMINT) + # Find the first OpenAPI YAML file and output its path COSMOS_OPENAPI_PATH=$(find "$DIR_PATH" -type f -name "*.yaml" -exec grep -q "swagger:" {} \; -exec echo {} \; | head -n 1) +ETHERMINT_OPENAPI_PATH=$(find "$DIR_PATH_ETHERMINT" -type f -name "*.yaml" -exec grep -q "swagger:" {} \; -exec grep -l "/ethermint/evm/v1/" {} + | head -n 1) + # Generate OpenAPI YAML file using buf buf generate --template $PROTO_TEMPLATE --output=$OUTPUT_DIR $ZETACHAIN_PROTO_PATH @@ -32,9 +37,9 @@ echo "$OPENAPI_HEADER" > $OUTPUT_DIR/$MERGED_SWAGGER_FILE # Merge them using yq and write to the merged swagger file { echo "paths:" - yq ea 'select(fileIndex == 0).paths * select(fileIndex == 1).paths' $COSMOS_OPENAPI_PATH $OUTPUT_DIR/$ZETACHAIN_OPENAPI | sed -e 's/^/ /' + yq ea 'select(fileIndex == 0).paths * select(fileIndex == 1).paths * select(fileIndex == 2).paths' $COSMOS_OPENAPI_PATH $OUTPUT_DIR/$ZETACHAIN_OPENAPI $ETHERMINT_OPENAPI_PATH | sed -e 's/^/ /' echo "definitions:" - yq ea 'select(fileIndex == 0).definitions * select(fileIndex == 1).definitions' $COSMOS_OPENAPI_PATH $OUTPUT_DIR/$ZETACHAIN_OPENAPI | sed -e 's/^/ /' + yq ea 'select(fileIndex == 0).definitions * select(fileIndex == 1).definitions * select(fileIndex == 2).definitions' $COSMOS_OPENAPI_PATH $OUTPUT_DIR/$ZETACHAIN_OPENAPI $ETHERMINT_OPENAPI_PATH | sed -e 's/^/ /' } >> $OUTPUT_DIR/$MERGED_SWAGGER_FILE # Check if the merged swagger file was created successfully From ae22f1e01e3ed3feebecabe561ca6b612a91c578 Mon Sep 17 00:00:00 2001 From: kevinssgh <79858682+kevinssgh@users.noreply.github.com> Date: Mon, 12 Feb 2024 15:40:29 -0500 Subject: [PATCH 3/7] refactor: zetaclient re-organization (#1640) * initial refactor commit * fix lint errors * update changelog * fix import cycle * ran make generate * run make generate again * fix error * renamed btc to bitcoin, removed klayton, and moved evm/utils functions to inbounds.go * ran make generate and addressed comments * moved dynamic ticker into types section * use a different name for btc client * ran make generate * fix test file --------- Co-authored-by: Charlie Chen --- changelog.md | 1 + cmd/zetaclientd/debug.go | 17 +- cmd/zetaclientd/keygen_tss.go | 10 +- cmd/zetaclientd/p2p_diagnostics.go | 7 +- cmd/zetaclientd/start.go | 2 +- cmd/zetaclientd/utils.go | 44 +- common/chain.go | 4 - .../orchestrator/smoketest/runner/bitcoin.go | 15 +- .../smoketest/runner/setup_evm.go | 4 +- .../smoketest/smoketests/test_donation.go | 4 +- zetaclient/{ => authz}/authz_signer.go | 14 +- zetaclient/{ => bitcoin}/bitcoin_client.go | 154 ++++--- .../{ => bitcoin}/bitcoin_client_db_test.go | 9 +- .../{ => bitcoin}/bitcoin_client_rpc_test.go | 8 +- .../{ => bitcoin}/bitcoin_client_test.go | 4 +- .../bitcoin_signer.go} | 33 +- .../bitcoin_signer_test.go} | 21 +- .../{btc_test.go => bitcoin/bitcoin_test.go} | 14 +- zetaclient/bitcoin/inbound_tracker.go | 92 ++++ zetaclient/bitcoin/utils.go | 129 ++++++ zetaclient/config/types.go | 2 +- zetaclient/{ => errors}/errors.go | 2 +- zetaclient/{ => evm}/evm_client.go | 185 ++++---- zetaclient/{ => evm}/evm_client_db_test.go | 2 +- zetaclient/{ => evm}/evm_signer.go | 59 +-- .../{inbound_tracker.go => evm/inbounds.go} | 253 ++++++---- zetaclient/{ => interfaces}/interfaces.go | 22 +- zetaclient/{ => interfaces}/signer.go | 2 +- zetaclient/{ => interfaces}/signer_test.go | 2 +- zetaclient/{ => keys}/keys.go | 20 +- zetaclient/{ => keys}/keys_test.go | 15 +- zetaclient/klaytn_client.go | 71 --- zetaclient/{ => metrics}/chainmetrics.go | 11 +- zetaclient/{ => metrics}/telemetry.go | 11 +- zetaclient/misc.go | 9 - .../out_tx_processor_manager.go | 16 +- .../zeta_supply_checker.go | 25 +- .../zeta_supply_checker_test.go | 5 +- zetaclient/{ => tss}/tss_signer.go | 38 +- zetaclient/{ => tss}/tss_signer_test.go | 8 +- zetaclient/tx.go | 195 -------- zetaclient/tx_vote_inbound.go | 153 ------- zetaclient/tx_vote_outbound.go | 154 ------- zetaclient/types/dynamic_ticker.go | 44 ++ zetaclient/utils.go | 326 ------------- zetaclient/utils_test.go | 6 +- zetaclient/voter_test.go | 21 +- zetaclient/{ => zetabridge}/block_height.go | 2 +- zetaclient/{ => zetabridge}/broadcast.go | 28 +- zetaclient/{ => zetabridge}/broadcast_test.go | 4 +- zetaclient/{ => zetabridge}/query.go | 18 +- zetaclient/zetabridge/tx.go | 431 ++++++++++++++++++ zetaclient/zetabridge/tx_vote_inbound.go | 55 +++ .../{ => zetabridge}/zetacore_bridge.go | 22 +- zetaclient/zetaclient_test.go | 13 +- zetaclient/zetacore_observer.go | 72 ++- zetaclient/zetacore_observer_test.go | 30 +- 57 files changed, 1448 insertions(+), 1470 deletions(-) rename zetaclient/{ => authz}/authz_signer.go (70%) rename zetaclient/{ => bitcoin}/bitcoin_client.go (89%) rename zetaclient/{ => bitcoin}/bitcoin_client_db_test.go (89%) rename zetaclient/{ => bitcoin}/bitcoin_client_rpc_test.go (98%) rename zetaclient/{ => bitcoin}/bitcoin_client_test.go (93%) rename zetaclient/{btc_signer.go => bitcoin/bitcoin_signer.go} (92%) rename zetaclient/{btc_signer_test.go => bitcoin/bitcoin_signer_test.go} (98%) rename zetaclient/{btc_test.go => bitcoin/bitcoin_test.go} (90%) create mode 100644 zetaclient/bitcoin/inbound_tracker.go create mode 100644 zetaclient/bitcoin/utils.go rename zetaclient/{ => errors}/errors.go (89%) rename zetaclient/{ => evm}/evm_client.go (89%) rename zetaclient/{ => evm}/evm_client_db_test.go (99%) rename zetaclient/{ => evm}/evm_signer.go (94%) rename zetaclient/{inbound_tracker.go => evm/inbounds.go} (53%) rename zetaclient/{ => interfaces}/interfaces.go (94%) rename zetaclient/{ => interfaces}/signer.go (99%) rename zetaclient/{ => interfaces}/signer_test.go (98%) rename zetaclient/{ => keys}/keys.go (87%) rename zetaclient/{ => keys}/keys_test.go (84%) delete mode 100644 zetaclient/klaytn_client.go rename zetaclient/{ => metrics}/chainmetrics.go (76%) rename zetaclient/{ => metrics}/telemetry.go (96%) delete mode 100644 zetaclient/misc.go rename zetaclient/{ => outtxprocessor}/out_tx_processor_manager.go (78%) rename zetaclient/{ => supplychecker}/zeta_supply_checker.go (89%) rename zetaclient/{ => supplychecker}/zeta_supply_checker_test.go (85%) rename zetaclient/{ => tss}/tss_signer.go (95%) rename zetaclient/{ => tss}/tss_signer_test.go (92%) delete mode 100644 zetaclient/tx.go delete mode 100644 zetaclient/tx_vote_inbound.go delete mode 100644 zetaclient/tx_vote_outbound.go create mode 100644 zetaclient/types/dynamic_ticker.go delete mode 100644 zetaclient/utils.go rename zetaclient/{ => zetabridge}/block_height.go (96%) rename zetaclient/{ => zetabridge}/broadcast.go (82%) rename zetaclient/{ => zetabridge}/broadcast_test.go (73%) rename zetaclient/{ => zetabridge}/query.go (98%) create mode 100644 zetaclient/zetabridge/tx.go create mode 100644 zetaclient/zetabridge/tx_vote_inbound.go rename zetaclient/{ => zetabridge}/zetacore_bridge.go (92%) diff --git a/changelog.md b/changelog.md index 2beb385214..229a1cf497 100644 --- a/changelog.md +++ b/changelog.md @@ -81,6 +81,7 @@ ### Refactoring * [1628](https://github.com/zeta-chain/node/pull/1628) optimize return and simplify code +* [1640](https://github.com/zeta-chain/node/pull/1640) reorganize zetaclient into subpackages ### Refactoring * [1619](https://github.com/zeta-chain/node/pull/1619) - Add evm fee calculation to tss migration of evm chains diff --git a/cmd/zetaclientd/debug.go b/cmd/zetaclientd/debug.go index 1bbb2a7934..4bc251d54a 100644 --- a/cmd/zetaclientd/debug.go +++ b/cmd/zetaclientd/debug.go @@ -8,6 +8,12 @@ import ( "strings" "sync" + "github.com/zeta-chain/zetacore/zetaclient/bitcoin" + "github.com/zeta-chain/zetacore/zetaclient/evm" + "github.com/zeta-chain/zetacore/zetaclient/keys" + "github.com/zeta-chain/zetacore/zetaclient/metrics" + "github.com/zeta-chain/zetacore/zetaclient/zetabridge" + "github.com/btcsuite/btcd/rpcclient" sdk "github.com/cosmos/cosmos-sdk/types" ethcommon "github.com/ethereum/go-ethereum/common" @@ -17,7 +23,6 @@ import ( "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/testutil/sample" observertypes "github.com/zeta-chain/zetacore/x/observer/types" - "github.com/zeta-chain/zetacore/zetaclient" "github.com/zeta-chain/zetacore/zetaclient/config" ) @@ -54,7 +59,7 @@ func DebugCmd() *cobra.Command { var ballotIdentifier string chainLogger := zerolog.New(io.Discard).Level(zerolog.Disabled) - telemetryServer := zetaclient.NewTelemetryServer() + telemetryServer := metrics.NewTelemetryServer() go func() { err := telemetryServer.Start() if err != nil { @@ -62,8 +67,8 @@ func DebugCmd() *cobra.Command { } }() - bridge, err := zetaclient.NewZetaCoreBridge( - &zetaclient.Keys{OperatorAddress: sdk.MustAccAddressFromBech32(sample.AccAddress())}, + bridge, err := zetabridge.NewZetaCoreBridge( + &keys.Keys{OperatorAddress: sdk.MustAccAddressFromBech32(sample.AccAddress())}, debugArgs.zetaNode, "", debugArgs.zetaChainID, @@ -89,7 +94,7 @@ func DebugCmd() *cobra.Command { if common.IsEVMChain(chain.ChainId) { - ob := zetaclient.EVMChainClient{ + ob := evm.ChainClient{ Mu: &sync.Mutex{}, } ob.WithZetaClient(bridge) @@ -159,7 +164,7 @@ func DebugCmd() *cobra.Command { } fmt.Println("CoinType : ", coinType) } else if common.IsBitcoinChain(chain.ChainId) { - obBtc := zetaclient.BitcoinChainClient{ + obBtc := bitcoin.BTCChainClient{ Mu: &sync.Mutex{}, } obBtc.WithZetaClient(bridge) diff --git a/cmd/zetaclientd/keygen_tss.go b/cmd/zetaclientd/keygen_tss.go index 1c77086f59..215f680a99 100644 --- a/cmd/zetaclientd/keygen_tss.go +++ b/cmd/zetaclientd/keygen_tss.go @@ -7,6 +7,9 @@ import ( "fmt" "time" + mc "github.com/zeta-chain/zetacore/zetaclient/tss" + "github.com/zeta-chain/zetacore/zetaclient/zetabridge" + "github.com/rs/zerolog" "github.com/tendermint/crypto/sha3" "github.com/tendermint/tendermint/crypto/secp256k1" @@ -15,17 +18,16 @@ import ( "github.com/zeta-chain/go-tss/p2p" "github.com/zeta-chain/zetacore/common" observertypes "github.com/zeta-chain/zetacore/x/observer/types" - mc "github.com/zeta-chain/zetacore/zetaclient" "github.com/zeta-chain/zetacore/zetaclient/config" "github.com/zeta-chain/zetacore/zetaclient/metrics" ) func GenerateTss(logger zerolog.Logger, cfg *config.Config, - zetaBridge *mc.ZetaCoreBridge, + zetaBridge *zetabridge.ZetaCoreBridge, peers p2p.AddrList, priKey secp256k1.PrivKey, - ts *mc.TelemetryServer, + ts *metrics.TelemetryServer, tssHistoricalList []observertypes.TSS, metrics *metrics.Metrics, tssPassword string, @@ -63,7 +65,7 @@ func GenerateTss(logger zerolog.Logger, // Set TSS block to 0 using genesis file to disable this feature // Note : The TSS generation is done through the "hotkey" or "Zeta-clientGrantee" This key needs to be present on the machine for the TSS signing to happen . // "ZetaClientGrantee" key is different from the "operator" key .The "Operator" key gives all zetaclient related permissions such as TSS generation ,reporting and signing, INBOUND and OUTBOUND vote signing, to the "ZetaClientGrantee" key. - // The votes to signify a successful TSS generation (Or unsuccessful) is signed by the operator key and broadcast to zetacore by the zetcalientGrantee key on behalf of the operator . + // The votes to signify a successful TSS generation (Or unsuccessful) is signed by the operator key and broadcast to zetabridge by the zetcalientGrantee key on behalf of the operator . ticker := time.NewTicker(time.Second * 1) triedKeygenAtBlock := false lastBlock := int64(0) diff --git a/cmd/zetaclientd/p2p_diagnostics.go b/cmd/zetaclientd/p2p_diagnostics.go index 5253e9cb4f..38bc269bc6 100644 --- a/cmd/zetaclientd/p2p_diagnostics.go +++ b/cmd/zetaclientd/p2p_diagnostics.go @@ -7,6 +7,8 @@ import ( "sync" "time" + "github.com/zeta-chain/zetacore/zetaclient/metrics" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" libp2p "github.com/libp2p/go-libp2p" dht "github.com/libp2p/go-libp2p-kad-dht" @@ -21,7 +23,6 @@ import ( "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/zeta-chain/go-tss/p2p" "github.com/zeta-chain/zetacore/common/cosmos" - mc "github.com/zeta-chain/zetacore/zetaclient" "github.com/zeta-chain/zetacore/zetaclient/config" ) @@ -38,7 +39,7 @@ func RunDiagnostics(startLogger zerolog.Logger, peers p2p.AddrList, bridgePk cry } startLogger.Warn().Msgf("my pubkey %s", pubkeyBech32) - var s *mc.TelemetryServer + var s *metrics.TelemetryServer if len(peers) == 0 { startLogger.Warn().Msg("No seed peer specified; assuming I'm the host") @@ -83,7 +84,7 @@ func RunDiagnostics(startLogger zerolog.Logger, peers p2p.AddrList, bridgePk cry } startLogger.Info().Msgf("host created: ID %s", host.ID().String()) if len(peers) == 0 { - s = mc.NewTelemetryServer() + s = metrics.NewTelemetryServer() s.SetP2PID(host.ID().String()) go func() { startLogger.Info().Msg("Starting TSS HTTP Server...") diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index 72d6c43b3f..e22d86b356 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -73,7 +73,7 @@ func start(_ *cobra.Command, _ []string) error { waitForZetaCore(cfg, startLogger) startLogger.Info().Msgf("ZetaCore is ready , Trying to connect to %s", cfg.Peer) - telemetryServer := mc.NewTelemetryServer() + telemetryServer := metrics2.NewTelemetryServer() go func() { err := telemetryServer.Start() if err != nil { diff --git a/cmd/zetaclientd/utils.go b/cmd/zetaclientd/utils.go index c1a4dfa069..87c009c11b 100644 --- a/cmd/zetaclientd/utils.go +++ b/cmd/zetaclientd/utils.go @@ -6,16 +6,22 @@ import ( "github.com/rs/zerolog" "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/common/cosmos" - "github.com/zeta-chain/zetacore/zetaclient" + "github.com/zeta-chain/zetacore/zetaclient/authz" + "github.com/zeta-chain/zetacore/zetaclient/bitcoin" "github.com/zeta-chain/zetacore/zetaclient/config" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/keys" "github.com/zeta-chain/zetacore/zetaclient/metrics" + "github.com/zeta-chain/zetacore/zetaclient/zetabridge" + + "github.com/zeta-chain/zetacore/zetaclient/evm" ) func CreateAuthzSigner(granter string, grantee sdk.AccAddress) { - zetaclient.SetupAuthZSignerList(granter, grantee) + authz.SetupAuthZSignerList(granter, grantee) } -func CreateZetaBridge(cfg *config.Config, telemetry *zetaclient.TelemetryServer, hotkeyPassword string) (*zetaclient.ZetaCoreBridge, error) { +func CreateZetaBridge(cfg *config.Config, telemetry *metrics.TelemetryServer, hotkeyPassword string) (*zetabridge.ZetaCoreBridge, error) { hotKey := cfg.AuthzHotkey if cfg.HsmMode { hotKey = cfg.HsmHotKey @@ -23,7 +29,7 @@ func CreateZetaBridge(cfg *config.Config, telemetry *zetaclient.TelemetryServer, chainIP := cfg.ZetaCoreURL - kb, _, err := zetaclient.GetKeyringKeybase(cfg, hotkeyPassword) + kb, _, err := keys.GetKeyringKeybase(cfg, hotkeyPassword) if err != nil { return nil, err } @@ -33,9 +39,9 @@ func CreateZetaBridge(cfg *config.Config, telemetry *zetaclient.TelemetryServer, return nil, err } - k := zetaclient.NewKeysWithKeybase(kb, granterAddreess, cfg.AuthzHotkey, hotkeyPassword) + k := keys.NewKeysWithKeybase(kb, granterAddreess, cfg.AuthzHotkey, hotkeyPassword) - bridge, err := zetaclient.NewZetaCoreBridge(k, chainIP, hotKey, cfg.ChainID, cfg.HsmMode, telemetry) + bridge, err := zetabridge.NewZetaCoreBridge(k, chainIP, hotKey, cfg.ChainID, cfg.HsmMode, telemetry) if err != nil { return nil, err } @@ -44,12 +50,12 @@ func CreateZetaBridge(cfg *config.Config, telemetry *zetaclient.TelemetryServer, } func CreateSignerMap( - tss zetaclient.TSSSigner, + tss interfaces.TSSSigner, logger zerolog.Logger, cfg *config.Config, - ts *zetaclient.TelemetryServer, -) (map[common.Chain]zetaclient.ChainSigner, error) { - signerMap := make(map[common.Chain]zetaclient.ChainSigner) + ts *metrics.TelemetryServer, +) (map[common.Chain]interfaces.ChainSigner, error) { + signerMap := make(map[common.Chain]interfaces.ChainSigner) // EVM signers for _, evmConfig := range cfg.GetAllEVMConfigs() { if evmConfig.Chain.IsZetaChain() { @@ -57,7 +63,7 @@ func CreateSignerMap( } mpiAddress := ethcommon.HexToAddress(evmConfig.ChainParams.ConnectorContractAddress) erc20CustodyAddress := ethcommon.HexToAddress(evmConfig.ChainParams.Erc20CustodyContractAddress) - signer, err := zetaclient.NewEVMSigner(evmConfig.Chain, evmConfig.Endpoint, tss, config.GetConnectorABI(), config.GetERC20CustodyABI(), mpiAddress, erc20CustodyAddress, logger, ts) + signer, err := evm.NewEVMSigner(evmConfig.Chain, evmConfig.Endpoint, tss, config.GetConnectorABI(), config.GetERC20CustodyABI(), mpiAddress, erc20CustodyAddress, logger, ts) if err != nil { logger.Error().Err(err).Msgf("NewEVMSigner error for chain %s", evmConfig.Chain.String()) continue @@ -67,7 +73,7 @@ func CreateSignerMap( // BTC signer btcChain, btcConfig, enabled := cfg.GetBTCConfig() if enabled { - signer, err := zetaclient.NewBTCSigner(btcConfig, tss, logger, ts) + signer, err := bitcoin.NewBTCSigner(btcConfig, tss, logger, ts) if err != nil { logger.Error().Err(err).Msgf("NewBTCSigner error for chain %s", btcChain.String()) } else { @@ -79,21 +85,21 @@ func CreateSignerMap( } func CreateChainClientMap( - bridge *zetaclient.ZetaCoreBridge, - tss zetaclient.TSSSigner, + bridge *zetabridge.ZetaCoreBridge, + tss interfaces.TSSSigner, dbpath string, metrics *metrics.Metrics, logger zerolog.Logger, cfg *config.Config, - ts *zetaclient.TelemetryServer, -) (map[common.Chain]zetaclient.ChainClient, error) { - clientMap := make(map[common.Chain]zetaclient.ChainClient) + ts *metrics.TelemetryServer, +) (map[common.Chain]interfaces.ChainClient, error) { + clientMap := make(map[common.Chain]interfaces.ChainClient) // EVM clients for _, evmConfig := range cfg.GetAllEVMConfigs() { if evmConfig.Chain.IsZetaChain() { continue } - co, err := zetaclient.NewEVMChainClient(bridge, tss, dbpath, metrics, logger, cfg, *evmConfig, ts) + co, err := evm.NewEVMChainClient(bridge, tss, dbpath, metrics, logger, cfg, *evmConfig, ts) if err != nil { logger.Error().Err(err).Msgf("NewEVMChainClient error for chain %s", evmConfig.Chain.String()) continue @@ -103,7 +109,7 @@ func CreateChainClientMap( // BTC client btcChain, btcConfig, enabled := cfg.GetBTCConfig() if enabled { - co, err := zetaclient.NewBitcoinClient(btcChain, bridge, tss, dbpath, metrics, logger, btcConfig, ts) + co, err := bitcoin.NewBitcoinClient(btcChain, bridge, tss, dbpath, metrics, logger, btcConfig, ts) if err != nil { logger.Error().Err(err).Msgf("NewBitcoinClient error for chain %s", btcChain.String()) diff --git a/common/chain.go b/common/chain.go index 303e3a238e..d048a251b1 100644 --- a/common/chain.go +++ b/common/chain.go @@ -114,10 +114,6 @@ func IsHeaderSupportedEvmChain(chainID int64) bool { chainID == 56 // bsc mainnet } -func (chain Chain) IsKlaytnChain() bool { - return chain.ChainId == 1001 -} - // SupportMerkleProof returns true if the chain supports block header-based verification func (chain Chain) SupportMerkleProof() bool { return IsEVMChain(chain.ChainId) || IsBitcoinChain(chain.ChainId) diff --git a/contrib/localnet/orchestrator/smoketest/runner/bitcoin.go b/contrib/localnet/orchestrator/smoketest/runner/bitcoin.go index b0ec145039..92d804f013 100644 --- a/contrib/localnet/orchestrator/smoketest/runner/bitcoin.go +++ b/contrib/localnet/orchestrator/smoketest/runner/bitcoin.go @@ -7,6 +7,8 @@ import ( "math/big" "time" + "github.com/zeta-chain/zetacore/common/bitcoin" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" @@ -19,9 +21,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rs/zerolog/log" "github.com/zeta-chain/zetacore/common" - "github.com/zeta-chain/zetacore/common/bitcoin" observertypes "github.com/zeta-chain/zetacore/x/observer/types" - "github.com/zeta-chain/zetacore/zetaclient" + zetabitcoin "github.com/zeta-chain/zetacore/zetaclient/bitcoin" ) var blockHeaderBTCTimeout = 5 * time.Minute @@ -54,7 +55,7 @@ func (sm *SmokeTestRunner) DepositBTCWithAmount(amount float64) (txHash *chainha sm.Logger.Info(" spendableUTXOs: %d", spendableUTXOs) sm.Logger.Info("Now sending two txs to TSS address...") - amount = amount + zetaclient.BtcDepositorFeeMin + amount = amount + zetabitcoin.BtcDepositorFeeMin txHash, err = sm.SendToTSSFromDeployerToDeposit(sm.BTCTSSAddress, amount, utxos, sm.BtcRPCClient, sm.BTCDeployerAddress) if err != nil { panic(err) @@ -100,12 +101,12 @@ func (sm *SmokeTestRunner) DepositBTC(testHeader bool) { sm.Logger.Info("Now sending two txs to TSS address...") // send two transactions to the TSS address - amount1 := 1.1 + zetaclient.BtcDepositorFeeMin + amount1 := 1.1 + zetabitcoin.BtcDepositorFeeMin txHash1, err := sm.SendToTSSFromDeployerToDeposit(sm.BTCTSSAddress, amount1, utxos[:2], btc, sm.BTCDeployerAddress) if err != nil { panic(err) } - amount2 := 0.05 + zetaclient.BtcDepositorFeeMin + amount2 := 0.05 + zetabitcoin.BtcDepositorFeeMin txHash2, err := sm.SendToTSSFromDeployerToDeposit(sm.BTCTSSAddress, amount2, utxos[2:4], btc, sm.BTCDeployerAddress) if err != nil { panic(err) @@ -118,7 +119,7 @@ func (sm *SmokeTestRunner) DepositBTC(testHeader bool) { 0.11, utxos[4:5], btc, - []byte(zetaclient.DonationMessage), + []byte(zetabitcoin.DonationMessage), sm.BTCDeployerAddress, ) if err != nil { @@ -269,7 +270,7 @@ func (sm *SmokeTestRunner) SendToTSSFromDeployerWithMemo( if err != nil { panic(err) } - events := zetaclient.FilterAndParseIncomingTx( + events := zetabitcoin.FilterAndParseIncomingTx( []btcjson.TxRawResult{*rawtx}, 0, sm.BTCTSSAddress.EncodeAddress(), diff --git a/contrib/localnet/orchestrator/smoketest/runner/setup_evm.go b/contrib/localnet/orchestrator/smoketest/runner/setup_evm.go index 5d89d9a3e4..135dcd3119 100644 --- a/contrib/localnet/orchestrator/smoketest/runner/setup_evm.go +++ b/contrib/localnet/orchestrator/smoketest/runner/setup_evm.go @@ -4,7 +4,7 @@ import ( "math/big" "time" - "github.com/zeta-chain/zetacore/zetaclient" + "github.com/zeta-chain/zetacore/zetaclient/evm" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" @@ -63,7 +63,7 @@ func (sm *SmokeTestRunner) SetupEVM(contractsDeployed bool) { // donate to the TSS address to avoid account errors because deploying gas token ZRC20 will automatically mint // gas token on ZetaChain to initialize the pool - txDonation, err := sm.SendEther(sm.TSSAddress, big.NewInt(101000000000000000), []byte(zetaclient.DonationMessage)) + txDonation, err := sm.SendEther(sm.TSSAddress, big.NewInt(101000000000000000), []byte(evm.DonationMessage)) if err != nil { panic(err) } diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_donation.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_donation.go index a81af67f3f..d23699b0d4 100644 --- a/contrib/localnet/orchestrator/smoketest/smoketests/test_donation.go +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_donation.go @@ -5,12 +5,12 @@ import ( "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" - "github.com/zeta-chain/zetacore/zetaclient" + "github.com/zeta-chain/zetacore/zetaclient/evm" ) // TestDonationEther tests donation of ether to the tss address func TestDonationEther(sm *runner.SmokeTestRunner) { - txDonation, err := sm.SendEther(sm.TSSAddress, big.NewInt(100000000000000000), []byte(zetaclient.DonationMessage)) + txDonation, err := sm.SendEther(sm.TSSAddress, big.NewInt(100000000000000000), []byte(evm.DonationMessage)) if err != nil { panic(err) } diff --git a/zetaclient/authz_signer.go b/zetaclient/authz/authz_signer.go similarity index 70% rename from zetaclient/authz_signer.go rename to zetaclient/authz/authz_signer.go index acde1dafd0..cc9cccb37a 100644 --- a/zetaclient/authz_signer.go +++ b/zetaclient/authz/authz_signer.go @@ -1,4 +1,4 @@ -package zetaclient +package authz import ( sdk "github.com/cosmos/cosmos-sdk/types" @@ -6,22 +6,22 @@ import ( crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" ) -type AuthZSigner struct { +type Signer struct { KeyType common.KeyType GranterAddress string GranteeAddress sdk.AccAddress } -func (a AuthZSigner) String() string { +func (a Signer) String() string { return a.KeyType.String() + " " + a.GranterAddress + " " + a.GranteeAddress.String() } -var signers map[string]AuthZSigner +var signers map[string]Signer func init() { - signersList := make(map[string]AuthZSigner) + signersList := make(map[string]Signer) for _, tx := range crosschaintypes.GetAllAuthzZetaclientTxTypes() { - signersList[tx] = AuthZSigner{KeyType: common.ZetaClientGranteeKey} + signersList[tx] = Signer{KeyType: common.ZetaClientGranteeKey} } signers = signersList } @@ -34,6 +34,6 @@ func SetupAuthZSignerList(granter string, grantee sdk.AccAddress) { } } -func GetSigner(msgURL string) AuthZSigner { +func GetSigner(msgURL string) Signer { return signers[msgURL] } diff --git a/zetaclient/bitcoin_client.go b/zetaclient/bitcoin/bitcoin_client.go similarity index 89% rename from zetaclient/bitcoin_client.go rename to zetaclient/bitcoin/bitcoin_client.go index aeb44fe4b2..98f94c14b3 100644 --- a/zetaclient/bitcoin_client.go +++ b/zetaclient/bitcoin/bitcoin_client.go @@ -1,4 +1,4 @@ -package zetaclient +package bitcoin import ( "bytes" @@ -13,6 +13,9 @@ import ( "sync/atomic" "time" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/zetabridge" + cosmosmath "cosmossdk.io/math" "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -33,7 +36,7 @@ import ( "gorm.io/gorm/logger" ) -var _ ChainClient = &BitcoinChainClient{} +var _ interfaces.ChainClient = &BTCChainClient{} type BTCLog struct { ChainLogger zerolog.Logger @@ -43,15 +46,15 @@ type BTCLog struct { WatchGasPrice zerolog.Logger } -// BitcoinChainClient represents a chain configuration for Bitcoin +// BTCChainClient represents a chain configuration for Bitcoin // Filled with above constants depending on chain -type BitcoinChainClient struct { - *ChainMetrics +type BTCChainClient struct { + *metricsPkg.ChainMetrics chain common.Chain - rpcClient BTCRPCClient - zetaClient ZetaCoreBridger - Tss TSSSigner + rpcClient interfaces.BTCRPCClient + zetaClient interfaces.ZetaCoreBridger + Tss interfaces.TSSSigner lastBlock int64 lastBlockScanned int64 BlockTime uint64 // block time in seconds @@ -67,7 +70,7 @@ type BitcoinChainClient struct { db *gorm.DB stop chan struct{} logger BTCLog - ts *TelemetryServer + ts *metricsPkg.TelemetryServer BlockCache *lru.Cache } @@ -77,14 +80,15 @@ const ( btcBlocksPerDay = 144 // for LRU block cache size bigValueSats = 200000000 // 2 BTC bigValueConfirmationCount = 6 // 6 confirmations for value >= 2 BTC + DonationMessage = "I am rich!" ) -func (ob *BitcoinChainClient) WithZetaClient(bridge *ZetaCoreBridge) { +func (ob *BTCChainClient) WithZetaClient(bridge *zetabridge.ZetaCoreBridge) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.zetaClient = bridge } -func (ob *BitcoinChainClient) WithLogger(logger zerolog.Logger) { +func (ob *BTCChainClient) WithLogger(logger zerolog.Logger) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.logger = BTCLog{ @@ -96,25 +100,25 @@ func (ob *BitcoinChainClient) WithLogger(logger zerolog.Logger) { } } -func (ob *BitcoinChainClient) WithBtcClient(client *rpcclient.Client) { +func (ob *BTCChainClient) WithBtcClient(client *rpcclient.Client) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.rpcClient = client } -func (ob *BitcoinChainClient) WithChain(chain common.Chain) { +func (ob *BTCChainClient) WithChain(chain common.Chain) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.chain = chain } -func (ob *BitcoinChainClient) SetChainParams(params observertypes.ChainParams) { +func (ob *BTCChainClient) SetChainParams(params observertypes.ChainParams) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.params = params } -func (ob *BitcoinChainClient) GetChainParams() observertypes.ChainParams { +func (ob *BTCChainClient) GetChainParams() observertypes.ChainParams { ob.Mu.Lock() defer ob.Mu.Unlock() return ob.params @@ -123,16 +127,16 @@ func (ob *BitcoinChainClient) GetChainParams() observertypes.ChainParams { // NewBitcoinClient returns a new configuration based on supplied target chain func NewBitcoinClient( chain common.Chain, - bridge ZetaCoreBridger, - tss TSSSigner, + bridge interfaces.ZetaCoreBridger, + tss interfaces.TSSSigner, dbpath string, metrics *metricsPkg.Metrics, logger zerolog.Logger, btcCfg config.BTCConfig, - ts *TelemetryServer, -) (*BitcoinChainClient, error) { - ob := BitcoinChainClient{ - ChainMetrics: NewChainMetrics(chain.ChainName.String(), metrics), + ts *metricsPkg.TelemetryServer, +) (*BTCChainClient, error) { + ob := BTCChainClient{ + ChainMetrics: metricsPkg.NewChainMetrics(chain.ChainName.String(), metrics), ts: ts, } ob.stop = make(chan struct{}) @@ -194,7 +198,7 @@ func NewBitcoinClient( return &ob, nil } -func (ob *BitcoinChainClient) Start() { +func (ob *BTCChainClient) Start() { ob.logger.ChainLogger.Info().Msgf("BitcoinChainClient is starting") go ob.WatchInTx() go ob.observeOutTx() @@ -204,7 +208,7 @@ func (ob *BitcoinChainClient) Start() { go ob.RPCStatus() } -func (ob *BitcoinChainClient) RPCStatus() { +func (ob *BTCChainClient) RPCStatus() { ob.logger.ChainLogger.Info().Msgf("RPCStatus is starting") ticker := time.NewTicker(60 * time.Second) @@ -251,20 +255,20 @@ func (ob *BitcoinChainClient) RPCStatus() { } } -func (ob *BitcoinChainClient) Stop() { +func (ob *BTCChainClient) Stop() { ob.logger.ChainLogger.Info().Msgf("ob %s is stopping", ob.chain.String()) close(ob.stop) // this notifies all goroutines to stop ob.logger.ChainLogger.Info().Msgf("%s observer stopped", ob.chain.String()) } -func (ob *BitcoinChainClient) SetLastBlockHeight(height int64) { +func (ob *BTCChainClient) SetLastBlockHeight(height int64) { if height < 0 { panic("lastBlock is negative") } atomic.StoreInt64(&ob.lastBlock, height) } -func (ob *BitcoinChainClient) GetLastBlockHeight() int64 { +func (ob *BTCChainClient) GetLastBlockHeight() int64 { height := atomic.LoadInt64(&ob.lastBlock) if height < 0 { panic("lastBlock is negative") @@ -272,7 +276,7 @@ func (ob *BitcoinChainClient) GetLastBlockHeight() int64 { return height } -func (ob *BitcoinChainClient) SetLastBlockHeightScanned(height int64) { +func (ob *BTCChainClient) SetLastBlockHeightScanned(height int64) { if height < 0 { panic("lastBlockScanned is negative") } @@ -281,7 +285,7 @@ func (ob *BitcoinChainClient) SetLastBlockHeightScanned(height int64) { ob.ts.SetLastScannedBlockNumber((ob.chain.ChainId), uint64(height)) } -func (ob *BitcoinChainClient) GetLastBlockHeightScanned() int64 { +func (ob *BTCChainClient) GetLastBlockHeightScanned() int64 { height := atomic.LoadInt64(&ob.lastBlockScanned) if height < 0 { panic("lastBlockScanned is negative") @@ -289,7 +293,7 @@ func (ob *BitcoinChainClient) GetLastBlockHeightScanned() int64 { return height } -func (ob *BitcoinChainClient) GetPendingNonce() uint64 { +func (ob *BTCChainClient) GetPendingNonce() uint64 { ob.Mu.Lock() defer ob.Mu.Unlock() return ob.pendingNonce @@ -298,12 +302,12 @@ func (ob *BitcoinChainClient) GetPendingNonce() uint64 { // GetBaseGasPrice ... // TODO: implement // https://github.com/zeta-chain/node/issues/868 -func (ob *BitcoinChainClient) GetBaseGasPrice() *big.Int { +func (ob *BTCChainClient) GetBaseGasPrice() *big.Int { return big.NewInt(0) } -func (ob *BitcoinChainClient) WatchInTx() { - ticker, err := NewDynamicTicker("Bitcoin_WatchInTx", ob.GetChainParams().InTxTicker) +func (ob *BTCChainClient) WatchInTx() { + ticker, err := clienttypes.NewDynamicTicker("Bitcoin_WatchInTx", ob.GetChainParams().InTxTicker) if err != nil { ob.logger.WatchInTx.Error().Err(err).Msg("WatchInTx error") return @@ -325,7 +329,7 @@ func (ob *BitcoinChainClient) WatchInTx() { } } -func (ob *BitcoinChainClient) postBlockHeader(tip int64) error { +func (ob *BTCChainClient) postBlockHeader(tip int64) error { ob.logger.WatchInTx.Info().Msgf("postBlockHeader: tip %d", tip) bn := tip res, err := ob.zetaClient.GetBlockHeaderStateByChain(ob.chain.ChainId) @@ -360,7 +364,7 @@ func (ob *BitcoinChainClient) postBlockHeader(tip int64) error { return err } -func (ob *BitcoinChainClient) observeInTx() error { +func (ob *BTCChainClient) observeInTx() error { // make sure inbound TXS / Send is enabled by the protocol flags, err := ob.zetaClient.GetCrosschainFlags() if err != nil { @@ -417,7 +421,7 @@ func (ob *BitcoinChainClient) observeInTx() error { } } - // add block header to zetacore + // add block header to zetabridge if flags.BlockHeaderVerificationFlags != nil && flags.BlockHeaderVerificationFlags.IsBtcTypeChainEnabled { err = ob.postBlockHeader(bn) if err != nil { @@ -435,10 +439,10 @@ func (ob *BitcoinChainClient) observeInTx() error { ob.chain.ChainId, ) - // post inbound vote message to zetacore + // post inbound vote message to zetabridge for _, inTx := range inTxs { msg := ob.GetInboundVoteMessageFromBtcEvent(inTx) - zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundExecutionGasLimit, msg) + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(zetabridge.PostVoteInboundGasLimit, zetabridge.PostVoteInboundExecutionGasLimit, msg) if err != nil { ob.logger.WatchInTx.Error().Err(err).Msgf("observeInTxBTC: error posting to zeta core for tx %s", inTx.TxHash) return err // we have to re-scan this block next time @@ -459,7 +463,7 @@ func (ob *BitcoinChainClient) observeInTx() error { } // ConfirmationsThreshold returns number of required Bitcoin confirmations depending on sent BTC amount. -func (ob *BitcoinChainClient) ConfirmationsThreshold(amount *big.Int) int64 { +func (ob *BTCChainClient) ConfirmationsThreshold(amount *big.Int) int64 { if amount.Cmp(big.NewInt(bigValueSats)) >= 0 { return bigValueConfirmationCount } @@ -471,7 +475,7 @@ func (ob *BitcoinChainClient) ConfirmationsThreshold(amount *big.Int) int64 { } // IsSendOutTxProcessed returns isIncluded(or inMempool), isConfirmed, Error -func (ob *BitcoinChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, _ common.CoinType, logger zerolog.Logger) (bool, bool, error) { +func (ob *BTCChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, _ common.CoinType, logger zerolog.Logger) (bool, bool, error) { outTxID := ob.GetTxID(nonce) logger.Info().Msgf("IsSendOutTxProcessed %s", outTxID) @@ -549,8 +553,8 @@ func (ob *BitcoinChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64 return true, true, nil } -func (ob *BitcoinChainClient) WatchGasPrice() { - ticker, err := NewDynamicTicker("Bitcoin_WatchGasPrice", ob.GetChainParams().GasPriceTicker) +func (ob *BTCChainClient) WatchGasPrice() { + ticker, err := clienttypes.NewDynamicTicker("Bitcoin_WatchGasPrice", ob.GetChainParams().GasPriceTicker) if err != nil { ob.logger.WatchGasPrice.Error().Err(err).Msg("WatchGasPrice error") return @@ -572,7 +576,7 @@ func (ob *BitcoinChainClient) WatchGasPrice() { } } -func (ob *BitcoinChainClient) PostGasPrice() error { +func (ob *BTCChainClient) PostGasPrice() error { if ob.chain.ChainId == 18444 { //bitcoin regtest; hardcode here since this RPC is not available on regtest bn, err := ob.rpcClient.GetBlockCount() if err != nil { @@ -652,13 +656,13 @@ func FilterAndParseIncomingTx( return inTxs } -func (ob *BitcoinChainClient) GetInboundVoteMessageFromBtcEvent(inTx *BTCInTxEvnet) *types.MsgVoteOnObservedInboundTx { +func (ob *BTCChainClient) GetInboundVoteMessageFromBtcEvent(inTx *BTCInTxEvnet) *types.MsgVoteOnObservedInboundTx { ob.logger.WatchInTx.Debug().Msgf("Processing inTx: %s", inTx.TxHash) amount := big.NewFloat(inTx.Value) amount = amount.Mul(amount, big.NewFloat(1e8)) amountInt, _ := amount.Int(nil) message := hex.EncodeToString(inTx.MemoBytes) - return GetInBoundVoteMessage( + return zetabridge.GetInBoundVoteMessage( inTx.FromAddress, ob.chain.ChainId, inTx.FromAddress, @@ -775,8 +779,8 @@ func GetBtcEvent( return nil, nil } -func (ob *BitcoinChainClient) WatchUTXOS() { - ticker, err := NewDynamicTicker("Bitcoin_WatchUTXOS", ob.GetChainParams().WatchUtxoTicker) +func (ob *BTCChainClient) WatchUTXOS() { + ticker, err := clienttypes.NewDynamicTicker("Bitcoin_WatchUTXOS", ob.GetChainParams().WatchUtxoTicker) if err != nil { ob.logger.WatchUTXOS.Error().Err(err).Msg("WatchUTXOS error") return @@ -798,7 +802,7 @@ func (ob *BitcoinChainClient) WatchUTXOS() { } } -func (ob *BitcoinChainClient) FetchUTXOS() error { +func (ob *BTCChainClient) FetchUTXOS() error { defer func() { if err := recover(); err != nil { ob.logger.WatchUTXOS.Error().Msgf("BTC fetchUTXOS: caught panic error: %v", err) @@ -862,7 +866,7 @@ func (ob *BitcoinChainClient) FetchUTXOS() error { // isTssTransaction checks if a given transaction was sent by TSS itself. // An unconfirmed transaction is safe to spend only if it was sent by TSS and verified by ourselves. -func (ob *BitcoinChainClient) isTssTransaction(txid string) bool { +func (ob *BTCChainClient) isTssTransaction(txid string) bool { _, found := ob.includedTxHashes[txid] return found } @@ -870,9 +874,9 @@ func (ob *BitcoinChainClient) isTssTransaction(txid string) bool { // refreshPendingNonce tries increasing the artificial pending nonce of outTx (if lagged behind). // There could be many (unpredictable) reasons for a pending nonce lagging behind, for example: // 1. The zetaclient gets restarted. -// 2. The tracker is missing in zetacore. -func (ob *BitcoinChainClient) refreshPendingNonce() { - // get pending nonces from zetacore +// 2. The tracker is missing in zetabridge. +func (ob *BTCChainClient) refreshPendingNonce() { + // get pending nonces from zetabridge p, err := ob.zetaClient.GetPendingNoncesByChain(ob.chain.ChainId) if err != nil { ob.logger.ChainLogger.Error().Err(err).Msg("refreshPendingNonce: error getting pending nonces") @@ -900,15 +904,15 @@ func (ob *BitcoinChainClient) refreshPendingNonce() { } } -func (ob *BitcoinChainClient) getOutTxidByNonce(nonce uint64, test bool) (string, error) { +func (ob *BTCChainClient) getOutTxidByNonce(nonce uint64, test bool) (string, error) { // There are 2 types of txids an observer can trust // 1. The ones had been verified and saved by observer self. - // 2. The ones had been finalized in zetacore based on majority vote. + // 2. The ones had been finalized in zetabridge based on majority vote. if res := ob.getIncludedTx(nonce); res != nil { return res.TxID, nil } - if !test { // if not unit test, get cctx from zetacore + if !test { // if not unit test, get cctx from zetabridge send, err := ob.zetaClient.GetCctxByNonce(ob.chain.ChainId, nonce) if err != nil { return "", errors.Wrapf(err, "getOutTxidByNonce: error getting cctx for nonce %d", nonce) @@ -930,7 +934,7 @@ func (ob *BitcoinChainClient) getOutTxidByNonce(nonce uint64, test bool) (string return "", fmt.Errorf("getOutTxidByNonce: cannot find outTx txid for nonce %d", nonce) } -func (ob *BitcoinChainClient) findNonceMarkUTXO(nonce uint64, txid string) (int, error) { +func (ob *BTCChainClient) findNonceMarkUTXO(nonce uint64, txid string) (int, error) { tssAddress := ob.Tss.BTCAddressWitnessPubkeyHash().EncodeAddress() amount := common.NonceMarkAmount(nonce) for i, utxo := range ob.utxos { @@ -960,7 +964,7 @@ func (ob *BitcoinChainClient) findNonceMarkUTXO(nonce uint64, txid string) (int, // - the total value of the selected UTXOs. // - the number of consolidated UTXOs. // - the total value of the consolidated UTXOs. -func (ob *BitcoinChainClient) SelectUTXOs(amount float64, utxosToSpend uint16, nonce uint64, consolidateRank uint16, test bool) ([]btcjson.ListUnspentResult, float64, uint16, float64, error) { +func (ob *BTCChainClient) SelectUTXOs(amount float64, utxosToSpend uint16, nonce uint64, consolidateRank uint16, test bool) ([]btcjson.ListUnspentResult, float64, uint16, float64, error) { idx := -1 if nonce == 0 { // for nonce = 0; make exception; no need to include nonce-mark utxo @@ -1033,7 +1037,7 @@ func (ob *BitcoinChainClient) SelectUTXOs(amount float64, utxosToSpend uint16, n } // SaveBroadcastedTx saves successfully broadcasted transaction -func (ob *BitcoinChainClient) SaveBroadcastedTx(txHash string, nonce uint64) { +func (ob *BTCChainClient) SaveBroadcastedTx(txHash string, nonce uint64) { outTxID := ob.GetTxID(nonce) ob.Mu.Lock() ob.broadcastedTx[outTxID] = txHash @@ -1046,7 +1050,7 @@ func (ob *BitcoinChainClient) SaveBroadcastedTx(txHash string, nonce uint64) { ob.logger.ObserveOutTx.Info().Msgf("SaveBroadcastedTx: saved broadcasted txHash %s for outTx %s", txHash, outTxID) } -func (ob *BitcoinChainClient) GetCctxParams(nonce uint64) (types.OutboundTxParams, error) { +func (ob *BTCChainClient) GetCctxParams(nonce uint64) (types.OutboundTxParams, error) { send, err := ob.zetaClient.GetCctxByNonce(ob.chain.ChainId, nonce) if err != nil { return types.OutboundTxParams{}, err @@ -1057,8 +1061,8 @@ func (ob *BitcoinChainClient) GetCctxParams(nonce uint64) (types.OutboundTxParam return *send.GetCurrentOutTxParam(), nil } -func (ob *BitcoinChainClient) observeOutTx() { - ticker, err := NewDynamicTicker("Bitcoin_observeOutTx", ob.GetChainParams().OutTxTicker) +func (ob *BTCChainClient) observeOutTx() { + ticker, err := clienttypes.NewDynamicTicker("Bitcoin_observeOutTx", ob.GetChainParams().OutTxTicker) if err != nil { ob.logger.ObserveOutTx.Error().Err(err).Msg("observeOutTx: error creating ticker") return @@ -1068,7 +1072,7 @@ func (ob *BitcoinChainClient) observeOutTx() { for { select { case <-ticker.C(): - trackers, err := ob.zetaClient.GetAllOutTxTrackerByChain(ob.chain.ChainId, Ascending) + trackers, err := ob.zetaClient.GetAllOutTxTrackerByChain(ob.chain.ChainId, interfaces.Ascending) if err != nil { ob.logger.ObserveOutTx.Error().Err(err).Msg("observeOutTx: error GetAllOutTxTrackerByChain") continue @@ -1121,7 +1125,7 @@ func (ob *BitcoinChainClient) observeOutTx() { // checkIncludedTx checks if a txHash is included and returns (txResult, inMempool) // Note: if txResult is nil, then inMempool flag should be ignored. -func (ob *BitcoinChainClient) checkIncludedTx(txHash string, params types.OutboundTxParams) (*btcjson.GetTransactionResult, bool) { +func (ob *BTCChainClient) checkIncludedTx(txHash string, params types.OutboundTxParams) (*btcjson.GetTransactionResult, bool) { outTxID := ob.GetTxID(params.OutboundTxTssNonce) hash, getTxResult, err := ob.GetTxResultByHash(txHash) if err != nil { @@ -1144,7 +1148,7 @@ func (ob *BitcoinChainClient) checkIncludedTx(txHash string, params types.Outbou } // setIncludedTx saves included tx result in memory -func (ob *BitcoinChainClient) setIncludedTx(nonce uint64, getTxResult *btcjson.GetTransactionResult) { +func (ob *BTCChainClient) setIncludedTx(nonce uint64, getTxResult *btcjson.GetTransactionResult) { txHash := getTxResult.TxID outTxID := ob.GetTxID(nonce) @@ -1172,14 +1176,14 @@ func (ob *BitcoinChainClient) setIncludedTx(nonce uint64, getTxResult *btcjson.G } // getIncludedTx gets the receipt and transaction from memory -func (ob *BitcoinChainClient) getIncludedTx(nonce uint64) *btcjson.GetTransactionResult { +func (ob *BTCChainClient) getIncludedTx(nonce uint64) *btcjson.GetTransactionResult { ob.Mu.Lock() defer ob.Mu.Unlock() return ob.includedTxResults[ob.GetTxID(nonce)] } // removeIncludedTx removes included tx from memory -func (ob *BitcoinChainClient) removeIncludedTx(nonce uint64) { +func (ob *BTCChainClient) removeIncludedTx(nonce uint64) { ob.Mu.Lock() defer ob.Mu.Unlock() txResult, found := ob.includedTxResults[ob.GetTxID(nonce)] @@ -1194,7 +1198,7 @@ func (ob *BitcoinChainClient) removeIncludedTx(nonce uint64) { // - check if all inputs are segwit && TSS inputs // // Returns: true if outTx passes basic checks. -func (ob *BitcoinChainClient) checkTssOutTxResult(hash *chainhash.Hash, res *btcjson.GetTransactionResult, params types.OutboundTxParams, nonce uint64) error { +func (ob *BTCChainClient) checkTssOutTxResult(hash *chainhash.Hash, res *btcjson.GetTransactionResult, params types.OutboundTxParams, nonce uint64) error { rawResult, err := ob.getRawTxResult(hash, res) if err != nil { return errors.Wrapf(err, "checkTssOutTxResult: error GetRawTxResultByHash %s", hash.String()) @@ -1210,7 +1214,7 @@ func (ob *BitcoinChainClient) checkTssOutTxResult(hash *chainhash.Hash, res *btc return nil } -func (ob *BitcoinChainClient) GetTxResultByHash(txID string) (*chainhash.Hash, *btcjson.GetTransactionResult, error) { +func (ob *BTCChainClient) GetTxResultByHash(txID string) (*chainhash.Hash, *btcjson.GetTransactionResult, error) { hash, err := chainhash.NewHashFromStr(txID) if err != nil { return nil, nil, errors.Wrapf(err, "GetTxResultByHash: error NewHashFromStr: %s", txID) @@ -1224,7 +1228,7 @@ func (ob *BitcoinChainClient) GetTxResultByHash(txID string) (*chainhash.Hash, * return hash, txResult, nil } -func (ob *BitcoinChainClient) getRawTxResult(hash *chainhash.Hash, res *btcjson.GetTransactionResult) (btcjson.TxRawResult, error) { +func (ob *BTCChainClient) getRawTxResult(hash *chainhash.Hash, res *btcjson.GetTransactionResult) (btcjson.TxRawResult, error) { if res.Confirmations == 0 { // for pending tx, we query the raw tx directly rawResult, err := ob.rpcClient.GetRawTransactionVerbose(hash) // for pending tx, we query the raw tx if err != nil { @@ -1253,7 +1257,7 @@ func (ob *BitcoinChainClient) getRawTxResult(hash *chainhash.Hash, res *btcjson. // checkTSSVin checks vin is valid if: // - The first input is the nonce-mark // - All inputs are from TSS address -func (ob *BitcoinChainClient) checkTSSVin(vins []btcjson.Vin, nonce uint64) error { +func (ob *BTCChainClient) checkTSSVin(vins []btcjson.Vin, nonce uint64) error { // vins: [nonce-mark, UTXO1, UTXO2, ...] if nonce > 0 && len(vins) <= 1 { return fmt.Errorf("checkTSSVin: len(vins) <= 1") @@ -1286,7 +1290,7 @@ func (ob *BitcoinChainClient) checkTSSVin(vins []btcjson.Vin, nonce uint64) erro // - The first output is the nonce-mark // - The second output is the correct payment to recipient // - The third output is the change to TSS (optional) -func (ob *BitcoinChainClient) checkTSSVout(vouts []btcjson.Vout, params types.OutboundTxParams, nonce uint64) error { +func (ob *BTCChainClient) checkTSSVout(vouts []btcjson.Vout, params types.OutboundTxParams, nonce uint64) error { // vouts: [nonce-mark, payment to recipient, change to TSS (optional)] if !(len(vouts) == 2 || len(vouts) == 3) { return fmt.Errorf("checkTSSVout: invalid number of vouts: %d", len(vouts)) @@ -1346,7 +1350,7 @@ func (ob *BitcoinChainClient) checkTSSVout(vouts []btcjson.Vout, params types.Ou return nil } -func (ob *BitcoinChainClient) BuildBroadcastedTxMap() error { +func (ob *BTCChainClient) BuildBroadcastedTxMap() error { var broadcastedTransactions []clienttypes.OutTxHashSQLType if err := ob.db.Find(&broadcastedTransactions).Error; err != nil { ob.logger.ChainLogger.Error().Err(err).Msg("error iterating over db") @@ -1358,7 +1362,7 @@ func (ob *BitcoinChainClient) BuildBroadcastedTxMap() error { return nil } -func (ob *BitcoinChainClient) LoadLastBlock() error { +func (ob *BTCChainClient) LoadLastBlock() error { bn, err := ob.rpcClient.GetBlockCount() if err != nil { return err @@ -1389,7 +1393,7 @@ func (ob *BitcoinChainClient) LoadLastBlock() error { return nil } -func (ob *BitcoinChainClient) loadDB(dbpath string) error { +func (ob *BTCChainClient) loadDB(dbpath string) error { if _, err := os.Stat(dbpath); os.IsNotExist(err) { err := os.MkdirAll(dbpath, os.ModePerm) if err != nil { @@ -1422,7 +1426,7 @@ func (ob *BitcoinChainClient) loadDB(dbpath string) error { return err } -func (ob *BitcoinChainClient) GetTxID(nonce uint64) string { +func (ob *BTCChainClient) GetTxID(nonce uint64) string { tssAddr := ob.Tss.BTCAddress() return fmt.Sprintf("%d-%s-%d", ob.chain.ChainId, tssAddr, nonce) } @@ -1432,7 +1436,7 @@ type BTCBlockNHeader struct { Block *btcjson.GetBlockVerboseTxResult } -func (ob *BitcoinChainClient) GetBlockByNumberCached(blockNumber int64) (*BTCBlockNHeader, error) { +func (ob *BTCChainClient) GetBlockByNumberCached(blockNumber int64) (*BTCBlockNHeader, error) { if result, ok := ob.BlockCache.Get(blockNumber); ok { return result.(*BTCBlockNHeader), nil } diff --git a/zetaclient/bitcoin_client_db_test.go b/zetaclient/bitcoin/bitcoin_client_db_test.go similarity index 89% rename from zetaclient/bitcoin_client_db_test.go rename to zetaclient/bitcoin/bitcoin_client_db_test.go index 22690bdac7..2f4a58943f 100644 --- a/zetaclient/bitcoin_client_db_test.go +++ b/zetaclient/bitcoin/bitcoin_client_db_test.go @@ -1,4 +1,4 @@ -package zetaclient +package bitcoin import ( "strconv" @@ -11,6 +11,9 @@ import ( "gorm.io/gorm" ) +const tempSQLiteDbPath = "file::memory:?cache=shared" +const numOfEntries = 2 + type BitcoinClientDBTestSuite struct { suite.Suite db *gorm.DB @@ -24,7 +27,7 @@ func TestBitcoinClientDB(t *testing.T) { func (suite *BitcoinClientDBTestSuite) SetupTest() { suite.submittedTx = map[string]btcjson.GetTransactionResult{} - db, err := gorm.Open(sqlite.Open(TempSQLiteDbPath), &gorm.Config{}) + db, err := gorm.Open(sqlite.Open(tempSQLiteDbPath), &gorm.Config{}) suite.NoError(err) suite.db = db @@ -33,7 +36,7 @@ func (suite *BitcoinClientDBTestSuite) SetupTest() { suite.NoError(err) //Create some Transaction entries in the DB - for i := 0; i < NumOfEntries; i++ { + for i := 0; i < numOfEntries; i++ { txResult := btcjson.GetTransactionResult{ Amount: float64(i), Fee: 0, diff --git a/zetaclient/bitcoin_client_rpc_test.go b/zetaclient/bitcoin/bitcoin_client_rpc_test.go similarity index 98% rename from zetaclient/bitcoin_client_rpc_test.go rename to zetaclient/bitcoin/bitcoin_client_rpc_test.go index 5bb5091259..3c31d2d973 100644 --- a/zetaclient/bitcoin_client_rpc_test.go +++ b/zetaclient/bitcoin/bitcoin_client_rpc_test.go @@ -1,4 +1,4 @@ -package zetaclient +package bitcoin import ( "encoding/hex" @@ -8,6 +8,8 @@ import ( "testing" "time" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/rpcclient" @@ -21,7 +23,7 @@ import ( type BitcoinClientTestSuite struct { suite.Suite - BitcoinChainClient *BitcoinChainClient + BitcoinChainClient *BTCChainClient } func (suite *BitcoinClientTestSuite) SetupTest() { @@ -34,7 +36,7 @@ func (suite *BitcoinClientTestSuite) SetupTest() { pkBytes := crypto.FromECDSAPub(&privateKey.PublicKey) suite.T().Logf("pubkey: %d", len(pkBytes)) - tss := TestSigner{ + tss := interfaces.TestSigner{ PrivKey: privateKey, } //client, err := NewBitcoinClient(common.BtcTestNetChain(), nil, tss, "", nil) diff --git a/zetaclient/bitcoin_client_test.go b/zetaclient/bitcoin/bitcoin_client_test.go similarity index 93% rename from zetaclient/bitcoin_client_test.go rename to zetaclient/bitcoin/bitcoin_client_test.go index 84d69c4a2c..42dfbeb288 100644 --- a/zetaclient/bitcoin_client_test.go +++ b/zetaclient/bitcoin/bitcoin_client_test.go @@ -1,4 +1,4 @@ -package zetaclient +package bitcoin import ( "math/big" @@ -10,7 +10,7 @@ import ( ) func TestConfirmationThreshold(t *testing.T) { - client := &BitcoinChainClient{Mu: &sync.Mutex{}} + client := &BTCChainClient{Mu: &sync.Mutex{}} t.Run("should return confirmations in chain param", func(t *testing.T) { client.SetChainParams(observertypes.ChainParams{ConfirmationCount: 3}) require.Equal(t, int64(3), client.ConfirmationsThreshold(big.NewInt(1000))) diff --git a/zetaclient/btc_signer.go b/zetaclient/bitcoin/bitcoin_signer.go similarity index 92% rename from zetaclient/btc_signer.go rename to zetaclient/bitcoin/bitcoin_signer.go index 245f4039ff..6f4ade60f1 100644 --- a/zetaclient/btc_signer.go +++ b/zetaclient/bitcoin/bitcoin_signer.go @@ -1,4 +1,4 @@ -package zetaclient +package bitcoin import ( "bytes" @@ -8,6 +8,11 @@ import ( "math/rand" "time" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/metrics" + "github.com/zeta-chain/zetacore/zetaclient/outtxprocessor" + "github.com/zeta-chain/zetacore/zetaclient/tss" + "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/rpcclient" @@ -29,15 +34,15 @@ const ( ) type BTCSigner struct { - tssSigner TSSSigner - rpcClient BTCRPCClient + tssSigner interfaces.TSSSigner + rpcClient interfaces.BTCRPCClient logger zerolog.Logger - ts *TelemetryServer + ts *metrics.TelemetryServer } -var _ ChainSigner = &BTCSigner{} +var _ interfaces.ChainSigner = &BTCSigner{} -func NewBTCSigner(cfg config.BTCConfig, tssSigner TSSSigner, logger zerolog.Logger, ts *TelemetryServer) (*BTCSigner, error) { +func NewBTCSigner(cfg config.BTCConfig, tssSigner interfaces.TSSSigner, logger zerolog.Logger, ts *metrics.TelemetryServer) (*BTCSigner, error) { connCfg := &rpcclient.ConnConfig{ Host: cfg.RPCHost, User: cfg.RPCUsername, @@ -67,7 +72,7 @@ func (signer *BTCSigner) SignWithdrawTx( amount float64, gasPrice *big.Int, sizeLimit uint64, - btcClient *BitcoinChainClient, + btcClient *BTCChainClient, height uint64, nonce uint64, chain *common.Chain, @@ -127,7 +132,7 @@ func (signer *BTCSigner) SignWithdrawTx( // calculate remaining btc to TSS self tssAddrWPKH := signer.tssSigner.BTCAddressWitnessPubkeyHash() - payToSelf, err := payToWitnessPubKeyHashScript(tssAddrWPKH.WitnessProgram()) + payToSelf, err := PayToWitnessPubKeyHashScript(tssAddrWPKH.WitnessProgram()) if err != nil { return nil, err } @@ -150,7 +155,7 @@ func (signer *BTCSigner) SignWithdrawTx( tx.AddTxOut(txOut1) // 2nd output: the payment to the recipient - pkScript, err := payToWitnessPubKeyHashScript(to.WitnessProgram()) + pkScript, err := PayToWitnessPubKeyHashScript(to.WitnessProgram()) if err != nil { return nil, err } @@ -180,7 +185,7 @@ func (signer *BTCSigner) SignWithdrawTx( return nil, err } } - tss, ok := signer.tssSigner.(*TSS) + tss, ok := signer.tssSigner.(*tss.TSS) if !ok { return nil, fmt.Errorf("tssSigner is not a TSS") } @@ -227,10 +232,10 @@ func (signer *BTCSigner) Broadcast(signedTx *wire.MsgTx) error { func (signer *BTCSigner) TryProcessOutTx( cctx *types.CrossChainTx, - outTxMan *OutTxProcessorManager, + outTxMan *outtxprocessor.Processor, outTxID string, - chainclient ChainClient, - zetaBridge ZetaCoreBridger, + chainclient interfaces.ChainClient, + zetaBridge interfaces.ZetaCoreBridger, height uint64, ) { defer func() { @@ -252,7 +257,7 @@ func (signer *BTCSigner) TryProcessOutTx( } logger.Info().Msgf("BTC TryProcessOutTx: %s, value %d to %s", cctx.Index, params.Amount.BigInt(), params.Receiver) - btcClient, ok := chainclient.(*BitcoinChainClient) + btcClient, ok := chainclient.(*BTCChainClient) if !ok { logger.Error().Msgf("chain client is not a bitcoin client") return diff --git a/zetaclient/btc_signer_test.go b/zetaclient/bitcoin/bitcoin_signer_test.go similarity index 98% rename from zetaclient/btc_signer_test.go rename to zetaclient/bitcoin/bitcoin_signer_test.go index 348bd8a4d5..ae1d3a2f1a 100644 --- a/zetaclient/btc_signer_test.go +++ b/zetaclient/bitcoin/bitcoin_signer_test.go @@ -1,4 +1,4 @@ -package zetaclient +package bitcoin import ( "encoding/hex" @@ -9,6 +9,9 @@ import ( "sync" "testing" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/metrics" + "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcjson" @@ -70,10 +73,10 @@ func (s *BTCSignerSuite) SetUpTest(c *C) { //privkeyBytes := crypto.FromECDSA(privateKey) //c.Logf("privatekey %s", hex.EncodeToString(privkeyBytes)) c.Assert(err, IsNil) - tss := TestSigner{ + tss := interfaces.TestSigner{ PrivKey: privateKey, } - s.btcSigner, err = NewBTCSigner(config.BTCConfig{}, &tss, zerolog.Logger{}, &TelemetryServer{}) + s.btcSigner, err = NewBTCSigner(config.BTCConfig{}, &tss, zerolog.Logger{}, &metrics.TelemetryServer{}) c.Assert(err, IsNil) } @@ -189,7 +192,7 @@ func (s *BTCSignerSuite) TestP2WPH(c *C) { txOut = wire.NewTxOut(0, nil) redeemTx.AddTxOut(txOut) txSigHashes := txscript.NewTxSigHashes(redeemTx) - pkScript, err = payToWitnessPubKeyHashScript(addr.WitnessProgram()) + pkScript, err = PayToWitnessPubKeyHashScript(addr.WitnessProgram()) c.Assert(err, IsNil) { @@ -238,7 +241,7 @@ func generateKeyPair(t *testing.T, net *chaincfg.Params) (*btcec.PrivateKey, []b addr, err := btcutil.NewAddressWitnessPubKeyHash(pubKeyHash, net) require.Nil(t, err) //fmt.Printf("New address: %s\n", addr.EncodeAddress()) - pkScript, err := payToWitnessPubKeyHashScript(addr.WitnessProgram()) + pkScript, err := PayToWitnessPubKeyHashScript(addr.WitnessProgram()) require.Nil(t, err) return privateKey, pkScript } @@ -418,17 +421,17 @@ func TestP2WPHSizeBreakdown(t *testing.T) { } // helper function to create a new BitcoinChainClient -func createTestClient(t *testing.T) *BitcoinChainClient { +func createTestClient(t *testing.T) *BTCChainClient { skHex := "7b8507ba117e069f4a3f456f505276084f8c92aee86ac78ae37b4d1801d35fa8" privateKey, err := crypto.HexToECDSA(skHex) require.Nil(t, err) - tss := TestSigner{ + tss := interfaces.TestSigner{ PrivKey: privateKey, } tssAddress := tss.BTCAddressWitnessPubkeyHash().EncodeAddress() // Create BitcoinChainClient - client := &BitcoinChainClient{ + client := &BTCChainClient{ Tss: tss, Mu: &sync.Mutex{}, includedTxResults: make(map[string]*btcjson.GetTransactionResult), @@ -443,7 +446,7 @@ func createTestClient(t *testing.T) *BitcoinChainClient { return client } -func mineTxNSetNonceMark(ob *BitcoinChainClient, nonce uint64, txid string, preMarkIndex int) { +func mineTxNSetNonceMark(ob *BTCChainClient, nonce uint64, txid string, preMarkIndex int) { // Mine transaction outTxID := ob.GetTxID(nonce) ob.includedTxResults[outTxID] = &btcjson.GetTransactionResult{TxID: txid} diff --git a/zetaclient/btc_test.go b/zetaclient/bitcoin/bitcoin_test.go similarity index 90% rename from zetaclient/btc_test.go rename to zetaclient/bitcoin/bitcoin_test.go index 16f60c25db..de73756be4 100644 --- a/zetaclient/btc_test.go +++ b/zetaclient/bitcoin/bitcoin_test.go @@ -1,4 +1,4 @@ -package zetaclient +package bitcoin import ( "bytes" @@ -7,6 +7,8 @@ import ( "math/big" "testing" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -21,7 +23,7 @@ import ( type BTCSignTestSuite struct { suite.Suite - testSigner *TestSigner + testSigner *interfaces.TestSigner db *gorm.DB } @@ -41,13 +43,13 @@ func (suite *BTCSignTestSuite) SetupTest() { //suite.T().Logf("pubkey: %d", len(pkBytes)) //suite.Require().NoError(err) - suite.testSigner = &TestSigner{ // fake TSS + suite.testSigner = &interfaces.TestSigner{ // fake TSS PrivKey: privateKey.ToECDSA(), } addr := suite.testSigner.BTCAddressWitnessPubkeyHash() suite.T().Logf("segwit addr: %s", addr) - db, err := gorm.Open(sqlite.Open(TempSQLiteDbPath), &gorm.Config{}) + db, err := gorm.Open(sqlite.Open(tempSQLiteDbPath), &gorm.Config{}) suite.NoError(err) suite.db = db @@ -107,7 +109,7 @@ func buildTX() (*wire.MsgTx, *txscript.TxSigHashes, int, int64, []byte, *btcec.P txIn := wire.NewTxIn(outpoint, nil, nil) tx.AddTxIn(txIn) - pkScript, err := payToWitnessPubKeyHashScript(addr.WitnessProgram()) + pkScript, err := PayToWitnessPubKeyHashScript(addr.WitnessProgram()) if err != nil { return nil, nil, 0, 0, nil, nil, false, err } @@ -137,7 +139,7 @@ func getWalletTX(tx *wire.MsgTx, sigHashes *txscript.TxSigHashes, idx int, amt i return walletTx, nil } -func getTSSTX(tss *TestSigner, tx *wire.MsgTx, sigHashes *txscript.TxSigHashes, idx int, amt int64, subscript []byte, hashType txscript.SigHashType) (string, error) { +func getTSSTX(tss *interfaces.TestSigner, tx *wire.MsgTx, sigHashes *txscript.TxSigHashes, idx int, amt int64, subscript []byte, hashType txscript.SigHashType) (string, error) { witnessHash, err := txscript.CalcWitnessSigHash(subscript, sigHashes, txscript.SigHashAll, tx, idx, amt) if err != nil { return "", err diff --git a/zetaclient/bitcoin/inbound_tracker.go b/zetaclient/bitcoin/inbound_tracker.go new file mode 100644 index 0000000000..8792f0c549 --- /dev/null +++ b/zetaclient/bitcoin/inbound_tracker.go @@ -0,0 +1,92 @@ +package bitcoin + +import ( + "errors" + + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/zeta-chain/zetacore/common" + "github.com/zeta-chain/zetacore/zetaclient/types" + "github.com/zeta-chain/zetacore/zetaclient/zetabridge" +) + +func (ob *BTCChainClient) ExternalChainWatcherForNewInboundTrackerSuggestions() { + ticker, err := types.NewDynamicTicker("Bitcoin_WatchInTx_InboundTrackerSuggestions", ob.GetChainParams().InTxTicker) + if err != nil { + ob.logger.WatchInTx.Err(err).Msg("error creating ticker") + return + } + + defer ticker.Stop() + for { + select { + case <-ticker.C(): + err := ob.ObserveTrackerSuggestions() + if err != nil { + ob.logger.WatchInTx.Error().Err(err).Msg("error observing in tx") + } + ticker.UpdateInterval(ob.GetChainParams().InTxTicker, ob.logger.WatchInTx) + case <-ob.stop: + ob.logger.WatchInTx.Info().Msg("ExternalChainWatcher for BTC inboundTrackerSuggestions stopped") + return + } + } +} + +func (ob *BTCChainClient) ObserveTrackerSuggestions() error { + trackers, err := ob.zetaClient.GetInboundTrackersForChain(ob.chain.ChainId) + if err != nil { + return err + } + for _, tracker := range trackers { + ob.logger.WatchInTx.Info().Msgf("checking tracker with hash :%s and coin-type :%s ", tracker.TxHash, tracker.CoinType) + ballotIdentifier, err := ob.CheckReceiptForBtcTxHash(tracker.TxHash, true) + if err != nil { + return err + } + ob.logger.WatchInTx.Info().Msgf("Vote submitted for inbound Tracker,Chain : %s,Ballot Identifier : %s, coin-type %s", ob.chain.ChainName, ballotIdentifier, common.CoinType_Gas.String()) + } + return nil +} + +func (ob *BTCChainClient) CheckReceiptForBtcTxHash(txHash string, vote bool) (string, error) { + hash, err := chainhash.NewHashFromStr(txHash) + if err != nil { + return "", err + } + tx, err := ob.rpcClient.GetRawTransactionVerbose(hash) + if err != nil { + return "", err + } + blockHash, err := chainhash.NewHashFromStr(tx.BlockHash) + if err != nil { + return "", err + } + block, err := ob.rpcClient.GetBlockVerbose(blockHash) + if err != nil { + return "", err + } + tss, err := ob.zetaClient.GetBtcTssAddress(ob.chain.ChainId) + if err != nil { + return "", err + } + // #nosec G701 always positive + event, err := GetBtcEvent(*tx, tss, uint64(block.Height), &ob.logger.WatchInTx, ob.chain.ChainId) + if err != nil { + return "", err + } + if event == nil { + return "", errors.New("no btc deposit event found") + } + msg := ob.GetInboundVoteMessageFromBtcEvent(event) + if !vote { + return msg.Digest(), nil + } + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(zetabridge.PostVoteInboundGasLimit, zetabridge.PostVoteInboundExecutionGasLimit, msg) + if err != nil { + ob.logger.WatchInTx.Error().Err(err).Msg("error posting to zeta core") + return "", err + } else if zetaHash != "" { + ob.logger.WatchInTx.Info().Msgf("BTC deposit detected and reported: PostVoteInbound zeta tx: %s ballot %s", zetaHash, ballot) + } + return msg.Digest(), nil +} diff --git a/zetaclient/bitcoin/utils.go b/zetaclient/bitcoin/utils.go new file mode 100644 index 0000000000..d6bea41cd6 --- /dev/null +++ b/zetaclient/bitcoin/utils.go @@ -0,0 +1,129 @@ +package bitcoin + +import ( + "encoding/json" + "math" + "math/big" + + "github.com/btcsuite/btcd/blockchain" + + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/pkg/errors" +) + +const ( + satoshiPerBitcoin = 1e8 + bytesPerKB = 1000 + bytesPerInput = 41 // each input is about 41 bytes + bytesPerOutput = 31 // each output is about 31 bytes + bytes1stWitness = 110 // the 1st witness incurs about 110 bytes and it may vary + bytesPerWitness = 108 // each additional witness incurs about 108 bytes and it may vary +) + +var ( + BtcOutTxBytesDepositor uint64 + BtcOutTxBytesWithdrawer uint64 + BtcDepositorFeeMin float64 +) + +func init() { + BtcOutTxBytesDepositor = SegWitTxSizeDepositor() // 68vB, the outtx size incurred by the depositor + BtcOutTxBytesWithdrawer = SegWitTxSizeWithdrawer() // 171vB, the outtx size incurred by the withdrawer + + // depositor fee calculation is based on a fixed fee rate of 20 sat/byte just for simplicity. + // In reality, the fee rate on UTXO deposit is different from the fee rate when the UTXO is spent. + BtcDepositorFeeMin = DepositorFee(20) // 0.00001360 (20 * 68vB / 100000000), the minimum deposit fee in BTC for 20 sat/byte +} + +func PrettyPrintStruct(val interface{}) (string, error) { + prettyStruct, err := json.MarshalIndent( + val, + "", + " ", + ) + if err != nil { + return "", err + } + return string(prettyStruct), nil +} + +// FeeRateToSatPerByte converts a fee rate in BTC/KB to sat/byte. +func FeeRateToSatPerByte(rate float64) *big.Int { + // #nosec G701 always in range + satPerKB := new(big.Int).SetInt64(int64(rate * satoshiPerBitcoin)) + return new(big.Int).Div(satPerKB, big.NewInt(bytesPerKB)) +} + +// WiredTxSize calculates the wired tx size in bytes +func WiredTxSize(numInputs uint64, numOutputs uint64) uint64 { + // Version 4 bytes + LockTime 4 bytes + Serialized varint size for the + // number of transaction inputs and outputs. + // #nosec G701 always positive + return uint64(8 + wire.VarIntSerializeSize(numInputs) + wire.VarIntSerializeSize(numOutputs)) +} + +// EstimateSegWitTxSize estimates SegWit tx size +func EstimateSegWitTxSize(numInputs uint64, numOutputs uint64) uint64 { + if numInputs == 0 { + return 0 + } + bytesWiredTx := WiredTxSize(numInputs, numOutputs) + bytesInput := numInputs * bytesPerInput + bytesOutput := numOutputs * bytesPerOutput + bytesWitness := bytes1stWitness + (numInputs-1)*bytesPerWitness + // https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-size-calculations + // Calculation for signed SegWit tx: blockchain.GetTransactionWeight(tx) / 4 + return bytesWiredTx + bytesInput + bytesOutput + bytesWitness/blockchain.WitnessScaleFactor +} + +// SegWitTxSizeDepositor returns SegWit tx size (68vB) incurred by the depositor +func SegWitTxSizeDepositor() uint64 { + return bytesPerInput + bytesPerWitness/blockchain.WitnessScaleFactor +} + +// SegWitTxSizeWithdrawer returns SegWit tx size (171vB) incurred by the withdrawer (1 input, 3 outputs) +func SegWitTxSizeWithdrawer() uint64 { + bytesWiredTx := WiredTxSize(1, 3) + bytesInput := uint64(1) * bytesPerInput // nonce mark + bytesOutput := uint64(3) * bytesPerOutput // 3 outputs: new nonce mark, payment, change + return bytesWiredTx + bytesInput + bytesOutput + bytes1stWitness/blockchain.WitnessScaleFactor +} + +// DepositorFee calculates the depositor fee in BTC for a given sat/byte fee rate +// Note: the depositor fee is charged in order to cover the cost of spending the deposited UTXO in the future +func DepositorFee(satPerByte int64) float64 { + return float64(satPerByte) * float64(BtcOutTxBytesDepositor) / satoshiPerBitcoin +} + +func GetSatoshis(btc float64) (int64, error) { + // The amount is only considered invalid if it cannot be represented + // as an integer type. This may happen if f is NaN or +-Infinity. + // BTC max amount is 21 mil and its at least 0 (Note: bitcoin allows creating 0-value outputs) + switch { + case math.IsNaN(btc): + fallthrough + case math.IsInf(btc, 1): + fallthrough + case math.IsInf(btc, -1): + return 0, errors.New("invalid bitcoin amount") + case btc > 21000000.0: + return 0, errors.New("exceeded max bitcoin amount") + case btc < 0.0: + return 0, errors.New("cannot be less than zero") + } + return round(btc * satoshiPerBitcoin), nil +} + +func round(f float64) int64 { + if f < 0 { + // #nosec G701 always in range + return int64(f - 0.5) + } + // #nosec G701 always in range + return int64(f + 0.5) +} + +func PayToWitnessPubKeyHashScript(pubKeyHash []byte) ([]byte, error) { + return txscript.NewScriptBuilder().AddOp(txscript.OP_0).AddData(pubKeyHash).Script() +} diff --git a/zetaclient/config/types.go b/zetaclient/config/types.go index 54ebccfce9..17467affd1 100644 --- a/zetaclient/config/types.go +++ b/zetaclient/config/types.go @@ -170,7 +170,7 @@ func (c *Config) UpdateChainParams( c.cfgLock.Lock() defer c.cfgLock.Unlock() - // Ignore whatever order zetacore organizes chain list in state + // Ignore whatever order zetabridge organizes chain list in state sort.SliceStable(newChains, func(i, j int) bool { return newChains[i].ChainId < newChains[j].ChainId }) diff --git a/zetaclient/errors.go b/zetaclient/errors/errors.go similarity index 89% rename from zetaclient/errors.go rename to zetaclient/errors/errors.go index 3c8b579301..3e5ff0a1e5 100644 --- a/zetaclient/errors.go +++ b/zetaclient/errors/errors.go @@ -1,4 +1,4 @@ -package zetaclient +package errors import ( "errors" diff --git a/zetaclient/evm_client.go b/zetaclient/evm/evm_client.go similarity index 89% rename from zetaclient/evm_client.go rename to zetaclient/evm/evm_client.go index a4cf459c8b..97b29495f6 100644 --- a/zetaclient/evm_client.go +++ b/zetaclient/evm/evm_client.go @@ -1,4 +1,4 @@ -package zetaclient +package evm import ( "bytes" @@ -14,6 +14,9 @@ import ( "sync/atomic" "time" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/zetabridge" + "github.com/ethereum/go-ethereum" "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zeta.non-eth.sol" zetaconnectoreth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.eth.sol" @@ -50,7 +53,7 @@ type OutTx struct { TxHash string Nonce int64 } -type EVMLog struct { +type Log struct { ChainLogger zerolog.Logger // Parent logger ExternalChainWatcher zerolog.Logger // Observes external Chains for incoming trasnactions WatchGasPrice zerolog.Logger // Observes external Chains for Gas prices and posts to core @@ -67,16 +70,15 @@ const ( TopicsDeposited = 2 // [signature, asset] https://github.com/zeta-chain/protocol-contracts/blob/d65814debf17648a6c67d757ba03646415842790/contracts/evm/ERC20Custody.sol#L42 ) -// EVMChainClient represents the chain configuration for an EVM chain +// ChainClient represents the chain configuration for an EVM chain // Filled with above constants depending on chain -type EVMChainClient struct { - *ChainMetrics +type ChainClient struct { + *metricsPkg.ChainMetrics chain common.Chain - evmClient EVMRPCClient + evmClient interfaces.EVMRPCClient + zetaClient interfaces.ZetaCoreBridger + Tss interfaces.TSSSigner evmClientAlternate *ethrpc.EthRPC // a fallback rpc client - KlaytnClient KlaytnRPCClient - zetaClient ZetaCoreBridger - Tss TSSSigner lastBlockScanned uint64 lastBlock uint64 BlockTimeExternalChain uint64 // block time in seconds @@ -91,35 +93,35 @@ type EVMChainClient struct { OutTxChan chan OutTx // send to this channel if you want something back! stop chan struct{} fileLogger *zerolog.Logger // for critical info - logger EVMLog + logger Log cfg *config.Config params observertypes.ChainParams - ts *TelemetryServer + ts *metricsPkg.TelemetryServer blockCache *lru.Cache blockCacheV3 *lru.Cache // blockCacheV3 caches blocks containing type-3 (BlobTxType) transactions headerCache *lru.Cache } -var _ ChainClient = (*EVMChainClient)(nil) +var _ interfaces.ChainClient = (*ChainClient)(nil) // NewEVMChainClient returns a new configuration based on supplied target chain func NewEVMChainClient( - bridge ZetaCoreBridger, - tss TSSSigner, + bridge interfaces.ZetaCoreBridger, + tss interfaces.TSSSigner, dbpath string, metrics *metricsPkg.Metrics, logger zerolog.Logger, cfg *config.Config, evmCfg config.EVMConfig, - ts *TelemetryServer, -) (*EVMChainClient, error) { - ob := EVMChainClient{ - ChainMetrics: NewChainMetrics(evmCfg.Chain.ChainName.String(), metrics), + ts *metricsPkg.TelemetryServer, +) (*ChainClient, error) { + ob := ChainClient{ + ChainMetrics: metricsPkg.NewChainMetrics(evmCfg.Chain.ChainName.String(), metrics), ts: ts, } chainLogger := logger.With().Str("chain", evmCfg.Chain.ChainName.String()).Logger() - ob.logger = EVMLog{ + ob.logger = Log{ ChainLogger: chainLogger, ExternalChainWatcher: chainLogger.With().Str("module", "ExternalChainWatcher").Logger(), WatchGasPrice: chainLogger.With().Str("module", "WatchGasPrice").Logger(), @@ -171,15 +173,6 @@ func NewEVMChainClient( return nil, err } - if ob.chain.IsKlaytnChain() { - client, err := Dial(evmCfg.Endpoint) - if err != nil { - ob.logger.ChainLogger.Err(err).Msg("klaytn Client Dial") - return nil, err - } - ob.KlaytnClient = client - } - // create metric counters err = ob.RegisterPromCounter("rpc_getFilterLogs_count", "Number of getLogs") if err != nil { @@ -203,15 +196,15 @@ func NewEVMChainClient( return &ob, nil } -func (ob *EVMChainClient) WithChain(chain common.Chain) { +func (ob *ChainClient) WithChain(chain common.Chain) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.chain = chain } -func (ob *EVMChainClient) WithLogger(logger zerolog.Logger) { +func (ob *ChainClient) WithLogger(logger zerolog.Logger) { ob.Mu.Lock() defer ob.Mu.Unlock() - ob.logger = EVMLog{ + ob.logger = Log{ ChainLogger: logger, ExternalChainWatcher: logger.With().Str("module", "ExternalChainWatcher").Logger(), WatchGasPrice: logger.With().Str("module", "WatchGasPrice").Logger(), @@ -219,83 +212,83 @@ func (ob *EVMChainClient) WithLogger(logger zerolog.Logger) { } } -func (ob *EVMChainClient) WithEvmClient(client *ethclient.Client) { +func (ob *ChainClient) WithEvmClient(client *ethclient.Client) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.evmClient = client } -func (ob *EVMChainClient) WithZetaClient(bridge *ZetaCoreBridge) { +func (ob *ChainClient) WithZetaClient(bridge *zetabridge.ZetaCoreBridge) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.zetaClient = bridge } -func (ob *EVMChainClient) WithParams(params observertypes.ChainParams) { +func (ob *ChainClient) WithParams(params observertypes.ChainParams) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.params = params } -func (ob *EVMChainClient) SetConfig(cfg *config.Config) { +func (ob *ChainClient) SetConfig(cfg *config.Config) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.cfg = cfg } -func (ob *EVMChainClient) SetChainParams(params observertypes.ChainParams) { +func (ob *ChainClient) SetChainParams(params observertypes.ChainParams) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.params = params } -func (ob *EVMChainClient) GetChainParams() observertypes.ChainParams { +func (ob *ChainClient) GetChainParams() observertypes.ChainParams { ob.Mu.Lock() defer ob.Mu.Unlock() return ob.params } -func (ob *EVMChainClient) GetConnectorContract() (ethcommon.Address, *zetaconnector.ZetaConnectorNonEth, error) { +func (ob *ChainClient) GetConnectorContract() (ethcommon.Address, *zetaconnector.ZetaConnectorNonEth, error) { addr := ethcommon.HexToAddress(ob.GetChainParams().ConnectorContractAddress) contract, err := FetchConnectorContract(addr, ob.evmClient) return addr, contract, err } -func (ob *EVMChainClient) GetConnectorContractEth() (ethcommon.Address, *zetaconnectoreth.ZetaConnectorEth, error) { +func (ob *ChainClient) GetConnectorContractEth() (ethcommon.Address, *zetaconnectoreth.ZetaConnectorEth, error) { addr := ethcommon.HexToAddress(ob.GetChainParams().ConnectorContractAddress) contract, err := FetchConnectorContractEth(addr, ob.evmClient) return addr, contract, err } -func (ob *EVMChainClient) GetZetaTokenNonEthContract() (ethcommon.Address, *zeta.ZetaNonEth, error) { +func (ob *ChainClient) GetZetaTokenNonEthContract() (ethcommon.Address, *zeta.ZetaNonEth, error) { addr := ethcommon.HexToAddress(ob.GetChainParams().ZetaTokenContractAddress) contract, err := FetchZetaZetaNonEthTokenContract(addr, ob.evmClient) return addr, contract, err } -func (ob *EVMChainClient) GetERC20CustodyContract() (ethcommon.Address, *erc20custody.ERC20Custody, error) { +func (ob *ChainClient) GetERC20CustodyContract() (ethcommon.Address, *erc20custody.ERC20Custody, error) { addr := ethcommon.HexToAddress(ob.GetChainParams().Erc20CustodyContractAddress) contract, err := FetchERC20CustodyContract(addr, ob.evmClient) return addr, contract, err } -func FetchConnectorContract(addr ethcommon.Address, client EVMRPCClient) (*zetaconnector.ZetaConnectorNonEth, error) { +func FetchConnectorContract(addr ethcommon.Address, client interfaces.EVMRPCClient) (*zetaconnector.ZetaConnectorNonEth, error) { return zetaconnector.NewZetaConnectorNonEth(addr, client) } -func FetchConnectorContractEth(addr ethcommon.Address, client EVMRPCClient) (*zetaconnectoreth.ZetaConnectorEth, error) { +func FetchConnectorContractEth(addr ethcommon.Address, client interfaces.EVMRPCClient) (*zetaconnectoreth.ZetaConnectorEth, error) { return zetaconnectoreth.NewZetaConnectorEth(addr, client) } -func FetchZetaZetaNonEthTokenContract(addr ethcommon.Address, client EVMRPCClient) (*zeta.ZetaNonEth, error) { +func FetchZetaZetaNonEthTokenContract(addr ethcommon.Address, client interfaces.EVMRPCClient) (*zeta.ZetaNonEth, error) { return zeta.NewZetaNonEth(addr, client) } -func FetchERC20CustodyContract(addr ethcommon.Address, client EVMRPCClient) (*erc20custody.ERC20Custody, error) { +func FetchERC20CustodyContract(addr ethcommon.Address, client interfaces.EVMRPCClient) (*erc20custody.ERC20Custody, error) { return erc20custody.NewERC20Custody(addr, client) } -func (ob *EVMChainClient) Start() { +func (ob *ChainClient) Start() { go ob.ExternalChainWatcherForNewInboundTrackerSuggestions() go ob.ExternalChainWatcher() // Observes external Chains for incoming trasnactions go ob.WatchGasPrice() // Observes external Chains for Gas prices and posts to core @@ -303,7 +296,7 @@ func (ob *EVMChainClient) Start() { go ob.ExternalChainRPCStatus() } -func (ob *EVMChainClient) ExternalChainRPCStatus() { +func (ob *ChainClient) ExternalChainRPCStatus() { ob.logger.ChainLogger.Info().Msgf("Starting RPC status check for chain %s", ob.chain.String()) ticker := time.NewTicker(60 * time.Second) for { @@ -338,7 +331,7 @@ func (ob *EVMChainClient) ExternalChainRPCStatus() { } } -func (ob *EVMChainClient) Stop() { +func (ob *ChainClient) Stop() { ob.logger.ChainLogger.Info().Msgf("ob %s is stopping", ob.chain.String()) close(ob.stop) // this notifies all goroutines to stop @@ -357,7 +350,7 @@ func (ob *EVMChainClient) Stop() { // returns: isIncluded, isConfirmed, Error // If isConfirmed, it also post to ZetaCore -func (ob *EVMChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, cointype common.CoinType, logger zerolog.Logger) (bool, bool, error) { +func (ob *ChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, cointype common.CoinType, logger zerolog.Logger) (bool, bool, error) { params := ob.GetChainParams() receipt, transaction := ob.GetTxNReceipt(nonce) if receipt == nil || transaction == nil { // not confirmed yet @@ -448,7 +441,7 @@ func (ob *EVMChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, co if err == nil { logger.Info().Msgf("Found (outTx) sendHash %s on chain %s txhash %s", sendHash, ob.chain.String(), vLog.TxHash.Hex()) if confHeight <= ob.GetLastBlockHeight() { - logger.Info().Msg("Confirmed! Sending PostConfirmation to zetacore...") + logger.Info().Msg("Confirmed! Sending PostConfirmation to zetabridge...") // sanity check tx event err = ob.CheckEvmTxLog(vLog, connectorAddr, transaction.Hash().Hex(), TopicsZetaReceived) if err != nil { @@ -486,7 +479,7 @@ func (ob *EVMChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, co if err == nil { logger.Info().Msgf("Found (revertTx) sendHash %s on chain %s txhash %s", sendHash, ob.chain.String(), vLog.TxHash.Hex()) if confHeight <= ob.GetLastBlockHeight() { - logger.Info().Msg("Confirmed! Sending PostConfirmation to zetacore...") + logger.Info().Msg("Confirmed! Sending PostConfirmation to zetabridge...") // sanity check tx event err = ob.CheckEvmTxLog(vLog, connectorAddr, transaction.Hash().Hex(), TopicsZetaReverted) if err != nil { @@ -562,7 +555,7 @@ func (ob *EVMChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, co return false, false, err } if confHeight <= ob.GetLastBlockHeight() { - logger.Info().Msg("Confirmed! Sending PostConfirmation to zetacore...") + logger.Info().Msg("Confirmed! Sending PostConfirmation to zetabridge...") zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( sendHash, vLog.TxHash.Hex(), @@ -617,7 +610,7 @@ func (ob *EVMChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, co // FIXME: there's a chance that a txhash in OutTxChan may not deliver when Stop() is called // observeOutTx periodically checks all the txhash in potential outbound txs -func (ob *EVMChainClient) observeOutTx() { +func (ob *ChainClient) observeOutTx() { // read env variables if set timeoutNonce, err := strconv.Atoi(os.Getenv("OS_TIMEOUT_NONCE")) if err != nil || timeoutNonce <= 0 { @@ -625,7 +618,7 @@ func (ob *EVMChainClient) observeOutTx() { } ob.logger.ObserveOutTx.Info().Msgf("observeOutTx: using timeoutNonce %d seconds", timeoutNonce) - ticker, err := NewDynamicTicker(fmt.Sprintf("EVM_observeOutTx_%d", ob.chain.ChainId), ob.GetChainParams().OutTxTicker) + ticker, err := clienttypes.NewDynamicTicker(fmt.Sprintf("EVM_observeOutTx_%d", ob.chain.ChainId), ob.GetChainParams().OutTxTicker) if err != nil { ob.logger.ObserveOutTx.Error().Err(err).Msg("failed to create ticker") return @@ -635,7 +628,7 @@ func (ob *EVMChainClient) observeOutTx() { for { select { case <-ticker.C(): - trackers, err := ob.zetaClient.GetAllOutTxTrackerByChain(ob.chain.ChainId, Ascending) + trackers, err := ob.zetaClient.GetAllOutTxTrackerByChain(ob.chain.ChainId, interfaces.Ascending) if err != nil { continue } @@ -683,21 +676,21 @@ func (ob *EVMChainClient) observeOutTx() { } // SetPendingTx sets the pending transaction in memory -func (ob *EVMChainClient) SetPendingTx(nonce uint64, transaction *ethtypes.Transaction) { +func (ob *ChainClient) SetPendingTx(nonce uint64, transaction *ethtypes.Transaction) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.outTxPendingTransactions[ob.GetTxID(nonce)] = transaction } // GetPendingTx gets the pending transaction from memory -func (ob *EVMChainClient) GetPendingTx(nonce uint64) *ethtypes.Transaction { +func (ob *ChainClient) GetPendingTx(nonce uint64) *ethtypes.Transaction { ob.Mu.Lock() defer ob.Mu.Unlock() return ob.outTxPendingTransactions[ob.GetTxID(nonce)] } // SetTxNReceipt sets the receipt and transaction in memory -func (ob *EVMChainClient) SetTxNReceipt(nonce uint64, receipt *ethtypes.Receipt, transaction *ethtypes.Transaction) { +func (ob *ChainClient) SetTxNReceipt(nonce uint64, receipt *ethtypes.Receipt, transaction *ethtypes.Transaction) { ob.Mu.Lock() defer ob.Mu.Unlock() delete(ob.outTxPendingTransactions, ob.GetTxID(nonce)) // remove pending transaction, if any @@ -706,7 +699,7 @@ func (ob *EVMChainClient) SetTxNReceipt(nonce uint64, receipt *ethtypes.Receipt, } // GetTxNReceipt gets the receipt and transaction from memory -func (ob *EVMChainClient) GetTxNReceipt(nonce uint64) (*ethtypes.Receipt, *ethtypes.Transaction) { +func (ob *ChainClient) GetTxNReceipt(nonce uint64) (*ethtypes.Receipt, *ethtypes.Transaction) { ob.Mu.Lock() defer ob.Mu.Unlock() receipt := ob.outTXConfirmedReceipts[ob.GetTxID(nonce)] @@ -715,7 +708,7 @@ func (ob *EVMChainClient) GetTxNReceipt(nonce uint64) (*ethtypes.Receipt, *ethty } // isTxConfirmed returns true if there is a confirmed tx for 'nonce' -func (ob *EVMChainClient) isTxConfirmed(nonce uint64) bool { +func (ob *ChainClient) isTxConfirmed(nonce uint64) bool { ob.Mu.Lock() defer ob.Mu.Unlock() return ob.outTXConfirmedReceipts[ob.GetTxID(nonce)] != nil && ob.outTXConfirmedTransactions[ob.GetTxID(nonce)] != nil @@ -723,7 +716,7 @@ func (ob *EVMChainClient) isTxConfirmed(nonce uint64) bool { // checkConfirmedTx checks if a txHash is confirmed // returns (receipt, transaction, true) if confirmed or (nil, nil, false) otherwise -func (ob *EVMChainClient) checkConfirmedTx(txHash string, nonce uint64) (*ethtypes.Receipt, *ethtypes.Transaction, bool) { +func (ob *ChainClient) checkConfirmedTx(txHash string, nonce uint64) (*ethtypes.Receipt, *ethtypes.Transaction, bool) { ctxt, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() @@ -793,7 +786,7 @@ func (ob *EVMChainClient) checkConfirmedTx(txHash string, nonce uint64) (*ethtyp } // checkTxInclusion returns nil only if tx is included in the block at blockNumber and txIndex -func (ob *EVMChainClient) checkTxInclusion(tx *ethtypes.Transaction, blockNumber uint64, txIndex uint) error { +func (ob *ChainClient) checkTxInclusion(tx *ethtypes.Transaction, blockNumber uint64, txIndex uint) error { block, blockRPC, fallBack, _, err := ob.GetBlockByNumberCached(blockNumber) if err != nil { return fmt.Errorf("GetBlockByNumberCached error for block %d txHash %s nonce %d: %w", blockNumber, tx.Hash(), tx.Nonce(), err) @@ -825,19 +818,19 @@ func (ob *EVMChainClient) checkTxInclusion(tx *ethtypes.Transaction, blockNumber } // SetLastBlockHeightScanned set last block height scanned (not necessarily caught up with external block; could be slow/paused) -func (ob *EVMChainClient) SetLastBlockHeightScanned(height uint64) { +func (ob *ChainClient) SetLastBlockHeightScanned(height uint64) { atomic.StoreUint64(&ob.lastBlockScanned, height) ob.ts.SetLastScannedBlockNumber(ob.chain.ChainId, height) } // GetLastBlockHeightScanned get last block height scanned (not necessarily caught up with external block; could be slow/paused) -func (ob *EVMChainClient) GetLastBlockHeightScanned() uint64 { +func (ob *ChainClient) GetLastBlockHeightScanned() uint64 { height := atomic.LoadUint64(&ob.lastBlockScanned) return height } // SetLastBlockHeight set external last block height -func (ob *EVMChainClient) SetLastBlockHeight(height uint64) { +func (ob *ChainClient) SetLastBlockHeight(height uint64) { if height >= math.MaxInt64 { panic("lastBlock is too large") } @@ -845,7 +838,7 @@ func (ob *EVMChainClient) SetLastBlockHeight(height uint64) { } // GetLastBlockHeight get external last block height -func (ob *EVMChainClient) GetLastBlockHeight() uint64 { +func (ob *ChainClient) GetLastBlockHeight() uint64 { height := atomic.LoadUint64(&ob.lastBlock) if height >= math.MaxInt64 { panic("lastBlock is too large") @@ -853,8 +846,8 @@ func (ob *EVMChainClient) GetLastBlockHeight() uint64 { return height } -func (ob *EVMChainClient) ExternalChainWatcher() { - ticker, err := NewDynamicTicker(fmt.Sprintf("EVM_ExternalChainWatcher_%d", ob.chain.ChainId), ob.GetChainParams().InTxTicker) +func (ob *ChainClient) ExternalChainWatcher() { + ticker, err := clienttypes.NewDynamicTicker(fmt.Sprintf("EVM_ExternalChainWatcher_%d", ob.chain.ChainId), ob.GetChainParams().InTxTicker) if err != nil { ob.logger.ExternalChainWatcher.Error().Err(err).Msg("NewDynamicTicker error") return @@ -879,7 +872,7 @@ func (ob *EVMChainClient) ExternalChainWatcher() { } // calcBlockRangeToScan calculates the next range of blocks to scan -func (ob *EVMChainClient) calcBlockRangeToScan(latestConfirmed, lastScanned, batchSize uint64) (uint64, uint64) { +func (ob *ChainClient) calcBlockRangeToScan(latestConfirmed, lastScanned, batchSize uint64) (uint64, uint64) { startBlock := lastScanned + 1 toBlock := lastScanned + batchSize if toBlock > latestConfirmed { @@ -888,7 +881,7 @@ func (ob *EVMChainClient) calcBlockRangeToScan(latestConfirmed, lastScanned, bat return startBlock, toBlock } -func (ob *EVMChainClient) postBlockHeader(tip uint64) error { +func (ob *ChainClient) postBlockHeader(tip uint64) error { bn := tip res, err := ob.zetaClient.GetBlockHeaderStateByChain(ob.chain.ChainId) @@ -925,7 +918,7 @@ func (ob *EVMChainClient) postBlockHeader(tip uint64) error { return nil } -func (ob *EVMChainClient) observeInTX(sampledLogger zerolog.Logger) error { +func (ob *ChainClient) observeInTX(sampledLogger zerolog.Logger) error { // make sure inbound TXS / Send is enabled by the protocol flags, err := ob.zetaClient.GetCrosschainFlags() if err != nil { @@ -999,9 +992,9 @@ func (ob *EVMChainClient) observeInTX(sampledLogger zerolog.Logger) error { return nil } -// observeZetaSent queries the ZetaSent event from the connector contract and posts to zetacore +// observeZetaSent queries the ZetaSent event from the connector contract and posts to zetabridge // returns the last block successfully scanned -func (ob *EVMChainClient) observeZetaSent(startBlock, toBlock uint64) uint64 { +func (ob *ChainClient) observeZetaSent(startBlock, toBlock uint64) uint64 { // filter ZetaSent logs addrConnector, connector, err := ob.GetConnectorContract() if err != nil { @@ -1049,7 +1042,7 @@ func (ob *EVMChainClient) observeZetaSent(startBlock, toBlock uint64) uint64 { cnt.Inc() } - // post to zetacore + // post to zetabridge beingScanned := uint64(0) for _, event := range events { // remember which block we are scanning (there could be multiple events in the same block) @@ -1060,7 +1053,7 @@ func (ob *EVMChainClient) observeZetaSent(startBlock, toBlock uint64) uint64 { if msg == nil { continue } - zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundMessagePassingExecutionGasLimit, msg) + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(zetabridge.PostVoteInboundGasLimit, zetabridge.PostVoteInboundMessagePassingExecutionGasLimit, msg) if err != nil { ob.logger.ExternalChainWatcher.Error().Err(err).Msgf( "observeZetaSent: error posting event to zeta core for tx %s at height %d for chain %d", @@ -1076,9 +1069,9 @@ func (ob *EVMChainClient) observeZetaSent(startBlock, toBlock uint64) uint64 { return toBlock } -// observeERC20Deposited queries the ERC20CustodyDeposited event from the ERC20Custody contract and posts to zetacore +// observeERC20Deposited queries the ERC20CustodyDeposited event from the ERC20Custody contract and posts to zetabridge // returns the last block successfully scanned -func (ob *EVMChainClient) observeERC20Deposited(startBlock, toBlock uint64) uint64 { +func (ob *ChainClient) observeERC20Deposited(startBlock, toBlock uint64) uint64 { // filter ERC20CustodyDeposited logs addrCustody, erc20custodyContract, err := ob.GetERC20CustodyContract() if err != nil { @@ -1126,7 +1119,7 @@ func (ob *EVMChainClient) observeERC20Deposited(startBlock, toBlock uint64) uint cnt.Inc() } - // post to zetacore + // post to zetabridge guard := make(map[string]bool) // guard against multiple events in the same tx beingScanned := uint64(0) for _, event := range events { @@ -1155,7 +1148,7 @@ func (ob *EVMChainClient) observeERC20Deposited(startBlock, toBlock uint64) uint if msg == nil { continue } - zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundExecutionGasLimit, msg) + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(zetabridge.PostVoteInboundGasLimit, zetabridge.PostVoteInboundExecutionGasLimit, msg) if err != nil { ob.logger.ExternalChainWatcher.Error().Err(err).Msgf( "observeERC20Deposited: error posting event to zeta core for tx %s at height %d for chain %d", @@ -1172,9 +1165,9 @@ func (ob *EVMChainClient) observeERC20Deposited(startBlock, toBlock uint64) uint return toBlock } -// observeTssRecvd queries the incoming gas asset to TSS address and posts to zetacore +// observeTssRecvd queries the incoming gas asset to TSS address and posts to zetabridge // returns the last block successfully scanned -func (ob *EVMChainClient) observeTssRecvd(startBlock, toBlock uint64, flags observertypes.CrosschainFlags) uint64 { +func (ob *ChainClient) observeTssRecvd(startBlock, toBlock uint64, flags observertypes.CrosschainFlags) uint64 { if !ob.GetChainParams().IsSupported { //ob.logger.ExternalChainWatcher.Warn().Msgf("observeTssRecvd: chain %d is not supported", ob.chain.ChainId) return startBlock - 1 // lastScanned @@ -1188,7 +1181,7 @@ func (ob *EVMChainClient) observeTssRecvd(startBlock, toBlock uint64, flags obse // query incoming gas asset for bn := startBlock; bn <= toBlock; bn++ { - // post new block header (if any) to zetacore and ignore error + // post new block header (if any) to zetabridge and ignore error // TODO: consider having a independent ticker(from TSS scaning) for posting block headers if flags.BlockHeaderVerificationFlags != nil && flags.BlockHeaderVerificationFlags.IsEthTypeChainEnabled && @@ -1243,7 +1236,7 @@ func (ob *EVMChainClient) observeTssRecvd(startBlock, toBlock uint64, flags obse // processIntxToTss processes the incoming tx to TSS address and posts to zetacore // returns true if the tx is successfully processed, false otherwise -func (ob *EVMChainClient) processIntxToTss(tx *ethtypes.Transaction, bn uint64, blockHash ethcommon.Hash) bool { +func (ob *ChainClient) processIntxToTss(tx *ethtypes.Transaction, bn uint64, blockHash ethcommon.Hash) bool { receipt, err := ob.evmClient.TransactionReceipt(context.Background(), tx.Hash()) if err != nil { ob.logger.ExternalChainWatcher.Err(err).Msgf( @@ -1270,7 +1263,7 @@ func (ob *EVMChainClient) processIntxToTss(tx *ethtypes.Transaction, bn uint64, if msg == nil { return true // should never happen, always non-nil } - zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundExecutionGasLimit, msg) + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(zetabridge.PostVoteInboundGasLimit, zetabridge.PostVoteInboundExecutionGasLimit, msg) if err != nil { ob.logger.ExternalChainWatcher.Error().Err(err).Msgf( "processIntxToTss: error posting to zeta core for tx %s at height %d for chain %d", tx.Hash().Hex(), bn, ob.chain.ChainId) @@ -1283,7 +1276,7 @@ func (ob *EVMChainClient) processIntxToTss(tx *ethtypes.Transaction, bn uint64, return true } -func (ob *EVMChainClient) WatchGasPrice() { +func (ob *ChainClient) WatchGasPrice() { ob.logger.WatchGasPrice.Info().Msg("WatchGasPrice starting...") err := ob.PostGasPrice() if err != nil { @@ -1295,7 +1288,7 @@ func (ob *EVMChainClient) WatchGasPrice() { } } - ticker, err := NewDynamicTicker(fmt.Sprintf("EVM_WatchGasPrice_%d", ob.chain.ChainId), ob.GetChainParams().GasPriceTicker) + ticker, err := clienttypes.NewDynamicTicker(fmt.Sprintf("EVM_WatchGasPrice_%d", ob.chain.ChainId), ob.GetChainParams().GasPriceTicker) if err != nil { ob.logger.WatchGasPrice.Error().Err(err).Msg("NewDynamicTicker error") return @@ -1323,7 +1316,7 @@ func (ob *EVMChainClient) WatchGasPrice() { } } -func (ob *EVMChainClient) PostGasPrice() error { +func (ob *ChainClient) PostGasPrice() error { // GAS PRICE gasPrice, err := ob.evmClient.SuggestGasPrice(context.TODO()) @@ -1342,7 +1335,7 @@ func (ob *EVMChainClient) PostGasPrice() error { zetaHash, err := ob.zetaClient.PostGasPrice(ob.chain, gasPrice.Uint64(), supply, blockNum) if err != nil { - ob.logger.WatchGasPrice.Err(err).Msg("PostGasPrice to zetacore failed") + ob.logger.WatchGasPrice.Err(err).Msg("PostGasPrice to zetabridge failed") return err } _ = zetaHash @@ -1350,7 +1343,7 @@ func (ob *EVMChainClient) PostGasPrice() error { return nil } -func (ob *EVMChainClient) BuildLastBlock() error { +func (ob *ChainClient) BuildLastBlock() error { logger := ob.logger.ChainLogger.With().Str("module", "BuildBlockIndex").Logger() envvar := ob.chain.ChainName.String() + "_SCAN_FROM" scanFromBlock := os.Getenv(envvar) @@ -1388,7 +1381,7 @@ func (ob *EVMChainClient) BuildLastBlock() error { return nil } -func (ob *EVMChainClient) BuildReceiptsMap() error { +func (ob *ChainClient) BuildReceiptsMap() error { logger := ob.logger var receipts []clienttypes.ReceiptSQLType if err := ob.db.Find(&receipts).Error; err != nil { @@ -1406,7 +1399,7 @@ func (ob *EVMChainClient) BuildReceiptsMap() error { return nil } -func (ob *EVMChainClient) BuildTransactionsMap() error { +func (ob *ChainClient) BuildTransactionsMap() error { logger := ob.logger var transactions []clienttypes.TransactionSQLType if err := ob.db.Find(&transactions).Error; err != nil { @@ -1424,7 +1417,7 @@ func (ob *EVMChainClient) BuildTransactionsMap() error { } // LoadDB open sql database and load data into EVMChainClient -func (ob *EVMChainClient) LoadDB(dbPath string, chain common.Chain) error { +func (ob *ChainClient) LoadDB(dbPath string, chain common.Chain) error { if dbPath != "" { if _, err := os.Stat(dbPath); os.IsNotExist(err) { err := os.MkdirAll(dbPath, os.ModePerm) @@ -1455,7 +1448,7 @@ func (ob *EVMChainClient) LoadDB(dbPath string, chain common.Chain) error { return nil } -func (ob *EVMChainClient) SetMinAndMaxNonce(trackers []types.OutTxTracker) error { +func (ob *ChainClient) SetMinAndMaxNonce(trackers []types.OutTxTracker) error { minNonce, maxNonce := int64(-1), int64(0) for _, tracker := range trackers { conv := tracker.Nonce @@ -1480,12 +1473,12 @@ func (ob *EVMChainClient) SetMinAndMaxNonce(trackers []types.OutTxTracker) error return nil } -func (ob *EVMChainClient) GetTxID(nonce uint64) string { +func (ob *ChainClient) GetTxID(nonce uint64) string { tssAddr := ob.Tss.EVMAddress().String() return fmt.Sprintf("%d-%s-%d", ob.chain.ChainId, tssAddr, nonce) } -func (ob *EVMChainClient) GetBlockHeaderCached(blockNumber uint64) (*ethtypes.Header, error) { +func (ob *ChainClient) GetBlockHeaderCached(blockNumber uint64) (*ethtypes.Header, error) { if header, ok := ob.headerCache.Get(blockNumber); ok { return header.(*ethtypes.Header), nil } @@ -1499,7 +1492,7 @@ func (ob *EVMChainClient) GetBlockHeaderCached(blockNumber uint64) (*ethtypes.He // GetBlockByNumberCached get block by number from cache // returns block, ethrpc.Block, isFallback, isSkip, error -func (ob *EVMChainClient) GetBlockByNumberCached(blockNumber uint64) (*ethtypes.Block, *ethrpc.Block, bool, bool, error) { +func (ob *ChainClient) GetBlockByNumberCached(blockNumber uint64) (*ethtypes.Block, *ethrpc.Block, bool, bool, error) { if block, ok := ob.blockCache.Get(blockNumber); ok { return block.(*ethtypes.Block), nil, false, false, nil } diff --git a/zetaclient/evm_client_db_test.go b/zetaclient/evm/evm_client_db_test.go similarity index 99% rename from zetaclient/evm_client_db_test.go rename to zetaclient/evm/evm_client_db_test.go index 62a910702e..c9fbefce73 100644 --- a/zetaclient/evm_client_db_test.go +++ b/zetaclient/evm/evm_client_db_test.go @@ -1,4 +1,4 @@ -package zetaclient +package evm import ( "strconv" diff --git a/zetaclient/evm_signer.go b/zetaclient/evm/evm_signer.go similarity index 94% rename from zetaclient/evm_signer.go rename to zetaclient/evm/evm_signer.go index 831047bf9b..37988ab3ab 100644 --- a/zetaclient/evm_signer.go +++ b/zetaclient/evm/evm_signer.go @@ -1,4 +1,4 @@ -package zetaclient +package evm import ( "context" @@ -12,6 +12,10 @@ import ( "sync" "time" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/metrics" + "github.com/zeta-chain/zetacore/zetaclient/outtxprocessor" + "github.com/ethereum/go-ethereum/accounts/abi" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" @@ -24,6 +28,7 @@ import ( crosschainkeeper "github.com/zeta-chain/zetacore/x/crosschain/keeper" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" + zbridge "github.com/zeta-chain/zetacore/zetaclient/zetabridge" ) const ( @@ -32,37 +37,37 @@ const ( ZetaBlockTime = 6500 * time.Millisecond ) -type EVMSigner struct { - client EVMRPCClient +type Signer struct { + client interfaces.EVMRPCClient chain *common.Chain chainID *big.Int - tssSigner TSSSigner + tssSigner interfaces.TSSSigner ethSigner ethtypes.Signer abi abi.ABI erc20CustodyABI abi.ABI metaContractAddress ethcommon.Address erc20CustodyContractAddress ethcommon.Address logger zerolog.Logger - ts *TelemetryServer + ts *metrics.TelemetryServer // for outTx tracker report only mu *sync.Mutex outTxHashBeingReported map[string]bool } -var _ ChainSigner = &EVMSigner{} +var _ interfaces.ChainSigner = &Signer{} func NewEVMSigner( chain common.Chain, endpoint string, - tssSigner TSSSigner, + tssSigner interfaces.TSSSigner, abiString string, erc20CustodyABIString string, metaContract ethcommon.Address, erc20CustodyContract ethcommon.Address, logger zerolog.Logger, - ts *TelemetryServer, -) (*EVMSigner, error) { + ts *metrics.TelemetryServer, +) (*Signer, error) { client, err := ethclient.Dial(endpoint) if err != nil { return nil, err @@ -82,7 +87,7 @@ func NewEVMSigner( return nil, err } - return &EVMSigner{ + return &Signer{ client: client, chain: &chain, tssSigner: tssSigner, @@ -103,7 +108,7 @@ func NewEVMSigner( // Sign given data, and metadata (gas, nonce, etc) // returns a signed transaction, sig bytes, hash bytes, and error -func (signer *EVMSigner) Sign( +func (signer *Signer) Sign( data []byte, to ethcommon.Address, gasLimit uint64, @@ -134,7 +139,7 @@ func (signer *EVMSigner) Sign( } // Broadcast takes in signed tx, broadcast to external chain node -func (signer *EVMSigner) Broadcast(tx *ethtypes.Transaction) error { +func (signer *Signer) Broadcast(tx *ethtypes.Transaction) error { ctxt, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() return signer.client.SendTransaction(ctxt, tx) @@ -151,7 +156,7 @@ func (signer *EVMSigner) Broadcast(tx *ethtypes.Transaction) error { // bytes32 internalSendHash // // ) external virtual {} -func (signer *EVMSigner) SignOutboundTx(sender ethcommon.Address, +func (signer *Signer) SignOutboundTx(sender ethcommon.Address, srcChainID *big.Int, to ethcommon.Address, amount *big.Int, @@ -191,7 +196,7 @@ func (signer *EVMSigner) SignOutboundTx(sender ethcommon.Address, // bytes calldata message, // bytes32 internalSendHash // ) external override whenNotPaused onlyTssAddress -func (signer *EVMSigner) SignRevertTx( +func (signer *Signer) SignRevertTx( sender ethcommon.Address, srcChainID *big.Int, to []byte, @@ -220,7 +225,7 @@ func (signer *EVMSigner) SignRevertTx( return tx, nil } -func (signer *EVMSigner) SignCancelTx(nonce uint64, gasPrice *big.Int, height uint64) (*ethtypes.Transaction, error) { +func (signer *Signer) SignCancelTx(nonce uint64, gasPrice *big.Int, height uint64) (*ethtypes.Transaction, error) { tx := ethtypes.NewTransaction(nonce, signer.tssSigner.EVMAddress(), big.NewInt(0), 21000, gasPrice, nil) hashBytes := signer.ethSigner.Hash(tx).Bytes() sig, err := signer.tssSigner.Sign(hashBytes, height, nonce, signer.chain, "") @@ -241,7 +246,7 @@ func (signer *EVMSigner) SignCancelTx(nonce uint64, gasPrice *big.Int, height ui return signedTX, nil } -func (signer *EVMSigner) SignWithdrawTx( +func (signer *Signer) SignWithdrawTx( to ethcommon.Address, amount *big.Int, nonce uint64, @@ -268,7 +273,7 @@ func (signer *EVMSigner) SignWithdrawTx( return signedTX, nil } -func (signer *EVMSigner) SignCommandTx( +func (signer *Signer) SignCommandTx( cmd string, params string, to ethcommon.Address, @@ -320,12 +325,12 @@ func (signer *EVMSigner) SignCommandTx( return nil, fmt.Errorf("SignCommandTx: unknown command %s", cmd) } -func (signer *EVMSigner) TryProcessOutTx( +func (signer *Signer) TryProcessOutTx( cctx *types.CrossChainTx, - outTxMan *OutTxProcessorManager, + outTxMan *outtxprocessor.Processor, outTxID string, - chainclient ChainClient, - zetaBridge ZetaCoreBridger, + chainclient interfaces.ChainClient, + zetaBridge interfaces.ZetaCoreBridger, height uint64, ) { logger := signer.logger.With(). @@ -361,7 +366,7 @@ func (signer *EVMSigner) TryProcessOutTx( logger.Info().Msgf("Transaction doesn't need to be processed status: %d", cctx.CctxStatus.Status) return } - evmClient, ok := chainclient.(*EVMChainClient) + evmClient, ok := chainclient.(*ChainClient) if !ok { logger.Error().Msgf("chain client is not an EVMChainClient") return @@ -578,7 +583,7 @@ func (signer *EVMSigner) TryProcessOutTx( err := signer.Broadcast(tx) if err != nil { log.Warn().Err(err).Msgf("OutTx Broadcast error") - retry, report := HandleBroadcastError(err, strconv.FormatUint(cctx.GetCurrentOutTxParam().OutboundTxTssNonce, 10), toChain.String(), outTxHash) + retry, report := zbridge.HandleBroadcastError(err, strconv.FormatUint(cctx.GetCurrentOutTxParam().OutboundTxTssNonce, 10), toChain.String(), outTxHash) if report { signer.reportToOutTxTracker(zetaBridge, toChain.ChainId, tx.Nonce(), outTxHash, logger) } @@ -595,8 +600,8 @@ func (signer *EVMSigner) TryProcessOutTx( } } -// reportToOutTxTracker reports outTxHash to tracker only when tx is included in a block -func (signer *EVMSigner) reportToOutTxTracker(zetaBridge ZetaCoreBridger, chainID int64, nonce uint64, outTxHash string, logger zerolog.Logger) { +// reportToOutTxTracker reports outTxHash to tracker only when tx receipt is available +func (signer *Signer) reportToOutTxTracker(zetaBridge interfaces.ZetaCoreBridger, chainID int64, nonce uint64, outTxHash string, logger zerolog.Logger) { // skip if already being reported signer.mu.Lock() defer signer.mu.Unlock() @@ -695,7 +700,7 @@ func (signer *EVMSigner) reportToOutTxTracker(zetaBridge ZetaCoreBridger, chainI // address asset, // uint256 amount, // ) external onlyTssAddress -func (signer *EVMSigner) SignERC20WithdrawTx( +func (signer *Signer) SignERC20WithdrawTx( recipient ethcommon.Address, asset ethcommon.Address, amount *big.Int, @@ -726,7 +731,7 @@ func (signer *EVMSigner) SignERC20WithdrawTx( // function unwhitelist( // address asset, // ) external onlyTssAddress -func (signer *EVMSigner) SignWhitelistTx( +func (signer *Signer) SignWhitelistTx( action string, _ ethcommon.Address, asset ethcommon.Address, diff --git a/zetaclient/inbound_tracker.go b/zetaclient/evm/inbounds.go similarity index 53% rename from zetaclient/inbound_tracker.go rename to zetaclient/evm/inbounds.go index 31f0c4fab1..24f4006c2e 100644 --- a/zetaclient/inbound_tracker.go +++ b/zetaclient/evm/inbounds.go @@ -1,20 +1,31 @@ -package zetaclient +package evm import ( + "bytes" + "encoding/base64" + "encoding/hex" "errors" "fmt" + "math/big" + "strings" + + sdkmath "cosmossdk.io/math" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" + clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" - "github.com/btcsuite/btcd/chaincfg/chainhash" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/x/crosschain/types" + "github.com/zeta-chain/zetacore/zetaclient/zetabridge" "golang.org/x/net/context" ) // ExternalChainWatcherForNewInboundTrackerSuggestions At each tick, gets a list of Inbound tracker suggestions from zeta-core and tries to check if the in-tx was confirmed. // If it was, it tries to broadcast the confirmation vote. If this zeta client has previously broadcast the vote, the tx would be rejected -func (ob *EVMChainClient) ExternalChainWatcherForNewInboundTrackerSuggestions() { - ticker, err := NewDynamicTicker( +func (ob *ChainClient) ExternalChainWatcherForNewInboundTrackerSuggestions() { + ticker, err := clienttypes.NewDynamicTicker( fmt.Sprintf("EVM_ExternalChainWatcher_InboundTrackerSuggestions_%d", ob.chain.ChainId), ob.GetChainParams().InTxTicker, ) @@ -40,89 +51,7 @@ func (ob *EVMChainClient) ExternalChainWatcherForNewInboundTrackerSuggestions() } } -func (ob *BitcoinChainClient) ExternalChainWatcherForNewInboundTrackerSuggestions() { - ticker, err := NewDynamicTicker("Bitcoin_WatchInTx_InboundTrackerSuggestions", ob.GetChainParams().InTxTicker) - if err != nil { - ob.logger.WatchInTx.Err(err).Msg("error creating ticker") - return - } - - defer ticker.Stop() - for { - select { - case <-ticker.C(): - err := ob.ObserveTrackerSuggestions() - if err != nil { - ob.logger.WatchInTx.Error().Err(err).Msg("error observing in tx") - } - ticker.UpdateInterval(ob.GetChainParams().InTxTicker, ob.logger.WatchInTx) - case <-ob.stop: - ob.logger.WatchInTx.Info().Msg("ExternalChainWatcher for BTC inboundTrackerSuggestions stopped") - return - } - } -} - -func (ob *BitcoinChainClient) ObserveTrackerSuggestions() error { - trackers, err := ob.zetaClient.GetInboundTrackersForChain(ob.chain.ChainId) - if err != nil { - return err - } - for _, tracker := range trackers { - ob.logger.WatchInTx.Info().Msgf("checking tracker with hash :%s and coin-type :%s ", tracker.TxHash, tracker.CoinType) - ballotIdentifier, err := ob.CheckReceiptForBtcTxHash(tracker.TxHash, true) - if err != nil { - return err - } - ob.logger.WatchInTx.Info().Msgf("Vote submitted for inbound Tracker,Chain : %s,Ballot Identifier : %s, coin-type %s", ob.chain.ChainName, ballotIdentifier, common.CoinType_Gas.String()) - } - return nil -} - -func (ob *BitcoinChainClient) CheckReceiptForBtcTxHash(txHash string, vote bool) (string, error) { - hash, err := chainhash.NewHashFromStr(txHash) - if err != nil { - return "", err - } - tx, err := ob.rpcClient.GetRawTransactionVerbose(hash) - if err != nil { - return "", err - } - blockHash, err := chainhash.NewHashFromStr(tx.BlockHash) - if err != nil { - return "", err - } - block, err := ob.rpcClient.GetBlockVerbose(blockHash) - if err != nil { - return "", err - } - tss, err := ob.zetaClient.GetBtcTssAddress(ob.chain.ChainId) - if err != nil { - return "", err - } - // #nosec G701 always positive - event, err := GetBtcEvent(*tx, tss, uint64(block.Height), &ob.logger.WatchInTx, ob.chain.ChainId) - if err != nil { - return "", err - } - if event == nil { - return "", errors.New("no btc deposit event found") - } - msg := ob.GetInboundVoteMessageFromBtcEvent(event) - if !vote { - return msg.Digest(), nil - } - zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundExecutionGasLimit, msg) - if err != nil { - ob.logger.WatchInTx.Error().Err(err).Msg("error posting to zeta core") - return "", err - } else if zetaHash != "" { - ob.logger.WatchInTx.Info().Msgf("BTC deposit detected and reported: PostVoteInbound zeta tx: %s ballot %s", zetaHash, ballot) - } - return msg.Digest(), nil -} - -func (ob *EVMChainClient) ObserveTrackerSuggestions() error { +func (ob *ChainClient) ObserveTrackerSuggestions() error { trackers, err := ob.zetaClient.GetInboundTrackersForChain(ob.chain.ChainId) if err != nil { return err @@ -153,7 +82,7 @@ func (ob *EVMChainClient) ObserveTrackerSuggestions() error { return nil } -func (ob *EVMChainClient) CheckReceiptForCoinTypeZeta(txHash string, vote bool) (string, error) { +func (ob *ChainClient) CheckReceiptForCoinTypeZeta(txHash string, vote bool) (string, error) { addrConnector, connector, err := ob.GetConnectorContract() if err != nil { return "", err @@ -193,7 +122,7 @@ func (ob *EVMChainClient) CheckReceiptForCoinTypeZeta(txHash string, vote bool) return msg.Digest(), nil } - zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundMessagePassingExecutionGasLimit, msg) + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(zetabridge.PostVoteInboundGasLimit, zetabridge.PostVoteInboundMessagePassingExecutionGasLimit, msg) if err != nil { ob.logger.ExternalChainWatcher.Error().Err(err).Msg("error posting to zeta core") return "", err @@ -204,7 +133,7 @@ func (ob *EVMChainClient) CheckReceiptForCoinTypeZeta(txHash string, vote bool) return msg.Digest(), nil } -func (ob *EVMChainClient) CheckReceiptForCoinTypeERC20(txHash string, vote bool) (string, error) { +func (ob *ChainClient) CheckReceiptForCoinTypeERC20(txHash string, vote bool) (string, error) { addrCustory, custody, err := ob.GetERC20CustodyContract() if err != nil { return "", err @@ -253,7 +182,7 @@ func (ob *EVMChainClient) CheckReceiptForCoinTypeERC20(txHash string, vote bool) return msg.Digest(), nil } - zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundExecutionGasLimit, msg) + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(zetabridge.PostVoteInboundGasLimit, zetabridge.PostVoteInboundExecutionGasLimit, msg) if err != nil { ob.logger.ExternalChainWatcher.Error().Err(err).Msg("error posting to zeta core") return "", err @@ -264,7 +193,7 @@ func (ob *EVMChainClient) CheckReceiptForCoinTypeERC20(txHash string, vote bool) return msg.Digest(), nil } -func (ob *EVMChainClient) CheckReceiptForCoinTypeGas(txHash string, vote bool) (string, error) { +func (ob *ChainClient) CheckReceiptForCoinTypeGas(txHash string, vote bool) (string, error) { // TSS address should be set tssAddress := ob.Tss.EVMAddress() if tssAddress == (ethcommon.Address{}) { @@ -314,7 +243,7 @@ func (ob *EVMChainClient) CheckReceiptForCoinTypeGas(txHash string, vote bool) ( return msg.Digest(), nil } - zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundExecutionGasLimit, msg) + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(zetabridge.PostVoteInboundGasLimit, zetabridge.PostVoteInboundExecutionGasLimit, msg) if err != nil { ob.logger.ExternalChainWatcher.Error().Err(err).Msg("error posting to zeta core") return "", err @@ -324,3 +253,141 @@ func (ob *EVMChainClient) CheckReceiptForCoinTypeGas(txHash string, vote bool) ( return msg.Digest(), nil } + +// CheckEvmTxLog checks the basics of an EVM tx log +func (ob *ChainClient) CheckEvmTxLog(vLog *ethtypes.Log, wantAddress ethcommon.Address, wantHash string, wantTopics int) error { + if vLog.Removed { + return fmt.Errorf("log is removed, chain reorg?") + } + if vLog.Address != wantAddress { + return fmt.Errorf("log emitter address mismatch: want %s got %s", wantAddress.Hex(), vLog.Address.Hex()) + } + if vLog.TxHash.Hex() == "" { + return fmt.Errorf("log tx hash is empty: %d %s", vLog.BlockNumber, vLog.TxHash.Hex()) + } + if wantHash != "" && vLog.TxHash.Hex() != wantHash { + return fmt.Errorf("log tx hash mismatch: want %s got %s", wantHash, vLog.TxHash.Hex()) + } + if len(vLog.Topics) != wantTopics { + return fmt.Errorf("number of topics mismatch: want %d got %d", wantTopics, len(vLog.Topics)) + } + return nil +} + +// HasEnoughConfirmations checks if the given receipt has enough confirmations +func (ob *ChainClient) HasEnoughConfirmations(receipt *ethtypes.Receipt, lastHeight uint64) bool { + confHeight := receipt.BlockNumber.Uint64() + ob.GetChainParams().ConfirmationCount + return lastHeight >= confHeight +} + +// GetTransactionSender returns the sender of the given transaction +func (ob *ChainClient) GetTransactionSender(tx *ethtypes.Transaction, blockHash ethcommon.Hash, txIndex uint) (ethcommon.Address, error) { + sender, err := ob.evmClient.TransactionSender(context.Background(), tx, blockHash, txIndex) + if err != nil { + // trying local recovery (assuming LondonSigner dynamic fee tx type) of sender address + signer := ethtypes.NewLondonSigner(big.NewInt(ob.chain.ChainId)) + sender, err = signer.Sender(tx) + if err != nil { + ob.logger.ExternalChainWatcher.Err(err).Msgf("can't recover the sender from tx hash %s chain %d", tx.Hash().Hex(), ob.chain.ChainId) + return ethcommon.Address{}, err + } + } + return sender, nil +} + +func (ob *ChainClient) GetInboundVoteMsgForDepositedEvent(event *erc20custody.ERC20CustodyDeposited, sender ethcommon.Address) *types.MsgVoteOnObservedInboundTx { + if bytes.Equal(event.Message, []byte(DonationMessage)) { + ob.logger.ExternalChainWatcher.Info().Msgf("thank you rich folk for your donation! tx %s chain %d", event.Raw.TxHash.Hex(), ob.chain.ChainId) + return nil + } + message := hex.EncodeToString(event.Message) + ob.logger.ExternalChainWatcher.Info().Msgf("ERC20CustodyDeposited inTx detected on chain %d tx %s block %d from %s value %s message %s", + ob.chain.ChainId, event.Raw.TxHash.Hex(), event.Raw.BlockNumber, sender.Hex(), event.Amount.String(), message) + + return zetabridge.GetInBoundVoteMessage( + sender.Hex(), + ob.chain.ChainId, + "", + clienttypes.BytesToEthHex(event.Recipient), + ob.zetaClient.ZetaChain().ChainId, + sdkmath.NewUintFromBigInt(event.Amount), + hex.EncodeToString(event.Message), + event.Raw.TxHash.Hex(), + event.Raw.BlockNumber, + 1_500_000, + common.CoinType_ERC20, + event.Asset.String(), + ob.zetaClient.GetKeys().GetOperatorAddress().String(), + event.Raw.Index, + ) +} + +func (ob *ChainClient) GetInboundVoteMsgForZetaSentEvent(event *zetaconnector.ZetaConnectorNonEthZetaSent) *types.MsgVoteOnObservedInboundTx { + destChain := common.GetChainFromChainID(event.DestinationChainId.Int64()) + if destChain == nil { + ob.logger.ExternalChainWatcher.Warn().Msgf("chain id not supported %d", event.DestinationChainId.Int64()) + return nil + } + destAddr := clienttypes.BytesToEthHex(event.DestinationAddress) + if !destChain.IsZetaChain() { + cfgDest, found := ob.cfg.GetEVMConfig(destChain.ChainId) + if !found { + ob.logger.ExternalChainWatcher.Warn().Msgf("chain id not present in EVMChainConfigs %d", event.DestinationChainId.Int64()) + return nil + } + if strings.EqualFold(destAddr, cfgDest.ZetaTokenContractAddress) { + ob.logger.ExternalChainWatcher.Warn().Msgf("potential attack attempt: %s destination address is ZETA token contract address %s", destChain, destAddr) + return nil + } + } + message := base64.StdEncoding.EncodeToString(event.Message) + ob.logger.ExternalChainWatcher.Info().Msgf("ZetaSent inTx detected on chain %d tx %s block %d from %s value %s message %s", + ob.chain.ChainId, event.Raw.TxHash.Hex(), event.Raw.BlockNumber, event.ZetaTxSenderAddress.Hex(), event.ZetaValueAndGas.String(), message) + + return zetabridge.GetInBoundVoteMessage( + event.ZetaTxSenderAddress.Hex(), + ob.chain.ChainId, + event.SourceTxOriginAddress.Hex(), + clienttypes.BytesToEthHex(event.DestinationAddress), + destChain.ChainId, + sdkmath.NewUintFromBigInt(event.ZetaValueAndGas), + message, + event.Raw.TxHash.Hex(), + event.Raw.BlockNumber, + event.DestinationGasLimit.Uint64(), + common.CoinType_Zeta, + "", + ob.zetaClient.GetKeys().GetOperatorAddress().String(), + event.Raw.Index, + ) +} + +func (ob *ChainClient) GetInboundVoteMsgForTokenSentToTSS(tx *ethtypes.Transaction, sender ethcommon.Address, blockNumber uint64) *types.MsgVoteOnObservedInboundTx { + if bytes.Equal(tx.Data(), []byte(DonationMessage)) { + ob.logger.ExternalChainWatcher.Info().Msgf("thank you rich folk for your donation! tx %s chain %d", tx.Hash().Hex(), ob.chain.ChainId) + return nil + } + message := "" + if len(tx.Data()) != 0 { + message = hex.EncodeToString(tx.Data()) + } + ob.logger.ExternalChainWatcher.Info().Msgf("TSS inTx detected on chain %d tx %s block %d from %s value %s message %s", + ob.chain.ChainId, tx.Hash().Hex(), blockNumber, sender.Hex(), tx.Value().String(), hex.EncodeToString(tx.Data())) + + return zetabridge.GetInBoundVoteMessage( + sender.Hex(), + ob.chain.ChainId, + sender.Hex(), + sender.Hex(), + ob.zetaClient.ZetaChain().ChainId, + sdkmath.NewUintFromBigInt(tx.Value()), + message, + tx.Hash().Hex(), + blockNumber, + 90_000, + common.CoinType_Gas, + "", + ob.zetaClient.GetKeys().GetOperatorAddress().String(), + 0, // not a smart contract call + ) +} diff --git a/zetaclient/interfaces.go b/zetaclient/interfaces/interfaces.go similarity index 94% rename from zetaclient/interfaces.go rename to zetaclient/interfaces/interfaces.go index 0ef1b7ca50..2d4ecff7c6 100644 --- a/zetaclient/interfaces.go +++ b/zetaclient/interfaces/interfaces.go @@ -1,9 +1,12 @@ -package zetaclient +package interfaces import ( "context" "math/big" + "github.com/zeta-chain/zetacore/zetaclient/keys" + "github.com/zeta-chain/zetacore/zetaclient/outtxprocessor" + sdkmath "cosmossdk.io/math" "github.com/btcsuite/btcd/btcjson" @@ -22,6 +25,14 @@ import ( observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) +type Order string + +const ( + NoOrder Order = "" + Ascending Order = "ASC" + Descending Order = "DESC" +) + // ChainClient is the interface for chain clients type ChainClient interface { Start() @@ -39,7 +50,7 @@ type ChainClient interface { type ChainSigner interface { TryProcessOutTx( send *crosschaintypes.CrossChainTx, - outTxMan *OutTxProcessorManager, + outTxMan *outtxprocessor.Processor, outTxID string, evmClient ChainClient, zetaBridge ZetaCoreBridger, @@ -76,7 +87,7 @@ type ZetaCoreBridger interface { blockHash string, txIndex int64, ) (string, error) - GetKeys() *Keys + GetKeys() *keys.Keys GetBlockHeight() (int64, error) GetZetaBlockHeight() (int64, error) GetLastBlockHeightByChain(chain common.Chain) (*crosschaintypes.LastBlockHeight, error) @@ -129,8 +140,3 @@ type EVMRPCClient interface { TransactionReceipt(ctx context.Context, txHash ethcommon.Hash) (*ethtypes.Receipt, error) TransactionSender(ctx context.Context, tx *ethtypes.Transaction, block ethcommon.Hash, index uint) (ethcommon.Address, error) } - -// KlaytnRPCClient is the interface for Klaytn RPC client -type KlaytnRPCClient interface { - BlockByNumber(ctx context.Context, number *big.Int) (*RPCBlock, error) -} diff --git a/zetaclient/signer.go b/zetaclient/interfaces/signer.go similarity index 99% rename from zetaclient/signer.go rename to zetaclient/interfaces/signer.go index 6410af38ed..ae6ef31b59 100644 --- a/zetaclient/signer.go +++ b/zetaclient/interfaces/signer.go @@ -1,4 +1,4 @@ -package zetaclient +package interfaces import ( "crypto/ecdsa" diff --git a/zetaclient/signer_test.go b/zetaclient/interfaces/signer_test.go similarity index 98% rename from zetaclient/signer_test.go rename to zetaclient/interfaces/signer_test.go index 5c0849bb88..50867c4ffa 100644 --- a/zetaclient/signer_test.go +++ b/zetaclient/interfaces/signer_test.go @@ -1,4 +1,4 @@ -package zetaclient +package interfaces import ( "github.com/btcsuite/btcd/btcec" diff --git a/zetaclient/keys.go b/zetaclient/keys/keys.go similarity index 87% rename from zetaclient/keys.go rename to zetaclient/keys/keys.go index 77f424eac7..974743098f 100644 --- a/zetaclient/keys.go +++ b/zetaclient/keys/keys.go @@ -1,4 +1,4 @@ -package zetaclient +package keys import ( "bytes" @@ -14,9 +14,11 @@ import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/rs/zerolog/log" + "github.com/zeta-chain/zetacore/cmd" "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/common/cosmos" "github.com/zeta-chain/zetacore/zetaclient/config" + zetaerrors "github.com/zeta-chain/zetacore/zetaclient/errors" ) // Keys manages all the keys used by zeta client @@ -162,11 +164,11 @@ func (k *Keys) GetPubKeySet(password string) (common.PubKeySet, error) { s, err := cosmos.Bech32ifyPubKey(cosmos.Bech32PubKeyTypeAccPub, pK.PubKey()) if err != nil { - return pubkeySet, ErrBech32ifyPubKey + return pubkeySet, zetaerrors.ErrBech32ifyPubKey } pubkey, err := common.NewPubKey(s) if err != nil { - return pubkeySet, ErrNewPubKey + return pubkeySet, zetaerrors.ErrNewPubKey } pubkeySet.Secp256k1 = pubkey return pubkeySet, nil @@ -180,3 +182,15 @@ func (k *Keys) GetHotkeyPassword() string { } return "" } + +func SetupConfigForTest() { + config := cosmos.GetConfig() + config.SetBech32PrefixForAccount(cmd.Bech32PrefixAccAddr, cmd.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(cmd.Bech32PrefixValAddr, cmd.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(cmd.Bech32PrefixConsAddr, cmd.Bech32PrefixConsPub) + //config.SetCoinType(cmd.MetaChainCoinType) + config.SetFullFundraiserPath(cmd.ZetaChainHDPath) + sdk.SetCoinDenomRegex(func() string { + return cmd.DenomRegex + }) +} diff --git a/zetaclient/keys_test.go b/zetaclient/keys/keys_test.go similarity index 84% rename from zetaclient/keys_test.go rename to zetaclient/keys/keys_test.go index 74bb9b970b..5cbdeec5d5 100644 --- a/zetaclient/keys_test.go +++ b/zetaclient/keys/keys_test.go @@ -1,4 +1,4 @@ -package zetaclient +package keys import ( "bytes" @@ -14,7 +14,6 @@ import ( cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" hd "github.com/cosmos/cosmos-sdk/crypto/hd" cKeys "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" "github.com/zeta-chain/zetacore/zetaclient/config" . "gopkg.in/check.v1" @@ -37,18 +36,6 @@ var ( password = "password" ) -func SetupConfigForTest() { - config := cosmos.GetConfig() - config.SetBech32PrefixForAccount(cmd.Bech32PrefixAccAddr, cmd.Bech32PrefixAccPub) - config.SetBech32PrefixForValidator(cmd.Bech32PrefixValAddr, cmd.Bech32PrefixValPub) - config.SetBech32PrefixForConsensusNode(cmd.Bech32PrefixConsAddr, cmd.Bech32PrefixConsPub) - //config.SetCoinType(cmd.MetaChainCoinType) - config.SetFullFundraiserPath(cmd.ZetaChainHDPath) - types.SetCoinDenomRegex(func() string { - return cmd.DenomRegex - }) -} - const ( signerNameForTest = `jack` signerPasswordForTest = `password` diff --git a/zetaclient/klaytn_client.go b/zetaclient/klaytn_client.go deleted file mode 100644 index 0820f98fd3..0000000000 --- a/zetaclient/klaytn_client.go +++ /dev/null @@ -1,71 +0,0 @@ -package zetaclient - -import ( - "context" - "encoding/json" - "errors" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/rpc" -) - -type KlaytnClient struct { - c *rpc.Client -} - -type RPCHeader struct { - Hash *common.Hash `json:"hash"` - Number *hexutil.Big `json:"number"` - Time *hexutil.Big `json:"timestamp"` -} - -type RPCTransaction struct { - From *common.Address `json:"from"` - Input hexutil.Bytes `json:"input"` - To *common.Address `json:"to"` - Hash common.Hash `json:"hash"` - Value *hexutil.Big `json:"value"` -} - -type RPCBlock struct { - Hash *common.Hash `json:"hash"` - Transactions []RPCTransaction `json:"transactions"` -} - -func Dial(url string) (*KlaytnClient, error) { - c, err := rpc.Dial(url) - if err != nil { - return nil, err - } - return &KlaytnClient{c}, nil -} - -func (ec *KlaytnClient) BlockByNumber(ctx context.Context, number *big.Int) (*RPCBlock, error) { - return ec.getBlock(ctx, "klay_getBlockByNumber", toBlockNumArg(number), true) -} - -func (ec *KlaytnClient) getBlock(ctx context.Context, method string, args ...interface{}) (*RPCBlock, error) { - var raw json.RawMessage - err := ec.c.CallContext(ctx, &raw, method, args...) - if err != nil { - return nil, err - } else if len(raw) == 0 { - return nil, errors.New("not found") - } - - var block RPCBlock - if err := json.Unmarshal(raw, &block); err != nil { - return nil, err - } - - return &block, nil -} - -func toBlockNumArg(number *big.Int) string { - if number == nil { - return "latest" - } - return hexutil.EncodeBig(number) -} diff --git a/zetaclient/chainmetrics.go b/zetaclient/metrics/chainmetrics.go similarity index 76% rename from zetaclient/chainmetrics.go rename to zetaclient/metrics/chainmetrics.go index fbf2c6d8a6..df721d9d43 100644 --- a/zetaclient/chainmetrics.go +++ b/zetaclient/metrics/chainmetrics.go @@ -1,20 +1,19 @@ -package zetaclient +package metrics import ( "errors" "github.com/prometheus/client_golang/prometheus" - "github.com/zeta-chain/zetacore/zetaclient/metrics" ) const MetricGroup = "zetaclient" type ChainMetrics struct { chain string - metrics *metrics.Metrics + metrics *Metrics } -func NewChainMetrics(chain string, metrics *metrics.Metrics) *ChainMetrics { +func NewChainMetrics(chain string, metrics *Metrics) *ChainMetrics { return &ChainMetrics{ chain, metrics, @@ -22,7 +21,7 @@ func NewChainMetrics(chain string, metrics *metrics.Metrics) *ChainMetrics { } func (m *ChainMetrics) GetPromGauge(name string) (prometheus.Gauge, error) { - gauge, found := metrics.Gauges[m.buildGroupName(name)] + gauge, found := Gauges[m.buildGroupName(name)] if !found { return nil, errors.New("gauge not found") } @@ -35,7 +34,7 @@ func (m *ChainMetrics) RegisterPromGauge(name string, help string) error { } func (m *ChainMetrics) GetPromCounter(name string) (prometheus.Counter, error) { - if cnt, found := metrics.Counters[m.buildGroupName(name)]; found { + if cnt, found := Counters[m.buildGroupName(name)]; found { return cnt, nil } return nil, errors.New("counter not found") diff --git a/zetaclient/telemetry.go b/zetaclient/metrics/telemetry.go similarity index 96% rename from zetaclient/telemetry.go rename to zetaclient/metrics/telemetry.go index e43b0e6d99..07ca32ae35 100644 --- a/zetaclient/telemetry.go +++ b/zetaclient/metrics/telemetry.go @@ -1,4 +1,4 @@ -package zetaclient +package metrics import ( "context" @@ -13,7 +13,6 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/zeta-chain/zetacore/common" - "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/types" ) @@ -28,7 +27,7 @@ type TelemetryServer struct { lastStartTimestamp time.Time status types.Status ipAddress string - hotKeyBurnRate *metrics.BurnRate + HotKeyBurnRate *BurnRate } // NewTelemetryServer should only listen to the loopback @@ -37,7 +36,7 @@ func NewTelemetryServer() *TelemetryServer { logger: log.With().Str("module", "http").Logger(), lastScannedBlockNumber: make(map[int64]uint64), lastStartTimestamp: time.Now(), - hotKeyBurnRate: metrics.NewBurnRate(100), + HotKeyBurnRate: NewBurnRate(100), } s := &http.Server{ Addr: ":8123", @@ -114,7 +113,7 @@ func (t *TelemetryServer) SetNumberOfUTXOs(numberOfUTXOs int) { func (t *TelemetryServer) AddFeeEntry(block int64, amount int64) { t.mu.Lock() - err := t.hotKeyBurnRate.AddFee(amount, block) + err := t.HotKeyBurnRate.AddFee(amount, block) if err != nil { log.Error().Err(err).Msg("failed to update hotkey burn rate") } @@ -251,5 +250,5 @@ func (t *TelemetryServer) hotKeyFeeBurnRate(w http.ResponseWriter, _ *http.Reque w.WriteHeader(http.StatusOK) t.mu.Lock() defer t.mu.Unlock() - fmt.Fprintf(w, "%v", t.hotKeyBurnRate.GetBurnRate()) + fmt.Fprintf(w, "%v", t.HotKeyBurnRate.GetBurnRate()) } diff --git a/zetaclient/misc.go b/zetaclient/misc.go deleted file mode 100644 index 550c3461a3..0000000000 --- a/zetaclient/misc.go +++ /dev/null @@ -1,9 +0,0 @@ -package zetaclient - -// returns the maximum of two ints -func MaxInt(a int, b int) int { - if a < b { - return b - } - return a -} diff --git a/zetaclient/out_tx_processor_manager.go b/zetaclient/outtxprocessor/out_tx_processor_manager.go similarity index 78% rename from zetaclient/out_tx_processor_manager.go rename to zetaclient/outtxprocessor/out_tx_processor_manager.go index 5d6bb35110..b816c99dea 100644 --- a/zetaclient/out_tx_processor_manager.go +++ b/zetaclient/outtxprocessor/out_tx_processor_manager.go @@ -1,4 +1,4 @@ -package zetaclient +package outtxprocessor import ( "fmt" @@ -8,7 +8,7 @@ import ( "github.com/rs/zerolog" ) -type OutTxProcessorManager struct { +type Processor struct { outTxStartTime map[string]time.Time outTxEndTime map[string]time.Time outTxActive map[string]struct{} @@ -17,8 +17,8 @@ type OutTxProcessorManager struct { numActiveProcessor int64 } -func NewOutTxProcessorManager(logger zerolog.Logger) *OutTxProcessorManager { - return &OutTxProcessorManager{ +func NewOutTxProcessorManager(logger zerolog.Logger) *Processor { + return &Processor{ outTxStartTime: make(map[string]time.Time), outTxEndTime: make(map[string]time.Time), outTxActive: make(map[string]struct{}), @@ -28,7 +28,7 @@ func NewOutTxProcessorManager(logger zerolog.Logger) *OutTxProcessorManager { } } -func (outTxMan *OutTxProcessorManager) StartTryProcess(outTxID string) { +func (outTxMan *Processor) StartTryProcess(outTxID string) { outTxMan.mu.Lock() defer outTxMan.mu.Unlock() outTxMan.outTxStartTime[outTxID] = time.Now() @@ -37,7 +37,7 @@ func (outTxMan *OutTxProcessorManager) StartTryProcess(outTxID string) { outTxMan.Logger.Info().Msgf("StartTryProcess %s, numActiveProcessor %d", outTxID, outTxMan.numActiveProcessor) } -func (outTxMan *OutTxProcessorManager) EndTryProcess(outTxID string) { +func (outTxMan *Processor) EndTryProcess(outTxID string) { outTxMan.mu.Lock() defer outTxMan.mu.Unlock() outTxMan.outTxEndTime[outTxID] = time.Now() @@ -46,14 +46,14 @@ func (outTxMan *OutTxProcessorManager) EndTryProcess(outTxID string) { outTxMan.Logger.Info().Msgf("EndTryProcess %s, numActiveProcessor %d, time elapsed %s", outTxID, outTxMan.numActiveProcessor, time.Since(outTxMan.outTxStartTime[outTxID])) } -func (outTxMan *OutTxProcessorManager) IsOutTxActive(outTxID string) bool { +func (outTxMan *Processor) IsOutTxActive(outTxID string) bool { outTxMan.mu.Lock() defer outTxMan.mu.Unlock() _, found := outTxMan.outTxActive[outTxID] return found } -func (outTxMan *OutTxProcessorManager) TimeInTryProcess(outTxID string) time.Duration { +func (outTxMan *Processor) TimeInTryProcess(outTxID string) time.Duration { outTxMan.mu.Lock() defer outTxMan.mu.Unlock() if _, found := outTxMan.outTxActive[outTxID]; found { diff --git a/zetaclient/zeta_supply_checker.go b/zetaclient/supplychecker/zeta_supply_checker.go similarity index 89% rename from zetaclient/zeta_supply_checker.go rename to zetaclient/supplychecker/zeta_supply_checker.go index 3a94152578..1d560794e5 100644 --- a/zetaclient/zeta_supply_checker.go +++ b/zetaclient/supplychecker/zeta_supply_checker.go @@ -1,8 +1,14 @@ -package zetaclient +package supplychecker import ( "fmt" + "github.com/zeta-chain/zetacore/zetaclient/bitcoin" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/zetabridge" + + "github.com/zeta-chain/zetacore/zetaclient/evm" + sdkmath "cosmossdk.io/math" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" @@ -11,13 +17,14 @@ import ( "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/x/crosschain/types" "github.com/zeta-chain/zetacore/zetaclient/config" + clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" ) type ZetaSupplyChecker struct { cfg *config.Config evmClient map[int64]*ethclient.Client - zetaClient *ZetaCoreBridge - ticker *DynamicTicker + zetaClient *zetabridge.ZetaCoreBridge + ticker *clienttypes.DynamicTicker stop chan struct{} logger zerolog.Logger externalEvmChain []common.Chain @@ -25,8 +32,8 @@ type ZetaSupplyChecker struct { genesisSupply sdkmath.Int } -func NewZetaSupplyChecker(cfg *config.Config, zetaClient *ZetaCoreBridge, logger zerolog.Logger) (ZetaSupplyChecker, error) { - dynamicTicker, err := NewDynamicTicker("ZETASupplyTicker", 15) +func NewZetaSupplyChecker(cfg *config.Config, zetaClient *zetabridge.ZetaCoreBridge, logger zerolog.Logger) (ZetaSupplyChecker, error) { + dynamicTicker, err := clienttypes.NewDynamicTicker("ZETASupplyTicker", 15) if err != nil { return ZetaSupplyChecker{}, err } @@ -105,7 +112,7 @@ func (zs *ZetaSupplyChecker) CheckZetaTokenSupply() error { } zetaTokenAddressString := externalEvmChainConfig.ZetaTokenContractAddress zetaTokenAddress := ethcommon.HexToAddress(zetaTokenAddressString) - zetatokenNonEth, err := FetchZetaZetaNonEthTokenContract(zetaTokenAddress, zs.evmClient[chain.ChainId]) + zetatokenNonEth, err := evm.FetchZetaZetaNonEthTokenContract(zetaTokenAddress, zs.evmClient[chain.ChainId]) if err != nil { return err } @@ -127,7 +134,7 @@ func (zs *ZetaSupplyChecker) CheckZetaTokenSupply() error { } ethConnectorAddressString := ethConfig.ConnectorContractAddress ethConnectorAddress := ethcommon.HexToAddress(ethConnectorAddressString) - ethConnectorContract, err := FetchConnectorContractEth(ethConnectorAddress, zs.evmClient[zs.ethereumChain.ChainId]) + ethConnectorContract, err := evm.FetchConnectorContractEth(ethConnectorAddress, zs.evmClient[zs.ethereumChain.ChainId]) if err != nil { return err } @@ -168,7 +175,7 @@ type ZetaSupplyCheckLogs struct { } func (z ZetaSupplyCheckLogs) LogOutput() { - output, err := PrettyPrintStruct(z) + output, err := bitcoin.PrettyPrintStruct(z) if err != nil { z.Logger.Error().Err(err).Msgf("error pretty printing struct") } @@ -242,7 +249,7 @@ func (zs *ZetaSupplyChecker) GetPendingCCTXInTransit(receivingChains []common.Ch } } - trackers, err := zs.zetaClient.GetAllOutTxTrackerByChain(chain.ChainId, Ascending) + trackers, err := zs.zetaClient.GetAllOutTxTrackerByChain(chain.ChainId, interfaces.Ascending) if err != nil { continue } diff --git a/zetaclient/zeta_supply_checker_test.go b/zetaclient/supplychecker/zeta_supply_checker_test.go similarity index 85% rename from zetaclient/zeta_supply_checker_test.go rename to zetaclient/supplychecker/zeta_supply_checker_test.go index 020477ccd4..c2b788cff1 100644 --- a/zetaclient/zeta_supply_checker_test.go +++ b/zetaclient/supplychecker/zeta_supply_checker_test.go @@ -1,4 +1,4 @@ -package zetaclient_test +package supplychecker import ( "os" @@ -7,7 +7,6 @@ import ( sdkmath "cosmossdk.io/math" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" - "github.com/zeta-chain/zetacore/zetaclient" ) func MustNewIntFromString(val string) sdkmath.Int { @@ -47,7 +46,7 @@ func TestZetaSupplyChecker_ValidateZetaSupply(t *testing.T) { for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { logger := zerolog.New(os.Stdout).With().Timestamp().Logger() - tc.validate(t, zetaclient.ValidateZetaSupply(logger, tc.abortedTxAmount, tc.zetaInTransit, tc.genesisAmounts, tc.externalChainTotalSupply, tc.zetaTokenSupplyOnNode, tc.ethLockedAmount)) + tc.validate(t, ValidateZetaSupply(logger, tc.abortedTxAmount, tc.zetaInTransit, tc.genesisAmounts, tc.externalChainTotalSupply, tc.zetaTokenSupplyOnNode, tc.ethLockedAmount)) }) } } diff --git a/zetaclient/tss_signer.go b/zetaclient/tss/tss_signer.go similarity index 95% rename from zetaclient/tss_signer.go rename to zetaclient/tss/tss_signer.go index c286622d42..8fba8315f3 100644 --- a/zetaclient/tss_signer.go +++ b/zetaclient/tss/tss_signer.go @@ -1,4 +1,4 @@ -package zetaclient +package tss import ( "bytes" @@ -12,6 +12,9 @@ import ( "strings" "time" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/keys" + "github.com/binance-chain/tss-lib/ecdsa/keygen" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcutil" @@ -29,21 +32,21 @@ import ( zcommon "github.com/zeta-chain/zetacore/common/cosmos" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/config" - "github.com/zeta-chain/zetacore/zetaclient/metrics" + zetametrics "github.com/zeta-chain/zetacore/zetaclient/metrics" ) const ( envFlagPostBlame = "POST_BLAME" ) -type TSSKey struct { +type Key struct { PubkeyInBytes []byte // FIXME: compressed pubkey? PubkeyInBech32 string // FIXME: same above AddressInHex string } -func NewTSSKey(pk string) (*TSSKey, error) { - TSSKey := &TSSKey{ +func NewTSSKey(pk string) (*Key, error) { + TSSKey := &Key{ PubkeyInBech32: pk, } pubkey, err := zcommon.GetPubKeyFromBech32(zcommon.Bech32PubKeyTypeAccPub, pk) @@ -60,17 +63,17 @@ func NewTSSKey(pk string) (*TSSKey, error) { return TSSKey, nil } -var _ TSSSigner = (*TSS)(nil) +var _ interfaces.TSSSigner = (*TSS)(nil) // TSS is a struct that holds the server and the keys for TSS type TSS struct { Server *tss.TssServer - Keys map[string]*TSSKey // PubkeyInBech32 => TSSKey + Keys map[string]*Key // PubkeyInBech32 => TSSKey CurrentPubkey string logger zerolog.Logger Signers []string - CoreBridge ZetaCoreBridger - Metrics *ChainMetrics + CoreBridge interfaces.ZetaCoreBridger + Metrics *zetametrics.ChainMetrics // TODO: support multiple Bitcoin network, not just one network // https://github.com/zeta-chain/node/issues/1397 @@ -83,9 +86,9 @@ func NewTSS( privkey tmcrypto.PrivKey, preParams *keygen.LocalPreParams, cfg *config.Config, - bridge ZetaCoreBridger, + bridge interfaces.ZetaCoreBridger, tssHistoricalList []observertypes.TSS, - metrics *metrics.Metrics, + metrics *zetametrics.Metrics, bitcoinChainID int64, tssPassword string, hotkeyPassword string, @@ -96,7 +99,7 @@ func NewTSS( } newTss := TSS{ Server: server, - Keys: make(map[string]*TSSKey), + Keys: make(map[string]*Key), CurrentPubkey: cfg.CurrentTssPubkey, logger: log.With().Str("module", "tss_signer").Logger(), CoreBridge: bridge, @@ -107,7 +110,7 @@ func NewTSS( if err != nil { return nil, err } - _, pubkeyInBech32, err := GetKeyringKeybase(cfg, hotkeyPassword) + _, pubkeyInBech32, err := keys.GetKeyringKeybase(cfg, hotkeyPassword) if err != nil { return nil, err } @@ -424,8 +427,8 @@ func (tss *TSS) InsertPubKey(pk string) error { return nil } -func (tss *TSS) RegisterMetrics(metrics *metrics.Metrics) error { - tss.Metrics = NewChainMetrics("tss", metrics) +func (tss *TSS) RegisterMetrics(metrics *zetametrics.Metrics) error { + tss.Metrics = zetametrics.NewChainMetrics("tss", metrics) keygenRes, err := tss.CoreBridge.GetKeyGen() if err != nil { return err @@ -622,3 +625,8 @@ func getKeyAddrBTCWitnessPubkeyHash(tssPubkey string, chainID int64) (*btcutil.A } return addr, nil } + +func IsEnvFlagEnabled(flag string) bool { + value := os.Getenv(flag) + return value == "true" || value == "1" +} diff --git a/zetaclient/tss_signer_test.go b/zetaclient/tss/tss_signer_test.go similarity index 92% rename from zetaclient/tss_signer_test.go rename to zetaclient/tss/tss_signer_test.go index 3fffc9277d..3a78c4a4d0 100644 --- a/zetaclient/tss_signer_test.go +++ b/zetaclient/tss/tss_signer_test.go @@ -1,10 +1,12 @@ -package zetaclient +package tss import ( "fmt" "os" "testing" + "github.com/zeta-chain/zetacore/zetaclient/keys" + "github.com/cosmos/cosmos-sdk/testutil/testdata" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" @@ -39,7 +41,7 @@ func Test_LoadTssFilesFromDirectory(t *testing.T) { assert.NoError(t, err) tss := TSS{ logger: zerolog.New(os.Stdout), - Keys: map[string]*TSSKey{}, + Keys: map[string]*Key{}, CurrentPubkey: "", } err = tss.LoadTssFilesFromDirectory(tempdir) @@ -50,7 +52,7 @@ func Test_LoadTssFilesFromDirectory(t *testing.T) { } func GenerateKeyshareFiles(n int, dir string) error { - SetupConfigForTest() + keys.SetupConfigForTest() err := os.Chdir(dir) if err != nil { return err diff --git a/zetaclient/tx.go b/zetaclient/tx.go deleted file mode 100644 index f422a3e026..0000000000 --- a/zetaclient/tx.go +++ /dev/null @@ -1,195 +0,0 @@ -package zetaclient - -import ( - "fmt" - "strings" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/authz" - "github.com/zeta-chain/go-tss/blame" - "github.com/zeta-chain/zetacore/common" - "github.com/zeta-chain/zetacore/x/crosschain/types" - observerTypes "github.com/zeta-chain/zetacore/x/observer/types" - "github.com/zeta-chain/zetacore/zetaclient/config" -) - -const ( - // DefaultGasLimit is the default gas limit used for broadcasting txs - DefaultGasLimit = 200_000 - - // PostGasPriceGasLimit is the gas limit for voting new gas price - PostGasPriceGasLimit = 1_500_000 - - // AddTxHashToOutTxTrackerGasLimit is the gas limit for adding tx hash to out tx tracker - AddTxHashToOutTxTrackerGasLimit = 200_000 - - // PostBlameDataGasLimit is the gas limit for voting on blames - PostBlameDataGasLimit = 200_000 - - // DefaultRetryCount is the number of retries for broadcasting a tx - DefaultRetryCount = 5 - - // ExtendedRetryCount is an extended number of retries for broadcasting a tx, used in keygen operations - ExtendedRetryCount = 15 - - // DefaultRetryInterval is the interval between retries in seconds - DefaultRetryInterval = 5 -) - -func (b *ZetaCoreBridge) WrapMessageWithAuthz(msg sdk.Msg) (sdk.Msg, AuthZSigner, error) { - msgURL := sdk.MsgTypeURL(msg) - - // verify message validity - if err := msg.ValidateBasic(); err != nil { - return nil, AuthZSigner{}, fmt.Errorf("%s invalid msg | %s", msgURL, err.Error()) - } - - authzSigner := GetSigner(msgURL) - authzMessage := authz.NewMsgExec(authzSigner.GranteeAddress, []sdk.Msg{msg}) - return &authzMessage, authzSigner, nil -} - -func (b *ZetaCoreBridge) PostGasPrice(chain common.Chain, gasPrice uint64, supply string, blockNum uint64) (string, error) { - // double the gas price to avoid gas price spike - gasPrice = gasPrice * 2 - signerAddress := b.keys.GetOperatorAddress().String() - msg := types.NewMsgGasPriceVoter(signerAddress, chain.ChainId, gasPrice, supply, blockNum) - - authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) - if err != nil { - return "", err - } - - for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := b.Broadcast(PostGasPriceGasLimit, authzMsg, authzSigner) - if err == nil { - return zetaTxHash, nil - } - b.logger.Debug().Err(err).Msgf("PostGasPrice broadcast fail | Retry count : %d", i+1) - time.Sleep(DefaultRetryInterval * time.Second) - } - - return "", fmt.Errorf("post gasprice failed after %d retries", DefaultRetryInterval) -} - -func (b *ZetaCoreBridge) AddTxHashToOutTxTracker( - chainID int64, - nonce uint64, - txHash string, - proof *common.Proof, - blockHash string, - txIndex int64, -) (string, error) { - // don't report if the tracker already contains the txHash - tracker, err := b.GetOutTxTracker(common.Chain{ChainId: chainID}, nonce) - if err == nil { - for _, hash := range tracker.HashList { - if strings.EqualFold(hash.TxHash, txHash) { - return "", nil - } - } - } - signerAddress := b.keys.GetOperatorAddress().String() - msg := types.NewMsgAddToOutTxTracker(signerAddress, chainID, nonce, txHash, proof, blockHash, txIndex) - - authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) - if err != nil { - return "", err - } - - zetaTxHash, err := b.Broadcast(AddTxHashToOutTxTrackerGasLimit, authzMsg, authzSigner) - if err != nil { - return "", err - } - return zetaTxHash, nil -} - -func (b *ZetaCoreBridge) SetTSS(tssPubkey string, keyGenZetaHeight int64, status common.ReceiveStatus) (string, error) { - signerAddress := b.keys.GetOperatorAddress().String() - msg := types.NewMsgCreateTSSVoter(signerAddress, tssPubkey, keyGenZetaHeight, status) - - authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) - if err != nil { - return "", err - } - - zetaTxHash := "" - for i := 0; i <= DefaultRetryCount; i++ { - zetaTxHash, err = b.Broadcast(DefaultGasLimit, authzMsg, authzSigner) - if err == nil { - return zetaTxHash, nil - } - b.logger.Debug().Err(err).Msgf("SetTSS broadcast fail | Retry count : %d", i+1) - time.Sleep(DefaultRetryInterval * time.Second) - } - - return "", fmt.Errorf("set tss failed | err %s", err.Error()) -} - -func (b *ZetaCoreBridge) ConfigUpdater(cfg *config.Config) { - b.logger.Info().Msg("ConfigUpdater started") - ticker := time.NewTicker(time.Duration(cfg.ConfigUpdateTicker) * time.Second) - for { - select { - case <-ticker.C: - b.logger.Debug().Msg("Running Updater") - err := b.UpdateConfigFromCore(cfg, false) - if err != nil { - b.logger.Err(err).Msg("ConfigUpdater failed to update config") - } - case <-b.stop: - b.logger.Info().Msg("ConfigUpdater stopped") - return - } - } -} - -func (b *ZetaCoreBridge) PostBlameData(blame *blame.Blame, chainID int64, index string) (string, error) { - signerAddress := b.keys.GetOperatorAddress().String() - zetaBlame := observerTypes.Blame{ - Index: index, - FailureReason: blame.FailReason, - Nodes: observerTypes.ConvertNodes(blame.BlameNodes), - } - msg := observerTypes.NewMsgAddBlameVoteMsg(signerAddress, chainID, zetaBlame) - - authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) - if err != nil { - return "", err - } - - var gasLimit uint64 = PostBlameDataGasLimit - - for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := b.Broadcast(gasLimit, authzMsg, authzSigner) - if err == nil { - return zetaTxHash, nil - } - b.logger.Error().Err(err).Msgf("PostBlame broadcast fail | Retry count : %d", i+1) - time.Sleep(DefaultRetryInterval * time.Second) - } - return "", fmt.Errorf("post blame data failed after %d retries", DefaultRetryCount) -} - -func (b *ZetaCoreBridge) PostAddBlockHeader(chainID int64, blockHash []byte, height int64, header common.HeaderData) (string, error) { - signerAddress := b.keys.GetOperatorAddress().String() - - msg := observerTypes.NewMsgAddBlockHeader(signerAddress, chainID, blockHash, height, header) - - authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) - if err != nil { - return "", err - } - - var gasLimit uint64 = DefaultGasLimit - for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := b.Broadcast(gasLimit, authzMsg, authzSigner) - if err == nil { - return zetaTxHash, nil - } - b.logger.Error().Err(err).Msgf("PostAddBlockHeader broadcast fail | Retry count : %d", i+1) - time.Sleep(DefaultRetryInterval * time.Second) - } - return "", fmt.Errorf("post add block header failed after %d retries", DefaultRetryCount) -} diff --git a/zetaclient/tx_vote_inbound.go b/zetaclient/tx_vote_inbound.go deleted file mode 100644 index 1e5807474f..0000000000 --- a/zetaclient/tx_vote_inbound.go +++ /dev/null @@ -1,153 +0,0 @@ -package zetaclient - -import ( - "fmt" - "strings" - "time" - - "cosmossdk.io/math" - "github.com/zeta-chain/zetacore/common" - - "github.com/pkg/errors" - - "github.com/zeta-chain/zetacore/x/crosschain/types" -) - -const ( - // PostVoteInboundGasLimit is the gas limit for voting on observed inbound tx - PostVoteInboundGasLimit = 400_000 - - // PostVoteInboundExecutionGasLimit is the gas limit for voting on observed inbound tx and executing it - PostVoteInboundExecutionGasLimit = 4_000_000 - - // PostVoteInboundMessagePassingExecutionGasLimit is the gas limit for voting on, and executing ,observed inbound tx related to message passing (coin_type == zeta) - PostVoteInboundMessagePassingExecutionGasLimit = 1_000_000 - - // MonitorVoteInboundTxResultInterval is the interval between retries for monitoring tx result in seconds - MonitorVoteInboundTxResultInterval = 5 - - // MonitorVoteInboundTxResultRetryCount is the number of retries to fetch monitoring tx result - MonitorVoteInboundTxResultRetryCount = 20 -) - -// GetInBoundVoteMessage returns a new MsgVoteOnObservedInboundTx -func GetInBoundVoteMessage( - sender string, - senderChain int64, - txOrigin string, - receiver string, - receiverChain int64, - amount math.Uint, - message string, - inTxHash string, - inBlockHeight uint64, - gasLimit uint64, - coinType common.CoinType, - asset string, - signerAddress string, - eventIndex uint, -) *types.MsgVoteOnObservedInboundTx { - msg := types.NewMsgVoteOnObservedInboundTx( - signerAddress, - sender, - senderChain, - txOrigin, - receiver, - receiverChain, - amount, - message, - inTxHash, - inBlockHeight, - gasLimit, - coinType, - asset, - eventIndex, - ) - return msg -} - -// PostVoteInbound posts a vote on an observed inbound tx -// retryGasLimit is the gas limit used to resend the tx if it fails because of insufficient gas -// it is used when the ballot is finalized and the inbound tx needs to be processed -func (b *ZetaCoreBridge) PostVoteInbound(gasLimit, retryGasLimit uint64, msg *types.MsgVoteOnObservedInboundTx) (string, string, error) { - authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) - if err != nil { - return "", "", err - } - - // don't post send if has already voted before - ballotIndex := msg.Digest() - hasVoted, err := b.HasVoted(ballotIndex, msg.Creator) - if err != nil { - return "", ballotIndex, errors.Wrapf(err, "PostVoteInbound: unable to check if already voted for ballot %s voter %s", ballotIndex, msg.Creator) - } - if hasVoted { - return "", ballotIndex, nil - } - - for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := b.Broadcast(gasLimit, authzMsg, authzSigner) - if err == nil { - // monitor the result of the transaction and resend if necessary - go b.MonitorVoteInboundTxResult(zetaTxHash, retryGasLimit, msg) - - return zetaTxHash, ballotIndex, nil - } - b.logger.Debug().Err(err).Msgf("PostVoteInbound broadcast fail | Retry count : %d", i+1) - time.Sleep(DefaultRetryInterval * time.Second) - } - return "", ballotIndex, fmt.Errorf("post send failed after %d retries", DefaultRetryInterval) -} - -// MonitorVoteInboundTxResult monitors the result of a vote inbound tx -// retryGasLimit is the gas limit used to resend the tx if it fails because of insufficient gas -// if retryGasLimit is 0, the tx is not resent -func (b *ZetaCoreBridge) MonitorVoteInboundTxResult(zetaTxHash string, retryGasLimit uint64, msg *types.MsgVoteOnObservedInboundTx) { - var lastErr error - - for i := 0; i < MonitorVoteInboundTxResultRetryCount; i++ { - time.Sleep(MonitorVoteInboundTxResultInterval * time.Second) - - // query tx result from ZetaChain - txResult, err := b.QueryTxResult(zetaTxHash) - - if err == nil { - if strings.Contains(txResult.RawLog, "failed to execute message") { - // the inbound vote tx shouldn't fail to execute - // this shouldn't happen - b.logger.Error().Msgf( - "MonitorInboundTxResult: failed to execute vote, txHash: %s, log %s", zetaTxHash, txResult.RawLog, - ) - } else if strings.Contains(txResult.RawLog, "out of gas") { - // if the tx fails with an out of gas error, resend the tx with more gas if retryGasLimit > 0 - b.logger.Debug().Msgf( - "MonitorInboundTxResult: out of gas, txHash: %s, log %s", zetaTxHash, txResult.RawLog, - ) - if retryGasLimit > 0 { - // new retryGasLimit set to 0 to prevent reentering this function - _, _, err := b.PostVoteInbound(retryGasLimit, 0, msg) - if err != nil { - b.logger.Error().Err(err).Msgf( - "MonitorInboundTxResult: failed to resend tx, txHash: %s, log %s", zetaTxHash, txResult.RawLog, - ) - } else { - b.logger.Info().Msgf( - "MonitorInboundTxResult: successfully resent tx, txHash: %s, log %s", zetaTxHash, txResult.RawLog, - ) - } - } - } else { - b.logger.Debug().Msgf( - "MonitorInboundTxResult: successful txHash %s, log %s", zetaTxHash, txResult.RawLog, - ) - } - return - } - lastErr = err - } - - b.logger.Error().Err(lastErr).Msgf( - "MonitorInboundTxResult: unable to query tx result for txHash %s, err %s", zetaTxHash, lastErr.Error(), - ) - return -} diff --git a/zetaclient/tx_vote_outbound.go b/zetaclient/tx_vote_outbound.go deleted file mode 100644 index bcf79c69f6..0000000000 --- a/zetaclient/tx_vote_outbound.go +++ /dev/null @@ -1,154 +0,0 @@ -package zetaclient - -import ( - "fmt" - "math/big" - "strings" - "time" - - "cosmossdk.io/math" - "github.com/pkg/errors" - "github.com/zeta-chain/zetacore/common" - "github.com/zeta-chain/zetacore/x/crosschain/types" -) - -const ( - // PostVoteOutboundGasLimit is the gas limit for voting on observed outbound tx - PostVoteOutboundGasLimit = 400_000 - - // PostVoteOutboundRevertGasLimit is the gas limit for voting on observed outbound tx for revert (when outbound fails) - // The value needs to be higher because reverting implies interacting with the EVM to perform swaps for the gas token - PostVoteOutboundRevertGasLimit = 1_500_000 - - // MonitorVoteOutboundTxResultInterval is the interval between retries for monitoring tx result in seconds - MonitorVoteOutboundTxResultInterval = 5 - - // MonitorVoteOutboundTxResultRetryCount is the number of retries to fetch monitoring tx result - MonitorVoteOutboundTxResultRetryCount = 20 -) - -// PostVoteOutbound posts a vote on an observed outbound tx -func (b *ZetaCoreBridge) PostVoteOutbound( - sendHash string, - outTxHash string, - outBlockHeight uint64, - outTxGasUsed uint64, - outTxEffectiveGasPrice *big.Int, - outTxEffectiveGasLimit uint64, - amount *big.Int, - status common.ReceiveStatus, - chain common.Chain, - nonce uint64, - coinType common.CoinType, -) (string, string, error) { - signerAddress := b.keys.GetOperatorAddress().String() - msg := types.NewMsgVoteOnObservedOutboundTx( - signerAddress, - sendHash, - outTxHash, - outBlockHeight, - outTxGasUsed, - math.NewIntFromBigInt(outTxEffectiveGasPrice), - outTxEffectiveGasLimit, - math.NewUintFromBigInt(amount), - status, - chain.ChainId, - nonce, - coinType, - ) - - // when an outbound fails and a revert is required, the gas limit needs to be higher - // this is because the revert tx needs to interact with the EVM to perform swaps for the gas token - // the higher gas limit is only necessary when the vote is finalized and the outbound is processed - // therefore we use a retryGasLimit with a higher value to resend the tx if it fails (when the vote is finalized) - retryGasLimit := uint64(0) - if msg.Status == common.ReceiveStatus_Failed { - retryGasLimit = PostVoteOutboundRevertGasLimit - } - - return b.PostVoteOutboundFromMsg(PostVoteOutboundGasLimit, retryGasLimit, msg) -} - -// PostVoteOutboundFromMsg posts a vote on an observed outbound tx from a MsgVoteOnObservedOutboundTx -func (b *ZetaCoreBridge) PostVoteOutboundFromMsg(gasLimit, retryGasLimit uint64, msg *types.MsgVoteOnObservedOutboundTx) (string, string, error) { - authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) - if err != nil { - return "", "", err - } - - // don't post confirmation if has already voted before - ballotIndex := msg.Digest() - hasVoted, err := b.HasVoted(ballotIndex, msg.Creator) - if err != nil { - return "", ballotIndex, errors.Wrapf(err, "PostVoteOutbound: unable to check if already voted for ballot %s voter %s", ballotIndex, msg.Creator) - } - if hasVoted { - return "", ballotIndex, nil - } - for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := b.Broadcast(gasLimit, authzMsg, authzSigner) - if err == nil { - // monitor the result of the transaction and resend if necessary - go b.MonitorVoteOutboundTxResult(zetaTxHash, retryGasLimit, msg) - - return zetaTxHash, ballotIndex, nil - } - b.logger.Debug().Err(err).Msgf("PostVoteOutbound broadcast fail | Retry count : %d", i+1) - time.Sleep(DefaultRetryInterval * time.Second) - } - return "", ballotIndex, fmt.Errorf("post receive failed after %d retries", DefaultRetryCount) -} - -// MonitorVoteOutboundTxResult monitors the result of a vote outbound tx -// retryGasLimit is the gas limit used to resend the tx if it fails because of insufficient gas -// if retryGasLimit is 0, the tx is not resent -func (b *ZetaCoreBridge) MonitorVoteOutboundTxResult(zetaTxHash string, retryGasLimit uint64, msg *types.MsgVoteOnObservedOutboundTx) { - var lastErr error - - for i := 0; i < MonitorVoteOutboundTxResultRetryCount; i++ { - time.Sleep(MonitorVoteOutboundTxResultInterval * time.Second) - - // query tx result from ZetaChain - txResult, err := b.QueryTxResult(zetaTxHash) - - if err == nil { - if strings.Contains(txResult.RawLog, "failed to execute message") { - // the inbound vote tx shouldn't fail to execute - // this shouldn't happen - b.logger.Error().Msgf( - "MonitorVoteOutboundTxResult: failed to execute vote, txHash: %s, log %s", zetaTxHash, txResult.RawLog, - ) - } else if strings.Contains(txResult.RawLog, "out of gas") { - // if the tx fails with an out of gas error, resend the tx with more gas if retryGasLimit > 0 - b.logger.Debug().Msgf( - "MonitorVoteOutboundTxResult: out of gas, txHash: %s, log %s", zetaTxHash, txResult.RawLog, - ) - if retryGasLimit > 0 { - // new retryGasLimit set to 0 to prevent reentering this function - _, _, err := b.PostVoteOutboundFromMsg(retryGasLimit, 0, msg) - - if err != nil { - b.logger.Error().Err(err).Msgf( - "MonitorVoteOutboundTxResult: failed to resend tx, txHash: %s, log %s", zetaTxHash, txResult.RawLog, - ) - } else { - b.logger.Info().Msgf( - "MonitorVoteOutboundTxResult: successfully resent tx, txHash: %s, log %s", zetaTxHash, txResult.RawLog, - ) - } - } - } else { - b.logger.Debug().Msgf( - "MonitorVoteOutboundTxResult: successful txHash %s, log %s", zetaTxHash, txResult.RawLog, - ) - } - return - } - lastErr = err - } - - b.logger.Error().Err(lastErr).Msgf( - "MonitorVoteOutboundTxResult: unable to query tx result for txHash %s, err %s", zetaTxHash, lastErr.Error(), - ) - return -} diff --git a/zetaclient/types/dynamic_ticker.go b/zetaclient/types/dynamic_ticker.go new file mode 100644 index 0000000000..49434e03ea --- /dev/null +++ b/zetaclient/types/dynamic_ticker.go @@ -0,0 +1,44 @@ +package types + +import ( + "fmt" + "time" + + "github.com/rs/zerolog" +) + +type DynamicTicker struct { + name string + interval uint64 + impl *time.Ticker +} + +func NewDynamicTicker(name string, interval uint64) (*DynamicTicker, error) { + if interval <= 0 { + return nil, fmt.Errorf("non-positive ticker interval %d for %s", interval, name) + } + + return &DynamicTicker{ + name: name, + interval: interval, + impl: time.NewTicker(time.Duration(interval) * time.Second), + }, nil +} + +func (t *DynamicTicker) C() <-chan time.Time { + return t.impl.C +} + +func (t *DynamicTicker) UpdateInterval(newInterval uint64, logger zerolog.Logger) { + if newInterval > 0 && t.interval != newInterval { + t.impl.Stop() + oldInterval := t.interval + t.interval = newInterval + t.impl = time.NewTicker(time.Duration(t.interval) * time.Second) + logger.Info().Msgf("%s ticker interval changed from %d to %d", t.name, oldInterval, newInterval) + } +} + +func (t *DynamicTicker) Stop() { + t.impl.Stop() +} diff --git a/zetaclient/utils.go b/zetaclient/utils.go deleted file mode 100644 index 06f701a20d..0000000000 --- a/zetaclient/utils.go +++ /dev/null @@ -1,326 +0,0 @@ -package zetaclient - -import ( - "bytes" - "context" - "encoding/base64" - "encoding/hex" - "encoding/json" - "fmt" - "math" - "math/big" - "os" - "strings" - "time" - - sdkmath "cosmossdk.io/math" - "github.com/btcsuite/btcd/blockchain" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" - ethcommon "github.com/ethereum/go-ethereum/common" - ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/pkg/errors" - "github.com/rs/zerolog" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" - "github.com/zeta-chain/zetacore/common" - "github.com/zeta-chain/zetacore/x/crosschain/types" - clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" -) - -const ( - satoshiPerBitcoin = 1e8 - bytesPerKB = 1000 - bytesEmptyTx = 10 // an empty tx is about 10 bytes - bytesPerInput = 41 // each input is about 41 bytes - bytesPerOutput = 31 // each output is about 31 bytes - bytes1stWitness = 110 // the 1st witness incurs about 110 bytes and it may vary - bytesPerWitness = 108 // each additional witness incurs about 108 bytes and it may vary -) - -var ( - BtcOutTxBytesDepositor uint64 - BtcOutTxBytesWithdrawer uint64 - BtcDepositorFeeMin float64 -) - -func init() { - BtcOutTxBytesDepositor = SegWitTxSizeDepositor() // 68vB, the outtx size incurred by the depositor - BtcOutTxBytesWithdrawer = SegWitTxSizeWithdrawer() // 171vB, the outtx size incurred by the withdrawer - - // depositor fee calculation is based on a fixed fee rate of 20 sat/byte just for simplicity. - // In reality, the fee rate on UTXO deposit is different from the fee rate when the UTXO is spent. - BtcDepositorFeeMin = DepositorFee(20) // 0.00001360 (20 * 68vB / 100000000), the minimum deposit fee in BTC for 20 sat/byte -} - -func IsEnvFlagEnabled(flag string) bool { - value := os.Getenv(flag) - return value == "true" || value == "1" -} - -func PrettyPrintStruct(val interface{}) (string, error) { - prettyStruct, err := json.MarshalIndent( - val, - "", - " ", - ) - if err != nil { - return "", err - } - return string(prettyStruct), nil -} - -// FeeRateToSatPerByte converts a fee rate in BTC/KB to sat/byte. -func FeeRateToSatPerByte(rate float64) *big.Int { - // #nosec G701 always in range - satPerKB := new(big.Int).SetInt64(int64(rate * satoshiPerBitcoin)) - return new(big.Int).Div(satPerKB, big.NewInt(bytesPerKB)) -} - -// WiredTxSize calculates the wired tx size in bytes -func WiredTxSize(numInputs uint64, numOutputs uint64) uint64 { - // Version 4 bytes + LockTime 4 bytes + Serialized varint size for the - // number of transaction inputs and outputs. - // #nosec G701 always positive - return uint64(8 + wire.VarIntSerializeSize(numInputs) + wire.VarIntSerializeSize(numOutputs)) -} - -// EstimateSegWitTxSize estimates SegWit tx size in vByte -func EstimateSegWitTxSize(numInputs uint64, numOutputs uint64) uint64 { - if numInputs == 0 { - return 0 - } - bytesWiredTx := WiredTxSize(numInputs, numOutputs) - bytesInput := numInputs * bytesPerInput - bytesOutput := numOutputs * bytesPerOutput - bytesWitness := bytes1stWitness + (numInputs-1)*bytesPerWitness - - // https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-size-calculations - // Calculation for signed SegWit tx: blockchain.GetTransactionWeight(tx) / 4 - return bytesWiredTx + bytesInput + bytesOutput + bytesWitness/blockchain.WitnessScaleFactor -} - -// SegWitTxSizeDepositor returns SegWit tx size (68vB) incurred by the depositor -func SegWitTxSizeDepositor() uint64 { - return bytesPerInput + bytesPerWitness/blockchain.WitnessScaleFactor -} - -// SegWitTxSizeWithdrawer returns SegWit tx size (171vB) incurred by the withdrawer (1 input, 3 outputs) -func SegWitTxSizeWithdrawer() uint64 { - bytesWiredTx := WiredTxSize(1, 3) - bytesInput := uint64(1) * bytesPerInput // nonce mark - bytesOutput := uint64(3) * bytesPerOutput // 3 outputs: new nonce mark, payment, change - return bytesWiredTx + bytesInput + bytesOutput + bytes1stWitness/blockchain.WitnessScaleFactor -} - -// DepositorFee calculates the depositor fee in BTC for a given sat/byte fee rate -// Note: the depositor fee is charged in order to cover the cost of spending the deposited UTXO in the future -func DepositorFee(satPerByte int64) float64 { - return float64(satPerByte) * float64(BtcOutTxBytesDepositor) / satoshiPerBitcoin -} - -func GetSatoshis(btc float64) (int64, error) { - // The amount is only considered invalid if it cannot be represented - // as an integer type. This may happen if f is NaN or +-Infinity. - // BTC max amount is 21 mil and its at least 0 (Note: bitcoin allows creating 0-value outputs) - switch { - case math.IsNaN(btc): - fallthrough - case math.IsInf(btc, 1): - fallthrough - case math.IsInf(btc, -1): - return 0, errors.New("invalid bitcoin amount") - case btc > 21000000.0: - return 0, errors.New("exceeded max bitcoin amount") - case btc < 0.0: - return 0, errors.New("cannot be less than zero") - } - return round(btc * satoshiPerBitcoin), nil -} - -func round(f float64) int64 { - if f < 0 { - // #nosec G701 always in range - return int64(f - 0.5) - } - // #nosec G701 always in range - return int64(f + 0.5) -} - -func payToWitnessPubKeyHashScript(pubKeyHash []byte) ([]byte, error) { - return txscript.NewScriptBuilder().AddOp(txscript.OP_0).AddData(pubKeyHash).Script() -} - -type DynamicTicker struct { - name string - interval uint64 - impl *time.Ticker -} - -func NewDynamicTicker(name string, interval uint64) (*DynamicTicker, error) { - if interval <= 0 { - return nil, fmt.Errorf("non-positive ticker interval %d for %s", interval, name) - } - - return &DynamicTicker{ - name: name, - interval: interval, - impl: time.NewTicker(time.Duration(interval) * time.Second), - }, nil -} - -func (t *DynamicTicker) C() <-chan time.Time { - return t.impl.C -} - -func (t *DynamicTicker) UpdateInterval(newInterval uint64, logger zerolog.Logger) { - if newInterval > 0 && t.interval != newInterval { - t.impl.Stop() - oldInterval := t.interval - t.interval = newInterval - t.impl = time.NewTicker(time.Duration(t.interval) * time.Second) - logger.Info().Msgf("%s ticker interval changed from %d to %d", t.name, oldInterval, newInterval) - } -} - -func (t *DynamicTicker) Stop() { - t.impl.Stop() -} - -// CheckEvmTxLog checks the basics of an EVM tx log -func (ob *EVMChainClient) CheckEvmTxLog(vLog *ethtypes.Log, wantAddress ethcommon.Address, wantHash string, wantTopics int) error { - if vLog.Removed { - return fmt.Errorf("log is removed, chain reorg?") - } - if vLog.Address != wantAddress { - return fmt.Errorf("log emitter address mismatch: want %s got %s", wantAddress.Hex(), vLog.Address.Hex()) - } - if vLog.TxHash.Hex() == "" { - return fmt.Errorf("log tx hash is empty: %d %s", vLog.BlockNumber, vLog.TxHash.Hex()) - } - if wantHash != "" && vLog.TxHash.Hex() != wantHash { - return fmt.Errorf("log tx hash mismatch: want %s got %s", wantHash, vLog.TxHash.Hex()) - } - if len(vLog.Topics) != wantTopics { - return fmt.Errorf("number of topics mismatch: want %d got %d", wantTopics, len(vLog.Topics)) - } - return nil -} - -// HasEnoughConfirmations checks if the given receipt has enough confirmations -func (ob *EVMChainClient) HasEnoughConfirmations(receipt *ethtypes.Receipt, lastHeight uint64) bool { - confHeight := receipt.BlockNumber.Uint64() + ob.GetChainParams().ConfirmationCount - return lastHeight >= confHeight -} - -// GetTransactionSender returns the sender of the given transaction -func (ob *EVMChainClient) GetTransactionSender(tx *ethtypes.Transaction, blockHash ethcommon.Hash, txIndex uint) (ethcommon.Address, error) { - sender, err := ob.evmClient.TransactionSender(context.Background(), tx, blockHash, txIndex) - if err != nil { - // trying local recovery (assuming LondonSigner dynamic fee tx type) of sender address - signer := ethtypes.NewLondonSigner(big.NewInt(ob.chain.ChainId)) - sender, err = signer.Sender(tx) - if err != nil { - ob.logger.ExternalChainWatcher.Err(err).Msgf("can't recover the sender from tx hash %s chain %d", tx.Hash().Hex(), ob.chain.ChainId) - return ethcommon.Address{}, err - } - } - return sender, nil -} - -func (ob *EVMChainClient) GetInboundVoteMsgForDepositedEvent(event *erc20custody.ERC20CustodyDeposited, sender ethcommon.Address) *types.MsgVoteOnObservedInboundTx { - if bytes.Equal(event.Message, []byte(DonationMessage)) { - ob.logger.ExternalChainWatcher.Info().Msgf("thank you rich folk for your donation! tx %s chain %d", event.Raw.TxHash.Hex(), ob.chain.ChainId) - return nil - } - message := hex.EncodeToString(event.Message) - ob.logger.ExternalChainWatcher.Info().Msgf("ERC20CustodyDeposited inTx detected on chain %d tx %s block %d from %s value %s message %s", - ob.chain.ChainId, event.Raw.TxHash.Hex(), event.Raw.BlockNumber, sender.Hex(), event.Amount.String(), message) - - return GetInBoundVoteMessage( - sender.Hex(), - ob.chain.ChainId, - "", - clienttypes.BytesToEthHex(event.Recipient), - ob.zetaClient.ZetaChain().ChainId, - sdkmath.NewUintFromBigInt(event.Amount), - hex.EncodeToString(event.Message), - event.Raw.TxHash.Hex(), - event.Raw.BlockNumber, - 1_500_000, - common.CoinType_ERC20, - event.Asset.String(), - ob.zetaClient.GetKeys().GetOperatorAddress().String(), - event.Raw.Index, - ) -} - -func (ob *EVMChainClient) GetInboundVoteMsgForZetaSentEvent(event *zetaconnector.ZetaConnectorNonEthZetaSent) *types.MsgVoteOnObservedInboundTx { - destChain := common.GetChainFromChainID(event.DestinationChainId.Int64()) - if destChain == nil { - ob.logger.ExternalChainWatcher.Warn().Msgf("chain id not supported %d", event.DestinationChainId.Int64()) - return nil - } - destAddr := clienttypes.BytesToEthHex(event.DestinationAddress) - if !destChain.IsZetaChain() { - cfgDest, found := ob.cfg.GetEVMConfig(destChain.ChainId) - if !found { - ob.logger.ExternalChainWatcher.Warn().Msgf("chain id not present in EVMChainConfigs %d", event.DestinationChainId.Int64()) - return nil - } - if strings.EqualFold(destAddr, cfgDest.ZetaTokenContractAddress) { - ob.logger.ExternalChainWatcher.Warn().Msgf("potential attack attempt: %s destination address is ZETA token contract address %s", destChain, destAddr) - return nil - } - } - message := base64.StdEncoding.EncodeToString(event.Message) - ob.logger.ExternalChainWatcher.Info().Msgf("ZetaSent inTx detected on chain %d tx %s block %d from %s value %s message %s", - ob.chain.ChainId, event.Raw.TxHash.Hex(), event.Raw.BlockNumber, event.ZetaTxSenderAddress.Hex(), event.ZetaValueAndGas.String(), message) - - return GetInBoundVoteMessage( - event.ZetaTxSenderAddress.Hex(), - ob.chain.ChainId, - event.SourceTxOriginAddress.Hex(), - clienttypes.BytesToEthHex(event.DestinationAddress), - destChain.ChainId, - sdkmath.NewUintFromBigInt(event.ZetaValueAndGas), - message, - event.Raw.TxHash.Hex(), - event.Raw.BlockNumber, - event.DestinationGasLimit.Uint64(), - common.CoinType_Zeta, - "", - ob.zetaClient.GetKeys().GetOperatorAddress().String(), - event.Raw.Index, - ) -} - -func (ob *EVMChainClient) GetInboundVoteMsgForTokenSentToTSS(tx *ethtypes.Transaction, sender ethcommon.Address, blockNumber uint64) *types.MsgVoteOnObservedInboundTx { - if bytes.Equal(tx.Data(), []byte(DonationMessage)) { - ob.logger.ExternalChainWatcher.Info().Msgf("thank you rich folk for your donation! tx %s chain %d", tx.Hash().Hex(), ob.chain.ChainId) - return nil - } - message := "" - if len(tx.Data()) != 0 { - message = hex.EncodeToString(tx.Data()) - } - ob.logger.ExternalChainWatcher.Info().Msgf("TSS inTx detected on chain %d tx %s block %d from %s value %s message %s", - ob.chain.ChainId, tx.Hash().Hex(), blockNumber, sender.Hex(), tx.Value().String(), hex.EncodeToString(tx.Data())) - - return GetInBoundVoteMessage( - sender.Hex(), - ob.chain.ChainId, - sender.Hex(), - sender.Hex(), - ob.zetaClient.ZetaChain().ChainId, - sdkmath.NewUintFromBigInt(tx.Value()), - message, - tx.Hash().Hex(), - blockNumber, - 90_000, - common.CoinType_Gas, - "", - ob.zetaClient.GetKeys().GetOperatorAddress().String(), - 0, // not a smart contract call - ) -} diff --git a/zetaclient/utils_test.go b/zetaclient/utils_test.go index 98355e82ac..8054191b8b 100644 --- a/zetaclient/utils_test.go +++ b/zetaclient/utils_test.go @@ -4,6 +4,8 @@ import ( "fmt" "testing" + "github.com/zeta-chain/zetacore/zetaclient/evm" + ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" @@ -81,7 +83,7 @@ func TestCheckEvmTxLog(t *testing.T) { }, } - evmClient := EVMChainClient{} + evmClient := evm.ChainClient{} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { fmt.Printf("check test: %s\n", tt.name) @@ -89,7 +91,7 @@ func TestCheckEvmTxLog(t *testing.T) { tt.vLog, connectorAddr, "0xb252c9e77feafdeeae25cc1f037a16c4b50fa03c494754b99a7339d816c79626", - TopicsZetaSent, + evm.TopicsZetaSent, ) if tt.fail { require.Error(t, err) diff --git a/zetaclient/voter_test.go b/zetaclient/voter_test.go index 853c17eea9..21ca449d91 100644 --- a/zetaclient/voter_test.go +++ b/zetaclient/voter_test.go @@ -11,19 +11,22 @@ import ( "path/filepath" "time" + "github.com/zeta-chain/zetacore/zetaclient/keys" + "github.com/zeta-chain/zetacore/zetaclient/zetabridge" + "github.com/rs/zerolog/log" . "gopkg.in/check.v1" ) type VoterSuite struct { - bridge1 *ZetaCoreBridge - bridge2 *ZetaCoreBridge + bridge1 *zetabridge.ZetaCoreBridge + bridge2 *zetabridge.ZetaCoreBridge } var _ = Suite(&VoterSuite{}) func (s *VoterSuite) SetUpTest(c *C) { - SetupConfigForTest() // setup meta-prefix + keys.SetupConfigForTest() // setup meta-prefix c.Logf("Settting up test...") homeDir, err := os.UserHomeDir() @@ -40,18 +43,18 @@ func (s *VoterSuite) SetUpTest(c *C) { { signerName := "alice" signerPass := "password" - kb, _, err := GetKeyringKeybase([]common.KeyType{common.ObserverGranteeKey}, chainHomeFoler, signerName, signerPass) + kb, _, err := keys.GetKeyringKeybase([]common.KeyType{common.ObserverGranteeKey}, chainHomeFoler, signerName, signerPass) if err != nil { log.Fatal().Err(err).Msg("fail to get keyring keybase") } - k := NewKeysWithKeybase(kb, signerName, signerPass) + k := keys.NewKeysWithKeybase(kb, signerName, signerPass) chainIP := os.Getenv("CHAIN_IP") if chainIP == "" { chainIP = "127.0.0.1" } - bridge, err := NewZetaCoreBridge(k, chainIP, "alice") + bridge, err := zetabridge.NewZetaCoreBridge(k, chainIP, "alice") if err != nil { c.Fail() } @@ -63,18 +66,18 @@ func (s *VoterSuite) SetUpTest(c *C) { { signerName := "bob" signerPass := "password" - kb, _, err := GetKeyringKeybase(chainHomeFoler, signerName, signerPass) + kb, _, err := keys.GetKeyringKeybase(chainHomeFoler, signerName, signerPass) if err != nil { log.Fatal().Err(err).Msg("fail to get keyring keybase") } - k := NewKeysWithKeybase(kb, signerName, signerPass) + k := keys.NewKeysWithKeybase(kb, signerName, signerPass) chainIP := os.Getenv("CHAIN_IP") if chainIP == "" { chainIP = "127.0.0.1" } - bridge, err := NewZetaCoreBridge(k, chainIP, "bob") + bridge, err := zetabridge.NewZetaCoreBridge(k, chainIP, "bob") if err != nil { c.Fail() } diff --git a/zetaclient/block_height.go b/zetaclient/zetabridge/block_height.go similarity index 96% rename from zetaclient/block_height.go rename to zetaclient/zetabridge/block_height.go index 587fd8b8f4..30fcce82b4 100644 --- a/zetaclient/block_height.go +++ b/zetaclient/zetabridge/block_height.go @@ -1,4 +1,4 @@ -package zetaclient +package zetabridge import ( "context" diff --git a/zetaclient/broadcast.go b/zetaclient/zetabridge/broadcast.go similarity index 82% rename from zetaclient/broadcast.go rename to zetaclient/zetabridge/broadcast.go index b46a9c6c8d..93de44f3f1 100644 --- a/zetaclient/broadcast.go +++ b/zetaclient/zetabridge/broadcast.go @@ -1,4 +1,4 @@ -package zetaclient +package zetabridge import ( "fmt" @@ -6,6 +6,10 @@ import ( "strconv" "strings" + "github.com/rs/zerolog/log" + + "github.com/zeta-chain/zetacore/zetaclient/authz" + "github.com/cosmos/cosmos-sdk/client" clienttx "github.com/cosmos/cosmos-sdk/client/tx" sdktypes "github.com/cosmos/cosmos-sdk/types" @@ -32,7 +36,7 @@ var ( ) // Broadcast Broadcasts tx to metachain. Returns txHash and error -func (b *ZetaCoreBridge) Broadcast(gaslimit uint64, authzWrappedMsg sdktypes.Msg, authzSigner AuthZSigner) (string, error) { +func (b *ZetaCoreBridge) Broadcast(gaslimit uint64, authzWrappedMsg sdktypes.Msg, authzSigner authz.Signer) (string, error) { b.broadcastLock.Lock() defer b.broadcastLock.Unlock() var err error @@ -64,7 +68,7 @@ func (b *ZetaCoreBridge) Broadcast(gaslimit uint64, authzWrappedMsg sdktypes.Msg } } - flags := flag.NewFlagSet("zetacore", 0) + flags := flag.NewFlagSet("zetabridge", 0) ctx, err := b.GetContext() if err != nil { @@ -195,3 +199,21 @@ func (b *ZetaCoreBridge) QueryTxResult(hash string) (*sdktypes.TxResponse, error } return authtx.QueryTx(ctx, hash) } + +// HandleBroadcastError returns whether to retry in a few seconds, and whether to report via AddTxHashToOutTxTracker +func HandleBroadcastError(err error, nonce, toChain, outTxHash string) (bool, bool) { + if strings.Contains(err.Error(), "nonce too low") { + log.Warn().Err(err).Msgf("nonce too low! this might be a unnecessary key-sign. increase re-try interval and awaits outTx confirmation") + return false, false + } + if strings.Contains(err.Error(), "replacement transaction underpriced") { + log.Warn().Err(err).Msgf("Broadcast replacement: nonce %s chain %s outTxHash %s", nonce, toChain, outTxHash) + return false, false + } else if strings.Contains(err.Error(), "already known") { // this is error code from QuickNode + log.Warn().Err(err).Msgf("Broadcast duplicates: nonce %s chain %s outTxHash %s", nonce, toChain, outTxHash) + return false, true // report to tracker, because there's possibilities a successful broadcast gets this error code + } + + log.Error().Err(err).Msgf("Broadcast error: nonce %s chain %s outTxHash %s; retrying...", nonce, toChain, outTxHash) + return true, false +} diff --git a/zetaclient/broadcast_test.go b/zetaclient/zetabridge/broadcast_test.go similarity index 73% rename from zetaclient/broadcast_test.go rename to zetaclient/zetabridge/broadcast_test.go index 01c910aa72..a52ebb63eb 100644 --- a/zetaclient/broadcast_test.go +++ b/zetaclient/zetabridge/broadcast_test.go @@ -1,4 +1,4 @@ -package zetaclient +package zetabridge import ( "fmt" @@ -17,7 +17,7 @@ func (s *BcastSuite) SetUpTest(c *C) { } func (s *BcastSuite) TestParsingSeqNumMismatch(c *C) { - err_msg := "fail to broadcast to zetacore,code:32, log:account sequence mismatch, expected 386232, got 386230: incorrect account sequence" + err_msg := "fail to broadcast to zetabridge,code:32, log:account sequence mismatch, expected 386232, got 386230: incorrect account sequence" re := regexp.MustCompile(`account sequence mismatch, expected ([0-9]*), got ([0-9]*)`) fmt.Printf("%q\n", re.FindStringSubmatch(err_msg)) err_msg2 := "hahah" diff --git a/zetaclient/query.go b/zetaclient/zetabridge/query.go similarity index 98% rename from zetaclient/query.go rename to zetaclient/zetabridge/query.go index bf2afa4d9f..c2a8269ed3 100644 --- a/zetaclient/query.go +++ b/zetaclient/zetabridge/query.go @@ -1,4 +1,4 @@ -package zetaclient +package zetabridge import ( "context" @@ -6,6 +6,8 @@ import ( "sort" "time" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + sdkmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" @@ -22,14 +24,6 @@ import ( "google.golang.org/grpc" ) -type Order string - -const ( - NoOrder Order = "" - Ascending Order = "ASC" - Descending Order = "DESC" -) - func (b *ZetaCoreBridge) GetCrosschainFlags() (observertypes.CrosschainFlags, error) { client := observertypes.NewQueryClient(b.grpcConn) resp, err := client.CrosschainFlags(context.Background(), &observertypes.QueryGetCrosschainFlagsRequest{}) @@ -340,7 +334,7 @@ func (b *ZetaCoreBridge) GetOutTxTracker(chain common.Chain, nonce uint64) (*typ return &resp.OutTxTracker, nil } -func (b *ZetaCoreBridge) GetAllOutTxTrackerByChain(chainID int64, order Order) ([]types.OutTxTracker, error) { +func (b *ZetaCoreBridge) GetAllOutTxTrackerByChain(chainID int64, order interfaces.Order) ([]types.OutTxTracker, error) { client := types.NewQueryClient(b.grpcConn) resp, err := client.OutTxTrackerAllByChain(context.Background(), &types.QueryAllOutTxTrackerByChainRequest{ Chain: chainID, @@ -355,12 +349,12 @@ func (b *ZetaCoreBridge) GetAllOutTxTrackerByChain(chainID int64, order Order) ( if err != nil { return nil, err } - if order == Ascending { + if order == interfaces.Ascending { sort.SliceStable(resp.OutTxTracker, func(i, j int) bool { return resp.OutTxTracker[i].Nonce < resp.OutTxTracker[j].Nonce }) } - if order == Descending { + if order == interfaces.Descending { sort.SliceStable(resp.OutTxTracker, func(i, j int) bool { return resp.OutTxTracker[i].Nonce > resp.OutTxTracker[j].Nonce }) diff --git a/zetaclient/zetabridge/tx.go b/zetaclient/zetabridge/tx.go new file mode 100644 index 0000000000..337743a9df --- /dev/null +++ b/zetaclient/zetabridge/tx.go @@ -0,0 +1,431 @@ +package zetabridge + +import ( + "fmt" + "math/big" + "strings" + "time" + + "cosmossdk.io/math" + authz2 "github.com/zeta-chain/zetacore/zetaclient/authz" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" + "github.com/pkg/errors" + "github.com/zeta-chain/go-tss/blame" + "github.com/zeta-chain/zetacore/common" + "github.com/zeta-chain/zetacore/x/crosschain/types" + observerTypes "github.com/zeta-chain/zetacore/x/observer/types" + "github.com/zeta-chain/zetacore/zetaclient/config" +) + +const ( + // DefaultGasLimit is the default gas limit used for broadcasting txs + DefaultGasLimit = 200_000 + + // PostGasPriceGasLimit is the gas limit for voting new gas price + PostGasPriceGasLimit = 1_500_000 + + // AddTxHashToOutTxTrackerGasLimit is the gas limit for adding tx hash to out tx tracker + AddTxHashToOutTxTrackerGasLimit = 200_000 + + // PostBlameDataGasLimit is the gas limit for voting on blames + PostBlameDataGasLimit = 200_000 + + // DefaultRetryCount is the number of retries for broadcasting a tx + DefaultRetryCount = 5 + + // ExtendedRetryCount is an extended number of retries for broadcasting a tx, used in keygen operations + ExtendedRetryCount = 15 + + // DefaultRetryInterval is the interval between retries in seconds + DefaultRetryInterval = 5 + + // MonitorVoteInboundTxResultInterval is the interval between retries for monitoring tx result in seconds + MonitorVoteInboundTxResultInterval = 5 + + // MonitorVoteInboundTxResultRetryCount is the number of retries to fetch monitoring tx result + MonitorVoteInboundTxResultRetryCount = 20 + + // PostVoteOutboundGasLimit is the gas limit for voting on observed outbound tx + PostVoteOutboundGasLimit = 400_000 + + // PostVoteOutboundRevertGasLimit is the gas limit for voting on observed outbound tx for revert (when outbound fails) + // The value needs to be higher because reverting implies interacting with the EVM to perform swaps for the gas token + PostVoteOutboundRevertGasLimit = 1_500_000 + + // MonitorVoteOutboundTxResultInterval is the interval between retries for monitoring tx result in seconds + MonitorVoteOutboundTxResultInterval = 5 + + // MonitorVoteOutboundTxResultRetryCount is the number of retries to fetch monitoring tx result + MonitorVoteOutboundTxResultRetryCount = 20 +) + +func (b *ZetaCoreBridge) WrapMessageWithAuthz(msg sdk.Msg) (sdk.Msg, authz2.Signer, error) { + msgURL := sdk.MsgTypeURL(msg) + + // verify message validity + if err := msg.ValidateBasic(); err != nil { + return nil, authz2.Signer{}, fmt.Errorf("%s invalid msg | %s", msgURL, err.Error()) + } + + authzSigner := authz2.GetSigner(msgURL) + authzMessage := authz.NewMsgExec(authzSigner.GranteeAddress, []sdk.Msg{msg}) + return &authzMessage, authzSigner, nil +} + +func (b *ZetaCoreBridge) PostGasPrice(chain common.Chain, gasPrice uint64, supply string, blockNum uint64) (string, error) { + // double the gas price to avoid gas price spike + gasPrice = gasPrice * 2 + signerAddress := b.keys.GetOperatorAddress().String() + msg := types.NewMsgGasPriceVoter(signerAddress, chain.ChainId, gasPrice, supply, blockNum) + + authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) + if err != nil { + return "", err + } + + for i := 0; i < DefaultRetryCount; i++ { + zetaTxHash, err := b.Broadcast(PostGasPriceGasLimit, authzMsg, authzSigner) + if err == nil { + return zetaTxHash, nil + } + b.logger.Debug().Err(err).Msgf("PostGasPrice broadcast fail | Retry count : %d", i+1) + time.Sleep(DefaultRetryInterval * time.Second) + } + + return "", fmt.Errorf("post gasprice failed after %d retries", DefaultRetryInterval) +} + +func (b *ZetaCoreBridge) AddTxHashToOutTxTracker( + chainID int64, + nonce uint64, + txHash string, + proof *common.Proof, + blockHash string, + txIndex int64, +) (string, error) { + // don't report if the tracker already contains the txHash + tracker, err := b.GetOutTxTracker(common.Chain{ChainId: chainID}, nonce) + if err == nil { + for _, hash := range tracker.HashList { + if strings.EqualFold(hash.TxHash, txHash) { + return "", nil + } + } + } + signerAddress := b.keys.GetOperatorAddress().String() + msg := types.NewMsgAddToOutTxTracker(signerAddress, chainID, nonce, txHash, proof, blockHash, txIndex) + + authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) + if err != nil { + return "", err + } + + zetaTxHash, err := b.Broadcast(AddTxHashToOutTxTrackerGasLimit, authzMsg, authzSigner) + if err != nil { + return "", err + } + return zetaTxHash, nil +} + +func (b *ZetaCoreBridge) SetTSS(tssPubkey string, keyGenZetaHeight int64, status common.ReceiveStatus) (string, error) { + signerAddress := b.keys.GetOperatorAddress().String() + msg := types.NewMsgCreateTSSVoter(signerAddress, tssPubkey, keyGenZetaHeight, status) + + authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) + if err != nil { + return "", err + } + + zetaTxHash := "" + for i := 0; i <= DefaultRetryCount; i++ { + zetaTxHash, err = b.Broadcast(DefaultGasLimit, authzMsg, authzSigner) + if err == nil { + return zetaTxHash, nil + } + b.logger.Debug().Err(err).Msgf("SetTSS broadcast fail | Retry count : %d", i+1) + time.Sleep(DefaultRetryInterval * time.Second) + } + + return "", fmt.Errorf("set tss failed | err %s", err.Error()) +} + +func (b *ZetaCoreBridge) ConfigUpdater(cfg *config.Config) { + b.logger.Info().Msg("ConfigUpdater started") + ticker := time.NewTicker(time.Duration(cfg.ConfigUpdateTicker) * time.Second) + for { + select { + case <-ticker.C: + b.logger.Debug().Msg("Running Updater") + err := b.UpdateConfigFromCore(cfg, false) + if err != nil { + b.logger.Err(err).Msg("ConfigUpdater failed to update config") + } + case <-b.stop: + b.logger.Info().Msg("ConfigUpdater stopped") + return + } + } +} + +func (b *ZetaCoreBridge) PostBlameData(blame *blame.Blame, chainID int64, index string) (string, error) { + signerAddress := b.keys.GetOperatorAddress().String() + zetaBlame := observerTypes.Blame{ + Index: index, + FailureReason: blame.FailReason, + Nodes: observerTypes.ConvertNodes(blame.BlameNodes), + } + msg := observerTypes.NewMsgAddBlameVoteMsg(signerAddress, chainID, zetaBlame) + + authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) + if err != nil { + return "", err + } + + var gasLimit uint64 = PostBlameDataGasLimit + + for i := 0; i < DefaultRetryCount; i++ { + zetaTxHash, err := b.Broadcast(gasLimit, authzMsg, authzSigner) + if err == nil { + return zetaTxHash, nil + } + b.logger.Error().Err(err).Msgf("PostBlame broadcast fail | Retry count : %d", i+1) + time.Sleep(DefaultRetryInterval * time.Second) + } + return "", fmt.Errorf("post blame data failed after %d retries", DefaultRetryCount) +} + +func (b *ZetaCoreBridge) PostAddBlockHeader(chainID int64, blockHash []byte, height int64, header common.HeaderData) (string, error) { + signerAddress := b.keys.GetOperatorAddress().String() + + msg := observerTypes.NewMsgAddBlockHeader(signerAddress, chainID, blockHash, height, header) + + authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) + if err != nil { + return "", err + } + + var gasLimit uint64 = DefaultGasLimit + for i := 0; i < DefaultRetryCount; i++ { + zetaTxHash, err := b.Broadcast(gasLimit, authzMsg, authzSigner) + if err == nil { + return zetaTxHash, nil + } + b.logger.Error().Err(err).Msgf("PostAddBlockHeader broadcast fail | Retry count : %d", i+1) + time.Sleep(DefaultRetryInterval * time.Second) + } + return "", fmt.Errorf("post add block header failed after %d retries", DefaultRetryCount) +} + +// PostVoteInbound posts a vote on an observed inbound tx +// retryGasLimit is the gas limit used to resend the tx if it fails because of insufficient gas +// it is used when the ballot is finalized and the inbound tx needs to be processed +func (b *ZetaCoreBridge) PostVoteInbound(gasLimit, retryGasLimit uint64, msg *types.MsgVoteOnObservedInboundTx) (string, string, error) { + authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) + if err != nil { + return "", "", err + } + + // don't post send if has already voted before + ballotIndex := msg.Digest() + hasVoted, err := b.HasVoted(ballotIndex, msg.Creator) + if err != nil { + return "", ballotIndex, errors.Wrapf(err, "PostVoteInbound: unable to check if already voted for ballot %s voter %s", ballotIndex, msg.Creator) + } + if hasVoted { + return "", ballotIndex, nil + } + + for i := 0; i < DefaultRetryCount; i++ { + zetaTxHash, err := b.Broadcast(gasLimit, authzMsg, authzSigner) + if err == nil { + // monitor the result of the transaction and resend if necessary + go b.MonitorVoteInboundTxResult(zetaTxHash, retryGasLimit, msg) + + return zetaTxHash, ballotIndex, nil + } + b.logger.Debug().Err(err).Msgf("PostVoteInbound broadcast fail | Retry count : %d", i+1) + time.Sleep(DefaultRetryInterval * time.Second) + } + return "", ballotIndex, fmt.Errorf("post send failed after %d retries", DefaultRetryInterval) +} + +// MonitorVoteInboundTxResult monitors the result of a vote inbound tx +// retryGasLimit is the gas limit used to resend the tx if it fails because of insufficient gas +// if retryGasLimit is 0, the tx is not resent +func (b *ZetaCoreBridge) MonitorVoteInboundTxResult(zetaTxHash string, retryGasLimit uint64, msg *types.MsgVoteOnObservedInboundTx) { + var lastErr error + + for i := 0; i < MonitorVoteInboundTxResultRetryCount; i++ { + time.Sleep(MonitorVoteInboundTxResultInterval * time.Second) + + // query tx result from ZetaChain + txResult, err := b.QueryTxResult(zetaTxHash) + + if err == nil { + if strings.Contains(txResult.RawLog, "failed to execute message") { + // the inbound vote tx shouldn't fail to execute + // this shouldn't happen + b.logger.Error().Msgf( + "MonitorInboundTxResult: failed to execute vote, txHash: %s, log %s", zetaTxHash, txResult.RawLog, + ) + } else if strings.Contains(txResult.RawLog, "out of gas") { + // if the tx fails with an out of gas error, resend the tx with more gas if retryGasLimit > 0 + b.logger.Debug().Msgf( + "MonitorInboundTxResult: out of gas, txHash: %s, log %s", zetaTxHash, txResult.RawLog, + ) + if retryGasLimit > 0 { + // new retryGasLimit set to 0 to prevent reentering this function + _, _, err := b.PostVoteInbound(retryGasLimit, 0, msg) + if err != nil { + b.logger.Error().Err(err).Msgf( + "MonitorInboundTxResult: failed to resend tx, txHash: %s, log %s", zetaTxHash, txResult.RawLog, + ) + } else { + b.logger.Info().Msgf( + "MonitorInboundTxResult: successfully resent tx, txHash: %s, log %s", zetaTxHash, txResult.RawLog, + ) + } + } + } else { + b.logger.Debug().Msgf( + "MonitorInboundTxResult: successful txHash %s, log %s", zetaTxHash, txResult.RawLog, + ) + } + return + } + lastErr = err + } + + b.logger.Error().Err(lastErr).Msgf( + "MonitorInboundTxResult: unable to query tx result for txHash %s, err %s", zetaTxHash, lastErr.Error(), + ) + return +} + +// PostVoteOutbound posts a vote on an observed outbound tx +func (b *ZetaCoreBridge) PostVoteOutbound( + sendHash string, + outTxHash string, + outBlockHeight uint64, + outTxGasUsed uint64, + outTxEffectiveGasPrice *big.Int, + outTxEffectiveGasLimit uint64, + amount *big.Int, + status common.ReceiveStatus, + chain common.Chain, + nonce uint64, + coinType common.CoinType, +) (string, string, error) { + signerAddress := b.keys.GetOperatorAddress().String() + msg := types.NewMsgVoteOnObservedOutboundTx( + signerAddress, + sendHash, + outTxHash, + outBlockHeight, + outTxGasUsed, + math.NewIntFromBigInt(outTxEffectiveGasPrice), + outTxEffectiveGasLimit, + math.NewUintFromBigInt(amount), + status, + chain.ChainId, + nonce, + coinType, + ) + + // when an outbound fails and a revert is required, the gas limit needs to be higher + // this is because the revert tx needs to interact with the EVM to perform swaps for the gas token + // the higher gas limit is only necessary when the vote is finalized and the outbound is processed + // therefore we use a retryGasLimit with a higher value to resend the tx if it fails (when the vote is finalized) + retryGasLimit := uint64(0) + if msg.Status == common.ReceiveStatus_Failed { + retryGasLimit = PostVoteOutboundRevertGasLimit + } + + return b.PostVoteOutboundFromMsg(PostVoteOutboundGasLimit, retryGasLimit, msg) +} + +// PostVoteOutboundFromMsg posts a vote on an observed outbound tx from a MsgVoteOnObservedOutboundTx +func (b *ZetaCoreBridge) PostVoteOutboundFromMsg(gasLimit, retryGasLimit uint64, msg *types.MsgVoteOnObservedOutboundTx) (string, string, error) { + authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) + if err != nil { + return "", "", err + } + + // don't post confirmation if has already voted before + ballotIndex := msg.Digest() + hasVoted, err := b.HasVoted(ballotIndex, msg.Creator) + if err != nil { + return "", ballotIndex, errors.Wrapf(err, "PostVoteOutbound: unable to check if already voted for ballot %s voter %s", ballotIndex, msg.Creator) + } + if hasVoted { + return "", ballotIndex, nil + } + for i := 0; i < DefaultRetryCount; i++ { + zetaTxHash, err := b.Broadcast(gasLimit, authzMsg, authzSigner) + if err == nil { + // monitor the result of the transaction and resend if necessary + go b.MonitorVoteOutboundTxResult(zetaTxHash, retryGasLimit, msg) + + return zetaTxHash, ballotIndex, nil + } + b.logger.Debug().Err(err).Msgf("PostVoteOutbound broadcast fail | Retry count : %d", i+1) + time.Sleep(DefaultRetryInterval * time.Second) + } + return "", ballotIndex, fmt.Errorf("post receive failed after %d retries", DefaultRetryCount) +} + +// MonitorVoteOutboundTxResult monitors the result of a vote outbound tx +// retryGasLimit is the gas limit used to resend the tx if it fails because of insufficient gas +// if retryGasLimit is 0, the tx is not resent +func (b *ZetaCoreBridge) MonitorVoteOutboundTxResult(zetaTxHash string, retryGasLimit uint64, msg *types.MsgVoteOnObservedOutboundTx) { + var lastErr error + + for i := 0; i < MonitorVoteOutboundTxResultRetryCount; i++ { + time.Sleep(MonitorVoteOutboundTxResultInterval * time.Second) + + // query tx result from ZetaChain + txResult, err := b.QueryTxResult(zetaTxHash) + + if err == nil { + if strings.Contains(txResult.RawLog, "failed to execute message") { + // the inbound vote tx shouldn't fail to execute + // this shouldn't happen + b.logger.Error().Msgf( + "MonitorVoteOutboundTxResult: failed to execute vote, txHash: %s, log %s", zetaTxHash, txResult.RawLog, + ) + } else if strings.Contains(txResult.RawLog, "out of gas") { + // if the tx fails with an out of gas error, resend the tx with more gas if retryGasLimit > 0 + b.logger.Debug().Msgf( + "MonitorVoteOutboundTxResult: out of gas, txHash: %s, log %s", zetaTxHash, txResult.RawLog, + ) + if retryGasLimit > 0 { + // new retryGasLimit set to 0 to prevent reentering this function + _, _, err := b.PostVoteOutboundFromMsg(retryGasLimit, 0, msg) + + if err != nil { + b.logger.Error().Err(err).Msgf( + "MonitorVoteOutboundTxResult: failed to resend tx, txHash: %s, log %s", zetaTxHash, txResult.RawLog, + ) + } else { + b.logger.Info().Msgf( + "MonitorVoteOutboundTxResult: successfully resent tx, txHash: %s, log %s", zetaTxHash, txResult.RawLog, + ) + } + } + } else { + b.logger.Debug().Msgf( + "MonitorVoteOutboundTxResult: successful txHash %s, log %s", zetaTxHash, txResult.RawLog, + ) + } + return + } + lastErr = err + } + + b.logger.Error().Err(lastErr).Msgf( + "MonitorVoteOutboundTxResult: unable to query tx result for txHash %s, err %s", zetaTxHash, lastErr.Error(), + ) + return +} diff --git a/zetaclient/zetabridge/tx_vote_inbound.go b/zetaclient/zetabridge/tx_vote_inbound.go new file mode 100644 index 0000000000..0885d84117 --- /dev/null +++ b/zetaclient/zetabridge/tx_vote_inbound.go @@ -0,0 +1,55 @@ +package zetabridge + +import ( + "cosmossdk.io/math" + "github.com/zeta-chain/zetacore/common" + + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +const ( + // PostVoteInboundGasLimit is the gas limit for voting on observed inbound tx + PostVoteInboundGasLimit = 400_000 + + // PostVoteInboundExecutionGasLimit is the gas limit for voting on observed inbound tx and executing it + PostVoteInboundExecutionGasLimit = 4_000_000 + + // PostVoteInboundMessagePassingExecutionGasLimit is the gas limit for voting on, and executing ,observed inbound tx related to message passing (coin_type == zeta) + PostVoteInboundMessagePassingExecutionGasLimit = 1_000_000 +) + +// GetInBoundVoteMessage returns a new MsgVoteOnObservedInboundTx +func GetInBoundVoteMessage( + sender string, + senderChain int64, + txOrigin string, + receiver string, + receiverChain int64, + amount math.Uint, + message string, + inTxHash string, + inBlockHeight uint64, + gasLimit uint64, + coinType common.CoinType, + asset string, + signerAddress string, + eventIndex uint, +) *types.MsgVoteOnObservedInboundTx { + msg := types.NewMsgVoteOnObservedInboundTx( + signerAddress, + sender, + senderChain, + txOrigin, + receiver, + receiverChain, + amount, + message, + inTxHash, + inBlockHeight, + gasLimit, + coinType, + asset, + eventIndex, + ) + return msg +} diff --git a/zetaclient/zetacore_bridge.go b/zetaclient/zetabridge/zetacore_bridge.go similarity index 92% rename from zetaclient/zetacore_bridge.go rename to zetaclient/zetabridge/zetacore_bridge.go index bf4466565c..15a462f818 100644 --- a/zetaclient/zetacore_bridge.go +++ b/zetaclient/zetabridge/zetacore_bridge.go @@ -1,10 +1,14 @@ -package zetaclient +package zetabridge import ( "fmt" "sync" "time" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/keys" + "github.com/zeta-chain/zetacore/zetaclient/metrics" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/simapp/params" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -21,7 +25,7 @@ import ( "google.golang.org/grpc" ) -var _ ZetaCoreBridger = &ZetaCoreBridge{} +var _ interfaces.ZetaCoreBridger = &ZetaCoreBridge{} // ZetaCoreBridge will be used to send tx to ZetaCore. type ZetaCoreBridge struct { @@ -33,21 +37,21 @@ type ZetaCoreBridge struct { httpClient *retryablehttp.Client cfg config.ClientConfiguration encodingCfg params.EncodingConfig - keys *Keys + keys *keys.Keys broadcastLock *sync.RWMutex zetaChainID string zetaChain common.Chain stop chan struct{} pause chan struct{} - Telemetry *TelemetryServer + Telemetry *metrics.TelemetryServer } // NewZetaCoreBridge create a new instance of ZetaCoreBridge -func NewZetaCoreBridge(k *Keys, chainIP string, +func NewZetaCoreBridge(k *keys.Keys, chainIP string, signerName string, chainID string, hsmMode bool, - telemetry *TelemetryServer) (*ZetaCoreBridge, error) { + telemetry *metrics.TelemetryServer) (*ZetaCoreBridge, error) { // main module logger logger := log.With().Str("module", "CoreBridge").Logger() @@ -181,7 +185,7 @@ func (b *ZetaCoreBridge) WaitForCoreToCreateBlocks() { } } -func (b *ZetaCoreBridge) GetKeys() *Keys { +func (b *ZetaCoreBridge) GetKeys() *keys.Keys { return b.keys } @@ -237,13 +241,13 @@ func (b *ZetaCoreBridge) UpdateConfigFromCore(cfg *config.Config, init bool) err } keyGen, err := b.GetKeyGen() if err != nil { - b.logger.Info().Msg("Unable to fetch keygen from zetacore") + b.logger.Info().Msg("Unable to fetch keygen from zetabridge") } cfg.UpdateChainParams(keyGen, newChains, newEVMParams, newBTCParams, init, b.logger) tss, err := b.GetCurrentTss() if err != nil { - b.logger.Debug().Err(err).Msg("Unable to fetch TSS from zetacore") + b.logger.Debug().Err(err).Msg("Unable to fetch TSS from zetabridge") } else { cfg.CurrentTssPubkey = tss.GetTssPubkey() } diff --git a/zetaclient/zetaclient_test.go b/zetaclient/zetaclient_test.go index bed9da67da..db5f24e342 100644 --- a/zetaclient/zetaclient_test.go +++ b/zetaclient/zetaclient_test.go @@ -10,18 +10,21 @@ import ( "os" "path/filepath" + "github.com/zeta-chain/zetacore/zetaclient/keys" + "github.com/zeta-chain/zetacore/zetaclient/zetabridge" + "github.com/rs/zerolog/log" . "gopkg.in/check.v1" ) type MySuite struct { - bridge *ZetaCoreBridge + bridge *zetabridge.ZetaCoreBridge } var _ = Suite(&MySuite{}) func (s *MySuite) SetUpTest(c *C) { - SetupConfigForTest() // setup meta-prefix + keys.SetupConfigForTest() // setup meta-prefix c.Logf("Settting up test...") homeDir, err := os.UserHomeDir() @@ -32,19 +35,19 @@ func (s *MySuite) SetUpTest(c *C) { // alice is the default user created by Starport chain serve signerName := "alice" signerPass := "password" - kb, _, err := GetKeyringKeybase(chainHomeFoler, signerName, signerPass) + kb, _, err := keys.GetKeyringKeybase(chainHomeFoler, signerName, signerPass) if err != nil { log.Fatal().Err(err).Msg("fail to get keyring keybase") } - k := NewKeysWithKeybase(kb, signerName, signerPass) + k := keys.NewKeysWithKeybase(kb, signerName, signerPass) //log.Info().Msgf("keybase: %s", k.GetSignerInfo().GetAddress()) chainIP := os.Getenv("CHAIN_IP") if chainIP == "" { chainIP = "127.0.0.1" } - bridge, err := NewZetaCoreBridge(k, chainIP, "alice") + bridge, err := zetabridge.NewZetaCoreBridge(k, chainIP, "alice") if err != nil { c.Fail() } diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index dbce742826..d37e2967cf 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -3,9 +3,12 @@ package zetaclient import ( "fmt" "math" - "strings" "time" + "github.com/zeta-chain/zetacore/zetaclient/bitcoin" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/outtxprocessor" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" sdkmath "cosmossdk.io/math" @@ -13,7 +16,6 @@ import ( "github.com/pkg/errors" prom "github.com/prometheus/client_golang/prometheus" "github.com/rs/zerolog" - "github.com/rs/zerolog/log" "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/x/crosschain/types" "github.com/zeta-chain/zetacore/zetaclient/config" @@ -31,28 +33,28 @@ type ZetaCoreLog struct { ZetaChainWatcher zerolog.Logger } -// CoreObserver wraps the zetacore bridge and adds the client and signer maps to it . This is the high level object used for CCTX interactions +// CoreObserver wraps the zetabridge bridge and adds the client and signer maps to it . This is the high level object used for CCTX interactions type CoreObserver struct { - bridge ZetaCoreBridger - signerMap map[common.Chain]ChainSigner - clientMap map[common.Chain]ChainClient + bridge interfaces.ZetaCoreBridger + signerMap map[common.Chain]interfaces.ChainSigner + clientMap map[common.Chain]interfaces.ChainClient metrics *metrics.Metrics logger ZetaCoreLog cfg *config.Config - ts *TelemetryServer + ts *metrics.TelemetryServer stop chan struct{} lastOperatorBalance sdkmath.Int } // NewCoreObserver creates a new CoreObserver func NewCoreObserver( - bridge ZetaCoreBridger, - signerMap map[common.Chain]ChainSigner, - clientMap map[common.Chain]ChainClient, + bridge interfaces.ZetaCoreBridger, + signerMap map[common.Chain]interfaces.ChainSigner, + clientMap map[common.Chain]interfaces.ChainClient, metrics *metrics.Metrics, logger zerolog.Logger, cfg *config.Config, - ts *TelemetryServer, + ts *metrics.TelemetryServer, ) *CoreObserver { co := CoreObserver{ ts: ts, @@ -116,7 +118,7 @@ func (co *CoreObserver) MonitorCore() { go co.startCctxScheduler() go func() { - // bridge queries UpgradePlan from zetacore and send to its pause channel if upgrade height is reached + // bridge queries UpgradePlan from zetabridge and send to its pause channel if upgrade height is reached co.bridge.Pause() // now stop everything close(co.stop) // this stops the startSendScheduler() loop @@ -128,7 +130,7 @@ func (co *CoreObserver) MonitorCore() { // startCctxScheduler schedules keysigns for cctxs on each ZetaChain block (the ticker) func (co *CoreObserver) startCctxScheduler() { - outTxMan := NewOutTxProcessorManager(co.logger.ChainLogger) + outTxMan := outtxprocessor.NewOutTxProcessorManager(co.logger.ChainLogger) observeTicker := time.NewTicker(3 * time.Second) var lastBlockNum int64 for { @@ -173,7 +175,7 @@ func (co *CoreObserver) startCctxScheduler() { co.logger.ZetaChainWatcher.Error().Err(err).Msgf("scheduleCctxEVM: failed to get prometheus gauge: %s for observer", metrics.PendingTxs) continue } // Gauge only takes float values - gauge.Set(float64(co.ts.hotKeyBurnRate.GetBurnRate().Int64())) + gauge.Set(float64(co.ts.HotKeyBurnRate.GetBurnRate().Int64())) // schedule keysign for pending cctxs on each chain supportedChains := co.Config().GetEnabledChains() @@ -223,13 +225,13 @@ func (co *CoreObserver) startCctxScheduler() { // scheduleCctxEVM schedules evm outtx keysign on each ZetaChain block (the ticker) func (co *CoreObserver) scheduleCctxEVM( - outTxMan *OutTxProcessorManager, + outTxMan *outtxprocessor.Processor, zetaHeight uint64, chainID int64, cctxList []*types.CrossChainTx, - ob ChainClient, - signer ChainSigner) { - res, err := co.bridge.GetAllOutTxTrackerByChain(chainID, Ascending) + ob interfaces.ChainClient, + signer interfaces.ChainSigner) { + res, err := co.bridge.GetAllOutTxTrackerByChain(chainID, interfaces.Ascending) if err != nil { co.logger.ZetaChainWatcher.Warn().Err(err).Msgf("scheduleCctxEVM: GetAllOutTxTrackerByChain failed for chain %d", chainID) return @@ -242,7 +244,7 @@ func (co *CoreObserver) scheduleCctxEVM( for idx, cctx := range cctxList { params := cctx.GetCurrentOutTxParam() nonce := params.OutboundTxTssNonce - outTxID := ToOutTxID(cctx.Index, params.ReceiverChainId, nonce) + outTxID := outtxprocessor.ToOutTxID(cctx.Index, params.ReceiverChainId, nonce) if params.ReceiverChainId != chainID { co.logger.ZetaChainWatcher.Error().Msgf("scheduleCctxEVM: outtx %s chainid mismatch: want %d, got %d", outTxID, chainID, params.ReceiverChainId) @@ -309,13 +311,13 @@ func (co *CoreObserver) scheduleCctxEVM( // 2. schedule keysign only when nonce-mark UTXO is available // 3. stop keysign when lookahead is reached func (co *CoreObserver) scheduleCctxBTC( - outTxMan *OutTxProcessorManager, + outTxMan *outtxprocessor.Processor, zetaHeight uint64, chainID int64, cctxList []*types.CrossChainTx, - ob ChainClient, - signer ChainSigner) { - btcClient, ok := ob.(*BitcoinChainClient) + ob interfaces.ChainClient, + signer interfaces.ChainSigner) { + btcClient, ok := ob.(*bitcoin.BTCChainClient) if !ok { // should never happen co.logger.ZetaChainWatcher.Error().Msgf("scheduleCctxBTC: chain client is not a bitcoin client") return @@ -328,7 +330,7 @@ func (co *CoreObserver) scheduleCctxBTC( for idx, cctx := range cctxList { params := cctx.GetCurrentOutTxParam() nonce := params.OutboundTxTssNonce - outTxID := ToOutTxID(cctx.Index, params.ReceiverChainId, nonce) + outTxID := outtxprocessor.ToOutTxID(cctx.Index, params.ReceiverChainId, nonce) if params.ReceiverChainId != chainID { co.logger.ZetaChainWatcher.Error().Msgf("scheduleCctxBTC: outtx %s chainid mismatch: want %d, got %d", outTxID, chainID, params.ReceiverChainId) @@ -363,7 +365,7 @@ func (co *CoreObserver) scheduleCctxBTC( } } -func (co *CoreObserver) getUpdatedChainOb(chainID int64) (ChainClient, error) { +func (co *CoreObserver) getUpdatedChainOb(chainID int64) (interfaces.ChainClient, error) { chainOb, err := co.getTargetChainOb(chainID) if err != nil { return nil, err @@ -393,7 +395,7 @@ func (co *CoreObserver) getUpdatedChainOb(chainID int64) (ChainClient, error) { return chainOb, nil } -func (co *CoreObserver) getTargetChainOb(chainID int64) (ChainClient, error) { +func (co *CoreObserver) getTargetChainOb(chainID int64) (interfaces.ChainClient, error) { c := common.GetChainFromChainID(chainID) if c == nil { return nil, fmt.Errorf("chain not found for chainID %d", chainID) @@ -404,21 +406,3 @@ func (co *CoreObserver) getTargetChainOb(chainID int64) (ChainClient, error) { } return chainOb, nil } - -// HandleBroadcastError returns whether to retry in a few seconds, and whether to report via AddTxHashToOutTxTracker -func HandleBroadcastError(err error, nonce, toChain, outTxHash string) (bool, bool) { - if strings.Contains(err.Error(), "nonce too low") { - log.Warn().Err(err).Msgf("nonce too low! this might be a unnecessary key-sign. increase re-try interval and awaits outTx confirmation") - return false, false - } - if strings.Contains(err.Error(), "replacement transaction underpriced") { - log.Warn().Err(err).Msgf("Broadcast replacement: nonce %s chain %s outTxHash %s", nonce, toChain, outTxHash) - return false, false - } else if strings.Contains(err.Error(), "already known") { // this is error code from QuickNode - log.Warn().Err(err).Msgf("Broadcast duplicates: nonce %s chain %s outTxHash %s", nonce, toChain, outTxHash) - return false, true // report to tracker, because there's possibilities a successful broadcast gets this error code - } - - log.Error().Err(err).Msgf("Broadcast error: nonce %s chain %s outTxHash %s; retrying...", nonce, toChain, outTxHash) - return true, false -} diff --git a/zetaclient/zetacore_observer_test.go b/zetaclient/zetacore_observer_test.go index 919450d1cb..a2340ea4a4 100644 --- a/zetaclient/zetacore_observer_test.go +++ b/zetaclient/zetacore_observer_test.go @@ -8,6 +8,12 @@ import ( "path/filepath" "time" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/keys" + "github.com/zeta-chain/zetacore/zetaclient/zetabridge" + + "github.com/zeta-chain/zetacore/zetaclient/evm" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/rs/zerolog/log" @@ -18,9 +24,9 @@ import ( ) type COSuite struct { - bridge1 *ZetaCoreBridge - bridge2 *ZetaCoreBridge - signer *EVMSigner + bridge1 *zetabridge.ZetaCoreBridge + bridge2 *zetabridge.ZetaCoreBridge + signer *evm.Signer coreObserver *CoreObserver } @@ -49,18 +55,18 @@ func (s *COSuite) SetUpTest(c *C) { { signerName := "alice" signerPass := "password" - kb, _, err := GetKeyringKeybase(chainHomeFoler, signerName, signerPass) + kb, _, err := keys.GetKeyringKeybase(chainHomeFoler, signerName, signerPass) if err != nil { log.Fatal().Err(err).Msg("fail to get keyring keybase") } - k := NewKeysWithKeybase(kb, signerName, signerPass) + k := keys.NewKeysWithKeybase(kb, signerName, signerPass) chainIP := os.Getenv("CHAIN_IP") if chainIP == "" { chainIP = "127.0.0.1" } - bridge, err := NewZetaCoreBridge(k, chainIP, "alice") + bridge, err := zetabridge.NewZetaCoreBridge(k, chainIP, "alice") if err != nil { c.Fail() } @@ -72,18 +78,18 @@ func (s *COSuite) SetUpTest(c *C) { { signerName := "bob" signerPass := "password" - kb, _, err := GetKeyringKeybase(chainHomeFoler, signerName, signerPass) + kb, _, err := keys.GetKeyringKeybase(chainHomeFoler, signerName, signerPass) if err != nil { log.Fatal().Err(err).Msg("fail to get keyring keybase") } - k := NewKeysWithKeybase(kb, signerName, signerPass) + k := keys.NewKeysWithKeybase(kb, signerName, signerPass) chainIP := os.Getenv("CHAIN_IP") if chainIP == "" { chainIP = "127.0.0.1" } - bridge, err := NewZetaCoreBridge(k, chainIP, "bob") + bridge, err := zetabridge.NewZetaCoreBridge(k, chainIP, "bob") if err != nil { c.Fail() } @@ -94,18 +100,18 @@ func (s *COSuite) SetUpTest(c *C) { // The following PrivKey has address 0xE80B6467863EbF8865092544f441da8fD3cF6074 privateKey, err := crypto.HexToECDSA(config.TssTestPrivkey) c.Assert(err, IsNil) - tss := TestSigner{ + tss := interfaces.TestSigner{ PrivKey: privateKey, } metaContractAddress := ethcommon.HexToAddress(config.ETH_MPI_ADDRESS) - signer, err := NewEVMSigner(common.Chain("ETH"), config.GOERLI_RPC_ENDPOINT, tss.EVMAddress(), tss, config.META_TEST_GOERLI_ABI, metaContractAddress) + signer, err := evm.NewEVMSigner(common.Chain("ETH"), config.GOERLI_RPC_ENDPOINT, tss.EVMAddress(), tss, config.META_TEST_GOERLI_ABI, metaContractAddress) c.Assert(err, IsNil) c.Logf("TSS EVMAddress %s", tss.EVMAddress().Hex()) c.Logf("ETH MPI EVMAddress: %s", config.ETH_MPI_ADDRESS) s.signer = signer - // setup zetacore observer + // setup zetabridge observer co := &CoreObserver{ bridge: s.bridge1, signer: signer, From 26e9e26566890f54d4ecc535fc82d1f8a2b6a2ee Mon Sep 17 00:00:00 2001 From: Grant Zukel <80433392+gzukel@users.noreply.github.com> Date: Mon, 12 Feb 2024 17:45:51 -0700 Subject: [PATCH 4/7] ci: Adding docker-compose for zetacored full rpc node and mainnet bitcoin node. (#1677) * Adding docker-compose for rpc full node and bitcoin node. --- .github/workflows/build.yml | 2 +- Makefile | 12 +- changelog.md | 1 + .../local-mainnet/bitcoind/docker-compose.yml | 23 + .../local-mainnet/zetacored/configs/app.toml | 77 ++ .../zetacored/configs/client.toml | 5 + .../zetacored/configs/config.toml | 116 ++ .../zetacored/configs/genesis.json | 1036 +++++++++++++++++ .../local-mainnet/zetacored/configs/start.sh | 123 ++ .../zetacored/docker-compose.yml | 34 + 10 files changed, 1427 insertions(+), 2 deletions(-) create mode 100644 contrib/local-mainnet/bitcoind/docker-compose.yml create mode 100644 contrib/local-mainnet/zetacored/configs/app.toml create mode 100644 contrib/local-mainnet/zetacored/configs/client.toml create mode 100644 contrib/local-mainnet/zetacored/configs/config.toml create mode 100644 contrib/local-mainnet/zetacored/configs/genesis.json create mode 100644 contrib/local-mainnet/zetacored/configs/start.sh create mode 100644 contrib/local-mainnet/zetacored/docker-compose.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ded55ff1e2..9c0f6058db 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -138,6 +138,6 @@ jobs: - name: Clean Up Workspace if: always() shell: bash - run: rm -rf * + run: sudo rm -rf * diff --git a/Makefile b/Makefile index 946d7a566b..03bb656d1d 100644 --- a/Makefile +++ b/Makefile @@ -293,4 +293,14 @@ release: -v `pwd`:/go/src/$(PACKAGE_NAME) \ -w /go/src/$(PACKAGE_NAME) \ ghcr.io/goreleaser/goreleaser-cross:${GOLANG_CROSS_VERSION} \ - release --clean --skip-validate \ No newline at end of file + release --clean --skip-validate + +############################################################################### +### Local Mainnet Development ### +############################################################################### + +mainnet-zetarpc-node: + cd contrib/local-mainnet/zetacored && docker-compose up + +mainnet-bitcoind-node: + cd contrib/local-mainnet/bitcoind && docker-compose up \ No newline at end of file diff --git a/changelog.md b/changelog.md index 229a1cf497..9684ba9546 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ ## Unreleased * `zetaclientd start` : 2 inputs required from stdin +* Added docker-compose and make commands for launching full nodes. `make mainnet-zetarpc-node` `make mainnet-bitcoind-node` ### Refactor diff --git a/contrib/local-mainnet/bitcoind/docker-compose.yml b/contrib/local-mainnet/bitcoind/docker-compose.yml new file mode 100644 index 0000000000..c38819ae96 --- /dev/null +++ b/contrib/local-mainnet/bitcoind/docker-compose.yml @@ -0,0 +1,23 @@ +version: '3' + +services: + bitcoin: + image: zetachain/bitcoin:35-mainnet + platform: linux/amd64 + environment: + - bitcoin_username=test + - bitcoin_password=test + - NETWORK_HEIGHT_URL=https://blockstream.info/api/blocks/tip/height + - WALLET_NAME=tssMainnet + - WALLET_ADDRESS=bc1qm24wp577nk8aacckv8np465z3dvmu7ry45el6y + - SNAPSHOT_URL=https://storage.googleapis.com/bitcoin-rpc-snapshots-prod/bitcoin.tar + - SNAPSHOT_RESTORE=true + - CLEAN_SNAPSHOT=true + - DOWNLOAD_SNAPSHOT=true + volumes: + - bitcoin_data:/root/ + ports: + - 8332:8332 + +volumes: + bitcoin_data: \ No newline at end of file diff --git a/contrib/local-mainnet/zetacored/configs/app.toml b/contrib/local-mainnet/zetacored/configs/app.toml new file mode 100644 index 0000000000..a91d81069f --- /dev/null +++ b/contrib/local-mainnet/zetacored/configs/app.toml @@ -0,0 +1,77 @@ +minimum-gas-prices = "10000000000azeta" +pruning = "nothing" +pruning-keep-recent = "0" +pruning-keep-every = "0" +pruning-interval = "0" +halt-height = 0 +halt-time = 0 +min-retain-blocks = 0 +inter-block-cache = true +index-events = [] +iavl-cache-size = 781250 +iavl-disable-fastnode = true + +[telemetry] +service-name = "tss" +enabled = true +enable-hostname = true +enable-hostname-label = true +enable-service-label = false +prometheus-retention-time = 10 +global-labels = [ ] + +[api] +enable = true +swagger = true +address = "tcp://0.0.0.0:1317" +max-open-connections = 1000 +rpc-read-timeout = 10 +rpc-write-timeout = 0 +rpc-max-body-bytes = 1000000 +enabled-unsafe-cors = false + +[rosetta] +enable = false +address = ":8080" +blockchain = "app" +network = "network" +retries = 3 +offline = false + +[grpc] +enable = true +address = "0.0.0.0:9090" + +[grpc-web] +enable = true +address = "127.0.0.1:9091" +enable-unsafe-cors = false + +[state-sync] +snapshot-interval = 0 +snapshot-keep-recent = 2 + +[evm] +tracer = "" +max-tx-gas-wanted = 0 + +[json-rpc] +enable = true +address = "0.0.0.0:8545" +ws-address = "0.0.0.0:8546" +api = "eth,net,web3,txpool,personal,debug" +gas-cap = 25000000 +evm-timeout = "5s" +txfee-cap = 1 +filter-cap = 200 +feehistory-cap = 100 +logs-cap = 10000 +block-range-cap = 10000 +http-timeout = "30s" +http-idle-timeout = "2m0s" +allow-unprotected-txs = false +max-open-connections = 0 +enable-indexer = false + +[tls] +certificate-path = "" diff --git a/contrib/local-mainnet/zetacored/configs/client.toml b/contrib/local-mainnet/zetacored/configs/client.toml new file mode 100644 index 0000000000..1bb9cc28f8 --- /dev/null +++ b/contrib/local-mainnet/zetacored/configs/client.toml @@ -0,0 +1,5 @@ +chain-id = "zetachain_7000-1" +keyring-backend = "test" +output = "text" +node = "tcp://localhost:26657" +broadcast-mode = "sync" \ No newline at end of file diff --git a/contrib/local-mainnet/zetacored/configs/config.toml b/contrib/local-mainnet/zetacored/configs/config.toml new file mode 100644 index 0000000000..830aa7b73b --- /dev/null +++ b/contrib/local-mainnet/zetacored/configs/config.toml @@ -0,0 +1,116 @@ +proxy_app = "tcp://127.0.0.1:26658" +moniker = "main-rpc" +fast_sync = true +db_backend = "pebbledb" +db_dir = "data" +log_level = "info" +log_format = "plain" +genesis_file = "config/genesis.json" +priv_validator_key_file = "config/priv_validator_key.json" +priv_validator_state_file = "data/priv_validator_state.json" +priv_validator_laddr = "" +node_key_file = "config/node_key.json" +abci = "socket" +filter_peers = false + +[rpc] +laddr = "tcp://0.0.0.0:26657" +cors_allowed_origins = [] +cors_allowed_methods = ["HEAD", "GET", "POST", ] +cors_allowed_headers = ["Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time", ] +grpc_laddr = "" +grpc_max_open_connections = 900 +unsafe = false +max_open_connections = 900 +max_subscription_clients = 100 +max_subscriptions_per_client = 5 +experimental_subscription_buffer_size = 200 +experimental_websocket_write_buffer_size = 200 +experimental_close_on_slow_client = false +timeout_broadcast_tx_commit = "10s" +max_body_bytes = 1000000 +max_header_bytes = 1048576 +tls_cert_file = "" +tls_key_file = "" +pprof_laddr = "localhost:6060" + +[p2p] +laddr = "tcp://0.0.0.0:26656" +external_address = "72.19.172.89:26656" +seeds = "8d93468c6022fb3b263963bdea46b0a131d247cd@34.28.196.79:26656" +persistent_peers = "e04ee1d6b5cc1aa24f7c1ab55139d1cec9962e39@52.45.59.77:26656" +upnp = false +addr_book_file = "config/addrbook.json" +addr_book_strict = true +max_num_inbound_peers = 120 +max_num_outbound_peers = 60 +unconditional_peer_ids = "" +persistent_peers_max_dial_period = "0s" +flush_throttle_timeout = "100ms" +max_packet_msg_payload_size = 1024 +send_rate = 5120000 +recv_rate = 5120000 +pex = true +seed_mode = false +private_peer_ids = "" +allow_duplicate_ip = true +handshake_timeout = "20s" +dial_timeout = "3s" + +[mempool] +version = "v0" +recheck = true +broadcast = true +wal_dir = "" + +size = 5000 +max_txs_bytes = 1073741824 +cache_size = 10000 +keep-invalid-txs-in-cache = false +max_tx_bytes = 1048576 +max_batch_bytes = 0 +ttl-duration = "0s" +ttl-num-blocks = 0 + +[statesync] +enable = "true" +rpc_servers = "34.69.20.168:26657,34.69.20.168:26657" +trust_height = "1542228" +trust_hash = "5B3377FE8F1D2A64E56D203C205AA68C64BB95269C9BD1B199B79225B4373BB0" +trust_period = "168h0m0s" +discovery_time = "15s" +temp_dir = "" +chunk_request_timeout = "10s" +chunk_fetchers = "4" + +[fastsync] +version = "v0" + +[consensus] +wal_file = "data/cs.wal/wal" +timeout_propose = "3s" +timeout_propose_delta = "500ms" +timeout_prevote = "1s" +timeout_prevote_delta = "500ms" +timeout_precommit = "1s" +timeout_precommit_delta = "500ms" +timeout_commit = "5s" +double_sign_check_height = 0 +skip_timeout_commit = false +create_empty_blocks = true +create_empty_blocks_interval = "0s" +peer_gossip_sleep_duration = "100ms" +peer_query_maj23_sleep_duration = "2s" + +[storage] +discard_abci_responses = false + +[tx_index] +indexer = "kv" +psql-conn = "" + +[instrumentation] +prometheus = true +prometheus_listen_addr = ":26660" +max_open_connections = 3 +namespace = "tendermint" diff --git a/contrib/local-mainnet/zetacored/configs/genesis.json b/contrib/local-mainnet/zetacored/configs/genesis.json new file mode 100644 index 0000000000..9a38f691bf --- /dev/null +++ b/contrib/local-mainnet/zetacored/configs/genesis.json @@ -0,0 +1,1036 @@ +{ + "genesis_time": "2023-10-19T19:00:00Z", + "chain_id": "zetachain_7000-1", + "initial_height": "1", + "consensus_params": { + "block": { + "max_bytes": "22020096", + "max_gas": "10000000", + "time_iota_ms": "1000" + }, + "evidence": { + "max_age_num_blocks": "100000", + "max_age_duration": "172800000000000", + "max_bytes": "1048576" + }, + "validator": { + "pub_key_types": [ + "ed25519" + ] + }, + "version": {} + }, + "app_hash": "", + "app_state": { + "auth": { + "params": { + "max_memo_characters": "256", + "tx_sig_limit": "7", + "tx_size_cost_per_byte": "10", + "sig_verify_cost_ed25519": "590", + "sig_verify_cost_secp256k1": "1000" + }, + "accounts": [ + { + "@type": "/ethermint.types.v1.EthAccount", + "base_account": { + "address": "zeta1hjct6q7npsspsg3dgvzk3sdf89spmlpf7rqmnw", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + }, + { + "@type": "/ethermint.types.v1.EthAccount", + "base_account": { + "address": "zeta1p0uwsq4naus5r4l7l744upy0k8ezzj84mn40nf", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + }, + { + "@type": "/ethermint.types.v1.EthAccount", + "base_account": { + "address": "zeta1l07weaxkmn6z69qm55t53v4rfr43eys4cjz54h", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + }, + { + "@type": "/ethermint.types.v1.EthAccount", + "base_account": { + "address": "zeta1t5pgk2fucx3drkynzew9zln5z9r7s3wqqyy0pe", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + }, + { + "@type": "/ethermint.types.v1.EthAccount", + "base_account": { + "address": "zeta1t0uj2z93jd2g3w94zl3jhfrn2ek6dnuk3v93j9", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + }, + { + "@type": "/ethermint.types.v1.EthAccount", + "base_account": { + "address": "zeta1k6vh9y7ctn06pu5jngznv5dyy0rltl2qp0j30g", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + }, + { + "@type": "/ethermint.types.v1.EthAccount", + "base_account": { + "address": "zeta19jr7nl82lrktge35f52x9g5y5prmvchmk40zhg", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + }, + { + "@type": "/ethermint.types.v1.EthAccount", + "base_account": { + "address": "zeta1cxj07f3ju484ry2cnnhxl5tryyex7gev0yzxtj", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + }, + { + "@type": "/ethermint.types.v1.EthAccount", + "base_account": { + "address": "zeta1v66xndg92tkt9ay70yyj3udaq0ej9wl765r7lf", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + } + ] + }, + "authz": { + "authorization": [] + }, + "bank": { + "params": { + "send_enabled": [], + "default_send_enabled": false + }, + "balances": [ + { + "address": "zeta1p0uwsq4naus5r4l7l744upy0k8ezzj84mn40nf", + "coins": [ + { + "denom": "azeta", + "amount": "20000000000000000000" + } + ] + }, + { + "address": "zeta19jr7nl82lrktge35f52x9g5y5prmvchmk40zhg", + "coins": [ + { + "denom": "azeta", + "amount": "50000000000000000000" + } + ] + }, + { + "address": "zeta1t0uj2z93jd2g3w94zl3jhfrn2ek6dnuk3v93j9", + "coins": [ + { + "denom": "azeta", + "amount": "20000000000000000000" + } + ] + }, + { + "address": "zeta1t5pgk2fucx3drkynzew9zln5z9r7s3wqqyy0pe", + "coins": [ + { + "denom": "azeta", + "amount": "20000000000000000000" + } + ] + }, + { + "address": "zeta1v66xndg92tkt9ay70yyj3udaq0ej9wl765r7lf", + "coins": [ + { + "denom": "azeta", + "amount": "50000000000000000000" + } + ] + }, + { + "address": "zeta1k6vh9y7ctn06pu5jngznv5dyy0rltl2qp0j30g", + "coins": [ + { + "denom": "azeta", + "amount": "50000000000000000000" + } + ] + }, + { + "address": "zeta1hjct6q7npsspsg3dgvzk3sdf89spmlpf7rqmnw", + "coins": [ + { + "denom": "azeta", + "amount": "20000000000000000000" + } + ] + }, + { + "address": "zeta1cxj07f3ju484ry2cnnhxl5tryyex7gev0yzxtj", + "coins": [ + { + "denom": "azeta", + "amount": "50000000000000000000" + } + ] + }, + { + "address": "zeta1l07weaxkmn6z69qm55t53v4rfr43eys4cjz54h", + "coins": [ + { + "denom": "azeta", + "amount": "20000000000000000000" + } + ] + } + ], + "supply": [ + { + "denom": "azeta", + "amount": "300000000000000000000" + } + ], + "denom_metadata": [ + { + "description": "The native token of ZetaChain", + "denom_units": [ + { + "denom": "azeta", + "exponent": 0, + "aliases": [ + "attozeta" + ] + }, + { + "denom": "zeta", + "exponent": 18, + "aliases": [] + } + ], + "base": "azeta", + "display": "zeta", + "name": "ZetaChain", + "symbol": "ZETA", + "uri": "", + "uri_hash": "" + } + ] + }, + "crisis": { + "constant_fee": { + "denom": "azeta", + "amount": "1000" + } + }, + "crosschain": { + "params": { + "enabled": false + }, + "outTxTrackerList": [], + "tss": null, + "gasPriceList": [], + "chainNoncesList": [], + "CrossChainTxs": [], + "lastBlockHeightList": [], + "inTxHashToCctxList": [], + "tss_history": [] + }, + "distribution": { + "params": { + "community_tax": "0.0", + "base_proposer_reward": "0.010000000000000000", + "bonus_proposer_reward": "0.040000000000000000", + "withdraw_addr_enabled": true + }, + "fee_pool": { + "community_pool": [] + }, + "delegator_withdraw_infos": [], + "previous_proposer": "", + "outstanding_rewards": [], + "validator_accumulated_commissions": [], + "validator_historical_rewards": [], + "validator_current_rewards": [], + "delegator_starting_infos": [], + "validator_slash_events": [] + }, + "emissions": { + "params": { + "max_bond_factor": "1.25", + "min_bond_factor": "0.75", + "avg_block_time": "6.00", + "target_bond_ratio": "00.67", + "validator_emission_percentage": "0.75", + "observer_emission_percentage": "0.125", + "tss_signer_emission_percentage": "0.125", + "duration_factor_constant": "0.001877876953694702", + "observer_slash_amount": "100000000000000000" + }, + "withdrawableEmissions": [] + }, + "evidence": { + "evidence": [] + }, + "evm": { + "accounts": [], + "params": { + "evm_denom": "azeta", + "enable_create": true, + "enable_call": true, + "extra_eips": [], + "chain_config": { + "homestead_block": "0", + "dao_fork_block": "0", + "dao_fork_support": true, + "eip150_block": "0", + "eip150_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "eip155_block": "0", + "eip158_block": "0", + "byzantium_block": "0", + "constantinople_block": "0", + "petersburg_block": "0", + "istanbul_block": "0", + "muir_glacier_block": "0", + "berlin_block": "0", + "london_block": "0", + "arrow_glacier_block": "0", + "gray_glacier_block": "0", + "merge_netsplit_block": "0", + "shanghai_block": "0", + "cancun_block": "0" + }, + "allow_unprotected_txs": false + } + }, + "feemarket": { + "params": { + "no_base_fee": false, + "base_fee_change_denominator": 8, + "elasticity_multiplier": 2, + "enable_height": "0", + "base_fee": "1000000000", + "min_gas_price": "0.000000000000000000", + "min_gas_multiplier": "0.500000000000000000" + }, + "block_gas": "0" + }, + "fungible": { + "params": {}, + "foreignCoinsList": [], + "systemContract": null + }, + "genutil": { + "gen_txs": [ + { + "body": { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "Blockdaemon", + "identity": "", + "website": "blockdaemon.com", + "security_contact": "support@blockdaemon.com", + "details": "" + }, + "commission": { + "rate": "0.100000000000000000", + "max_rate": "0.200000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "zeta1t5pgk2fucx3drkynzew9zln5z9r7s3wqqyy0pe", + "validator_address": "zetavaloper1t5pgk2fucx3drkynzew9zln5z9r7s3wqyyunv0", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "DUKST69tW93h1OSZKQyNNQG1DnZoeY/XDfFuz0te0yg=" + }, + "value": { + "denom": "azeta", + "amount": "10000000000000000000" + } + } + ], + "memo": "c692e280a93124c678de1748914b146cf6210363@172.17.0.7:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [ + { + "public_key": { + "@type": "/ethermint.crypto.v1.ethsecp256k1.PubKey", + "key": "Ajsf++7EyYAUl4ZntfDhy7nHlXvm3hDDL0MKVwmoJ9z6" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_DIRECT" + } + }, + "sequence": "0" + } + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + }, + "tip": null + }, + "signatures": [ + "D6on5wk8WteTfxlyXkREw4fnnBSeiwFqRCDyMoH1y15fQ+AWW4szXf6SpWn5USbUNkMLPkM4s7mpD9D/mx6upgA=" + ] + }, + { + "body": { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "Figment", + "identity": "E5F274B870BDA01D", + "website": "https://figment.io", + "security_contact": "security@figment.io", + "details": "Figment" + }, + "commission": { + "rate": "0.100000000000000000", + "max_rate": "1.000000000000000000", + "max_change_rate": "1.000000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "zeta1hjct6q7npsspsg3dgvzk3sdf89spmlpf7rqmnw", + "validator_address": "zetavaloper1hjct6q7npsspsg3dgvzk3sdf89spmlpf6rc87c", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "4P7Gx9WWjk+pYDe9jO/+njjlf5jMEoafwM3SQBj//pI=" + }, + "value": { + "denom": "azeta", + "amount": "10000000000000000000" + } + } + ], + "memo": "3ad52e6da939646c52533390b6e21fc988d12c9d@192.168.2.112:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [ + { + "public_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "A/ME5Wre92HJOJH4wBFveSeq3Yj8wZJrn/pEWc3pcrSc" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_LEGACY_AMINO_JSON" + } + }, + "sequence": "0" + } + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + }, + "tip": null + }, + "signatures": [ + "gA6rkVoGWvcUhXeuPR+SL3ZylNGrQ92wBcy9nFpqBz5wG9bVvC5OsKVUnxMy0pBWRw98iHU+TkWpQsEOC31PKg==" + ] + }, + { + "body": { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "InfStones", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "commission": { + "rate": "0.100000000000000000", + "max_rate": "0.200000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "zeta1t0uj2z93jd2g3w94zl3jhfrn2ek6dnuk3v93j9", + "validator_address": "zetavaloper1t0uj2z93jd2g3w94zl3jhfrn2ek6dnuk4vadln", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "/YSea9QT51BaUh2g/xzXpghOUui/yTYtBETHwR+/1Zg=" + }, + "value": { + "denom": "azeta", + "amount": "10000000000000000000" + } + } + ], + "memo": "d2674e8511c312a67f6826898480d1a7079d0f25@172.0.2.219:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [ + { + "public_key": { + "@type": "/ethermint.crypto.v1.ethsecp256k1.PubKey", + "key": "A6qTNepZbDodLk2Z0y77CHRn4A/YoVJxUeEgJtRtMlzJ" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_DIRECT" + } + }, + "sequence": "0" + } + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + }, + "tip": null + }, + "signatures": [ + "GDST6l1b2qM2AVQxGBgQ9BkGmbqh+55IRdnhqYPXpRFQAeGWB7ZtGFa1JXokvGxHQgebYdvsRl1/a6LMj5pkfgE=" + ] + }, + { + "body": { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "bastion-2", + "identity": "", + "website": "https://bastiondigital.com/", + "security_contact": "security@bastiondigital.com", + "details": "" + }, + "commission": { + "rate": "0.100000000000000000", + "max_rate": "0.200000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "zeta1l07weaxkmn6z69qm55t53v4rfr43eys4cjz54h", + "validator_address": "zetavaloper1l07weaxkmn6z69qm55t53v4rfr43eys4uj6gcp", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "/KKLqTvG+NsmHcKmPzXHKCscGmuNxTGfQ3WVdMOgOC0=" + }, + "value": { + "denom": "azeta", + "amount": "10000000000000000000" + } + } + ], + "memo": "c37f642698260707c0e25a8895f9d36735318ef3@172.31.57.153:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [ + { + "public_key": { + "@type": "/ethermint.crypto.v1.ethsecp256k1.PubKey", + "key": "ArsnYUKbYMRVuXZTtU2ApmRfU+y7/IF0fXjYr5VtWG0W" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_DIRECT" + } + }, + "sequence": "0" + } + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + }, + "tip": null + }, + "signatures": [ + "P1FY9Xalsue3TQz7oZStxxzexjM8LjQD10xcHsJJcdlX/toL1n2Muj/Prz9h8306KQrTkK3yexEjElxxy9WM2gA=" + ] + }, + { + "body": { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "bastion", + "identity": "", + "website": "https://bastiondigital.com/", + "security_contact": "security@bastiondigital.com", + "details": "" + }, + "commission": { + "rate": "0.100000000000000000", + "max_rate": "0.200000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "zeta1p0uwsq4naus5r4l7l744upy0k8ezzj84mn40nf", + "validator_address": "zetavaloper1p0uwsq4naus5r4l7l744upy0k8ezzj84lndn7l", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "4OBmT48Oox4IC+lyvsw5WrDTrY8+ntQ1FwkIlbZ1TtI=" + }, + "value": { + "denom": "azeta", + "amount": "10000000000000000000" + } + } + ], + "memo": "01de75368e56b38fc27a22c07236d50319ed129d@172.31.48.193:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [ + { + "public_key": { + "@type": "/ethermint.crypto.v1.ethsecp256k1.PubKey", + "key": "AyVhi69idWFuCGiJxsYHZ2IB4U/M64i7Q602PEVzZpNo" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_DIRECT" + } + }, + "sequence": "0" + } + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + }, + "tip": null + }, + "signatures": [ + "x91CvYs0ySWDZs7DvXnLN6SI06SBa+2LqL0d/BDJWtYdZ0ce8nwiBn54jMQqkCbjrqmsnhGH5c3g2bjtu2zhBwA=" + ] + }, + { + "body": { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "MP1", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "commission": { + "rate": "0.100000000000000000", + "max_rate": "0.200000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "zeta1cxj07f3ju484ry2cnnhxl5tryyex7gev0yzxtj", + "validator_address": "zetavaloper1cxj07f3ju484ry2cnnhxl5tryyex7gevty66xy", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "DsZWn+sZ93S1B4G3mNb2a1V8akbmCWLysqQ3XaQjctc=" + }, + "value": { + "denom": "azeta", + "amount": "10000000000000000000" + } + } + ], + "memo": "7069d3e30752526ab512b43cdd7aca1012d9a142@52.35.128.130:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [ + { + "public_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "Atnv3L9LKUn+uhJQSKz159H7T3Ip8BAJyoiK4/ABSUNz" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_LEGACY_AMINO_JSON" + } + }, + "sequence": "0" + } + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + }, + "tip": null + }, + "signatures": [ + "sffe9xziD6vesvv5bib7kD+UGYUIwjNx1PuA/PyfioEuUEYaCPfuz7aWD0cTNp5XEMm5nXjnDqYomB5iA7ucdw==" + ] + }, + { + "body": { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "MP4", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "commission": { + "rate": "0.100000000000000000", + "max_rate": "0.200000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "zeta1v66xndg92tkt9ay70yyj3udaq0ej9wl765r7lf", + "validator_address": "zetavaloper1v66xndg92tkt9ay70yyj3udaq0ej9wl775mzjl", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "4pZgiUYPEW5o7UTc9QxE5rsXQ3uQqbTPVSjbzXJrLGM=" + }, + "value": { + "denom": "azeta", + "amount": "20000000000000000000" + } + } + ], + "memo": "4d7a52d68af698c296211dec34a26cddefeb0b06@44.236.207.180:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [ + { + "public_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "A033XHSNuUwe0gA1GehbAN8G+lvz9zEFFEMOtMPac1gm" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_LEGACY_AMINO_JSON" + } + }, + "sequence": "0" + } + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + }, + "tip": null + }, + "signatures": [ + "eWLHyIM4yxOdDJWmdg6JoGAeZoST/5p/orkFtbt1Y28+Qi8yUpCUy3J+Tmh6Tq0kt2qyURzUQru75m3QGBRoAA==" + ] + }, + { + "body": { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "OmniChain1", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "commission": { + "rate": "0.100000000000000000", + "max_rate": "0.200000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "zeta1k6vh9y7ctn06pu5jngznv5dyy0rltl2qp0j30g", + "validator_address": "zetavaloper1k6vh9y7ctn06pu5jngznv5dyy0rltl2q902dz7", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "P7Wpyskb7H1ybAGX6DZbjjOztuBfVqf7DkuPiyy5x6Q=" + }, + "value": { + "denom": "azeta", + "amount": "20000000000000000000" + } + } + ], + "memo": "d32b3e22cfcb72e11050db30a59a59cd05646046@34.225.36.174:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [ + { + "public_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "AkaccaPHHvcOgOOo9lmsYHpS2d0UibouaY40mI7G3IaW" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_LEGACY_AMINO_JSON" + } + }, + "sequence": "0" + } + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + }, + "tip": null + }, + "signatures": [ + "gHJ25zQvJr4fdPi4/IS1DzyV1cSmfGVJfVIjL9X8lI8ZXMbB73FSFukC7yPq1ghxlsSyq3vK32zVIIjEfcGsLA==" + ] + }, + { + "body": { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "OmniChain2", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "commission": { + "rate": "0.100000000000000000", + "max_rate": "0.200000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "zeta19jr7nl82lrktge35f52x9g5y5prmvchmk40zhg", + "validator_address": "zetavaloper19jr7nl82lrktge35f52x9g5y5prmvchmj4h767", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "XMwY/ubWmBihvwta4i6BoKp6j+L8HkpfBEiedXAkBEI=" + }, + "value": { + "denom": "azeta", + "amount": "20000000000000000000" + } + } + ], + "memo": "e04ee1d6b5cc1aa24f7c1ab55139d1cec9962e39@52.45.59.77:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [ + { + "public_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "Ag3Ki+x4fR1/Rw38S4CizA73rf5g24PDoDCpbaBbTYSM" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_LEGACY_AMINO_JSON" + } + }, + "sequence": "0" + } + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + }, + "tip": null + }, + "signatures": [ + "UPcQ92gSdeAjF438vN8E+W6HbqE9EimprQTMOiBCgyURI0DVD4SlyHaJyWJsaXzwNW0bWkWpWwA0pje366KQUg==" + ] + } + ] + }, + "gov": { + "starting_proposal_id": "1", + "deposits": [], + "votes": [], + "proposals": [], + "deposit_params": { + "min_deposit": [ + { + "denom": "azeta", + "amount": "100000000000000000" + } + ], + "max_deposit_period": "1209600s" + }, + "voting_params": { + "voting_period": "86400s" + }, + "tally_params": { + "quorum": "0.4", + "threshold": "0.500000000000000000", + "veto_threshold": "0.334000000000000000" + } + }, + "group": { + "group_seq": "0", + "groups": [], + "group_members": [], + "group_policy_seq": "0", + "group_policies": [], + "proposal_seq": "0", + "proposals": [], + "votes": [] + }, + "mint": { + "params": { + "mint_denom": "azeta" + } + }, + "observer": { + "ballots": [], + "observers": [], + "nodeAccountList": [], + "crosschain_flags": { + "isInboundEnabled": true, + "isOutboundEnabled": true, + "gasPriceIncreaseFlags": null + }, + "params": { + "observer_params": [ + { + "chain": { + "chain_name": "btc_mainnet", + "chain_id": "8332" + }, + "ballot_threshold": "0.660000000000000000", + "min_observer_delegation": "1000000000000000000.000000000000000000", + "is_supported": true + }, + { + "chain": { + "chain_name": "bsc_mainnet", + "chain_id": "56" + }, + "ballot_threshold": "0.660000000000000000", + "min_observer_delegation": "1000000000000000000.000000000000000000", + "is_supported": true + }, + { + "chain": { + "chain_name": "eth_mainnet", + "chain_id": "1" + }, + "ballot_threshold": "0.660000000000000000", + "min_observer_delegation": "1000000000000000000.000000000000000000", + "is_supported": true + } + ], + "admin_policy": [], + "ballot_maturity_blocks": "100" + }, + "keygen": { + "status": "PendingKeygen", + "granteePubkeys": [], + "blockNumber": "9223372036854775807" + }, + "last_observer_count": null, + "core_params_list": { + "core_params": [] + } + }, + "params": null, + "slashing": { + "params": { + "signed_blocks_window": "5000", + "min_signed_per_window": "0.500000000000000000", + "downtime_jail_duration": "600s", + "slash_fraction_double_sign": "0.050000000000000000", + "slash_fraction_downtime": "0.010000000000000000" + }, + "signing_infos": [], + "missed_blocks": [] + }, + "staking": { + "params": { + "unbonding_time": "1814400s", + "max_validators": 125, + "max_entries": 7, + "historical_entries": 10000, + "bond_denom": "azeta", + "min_commission_rate": "0.05" + }, + "last_total_power": "0", + "last_validator_powers": [], + "validators": [], + "delegations": [], + "unbonding_delegations": [], + "redelegations": [], + "exported": false + }, + "upgrade": {}, + "vesting": {} + } +} diff --git a/contrib/local-mainnet/zetacored/configs/start.sh b/contrib/local-mainnet/zetacored/configs/start.sh new file mode 100644 index 0000000000..9452b9aec7 --- /dev/null +++ b/contrib/local-mainnet/zetacored/configs/start.sh @@ -0,0 +1,123 @@ +#!/bin/bash + +function logt(){ + echo "$(date) - $1" +} + +function download_binary() { + wget https://github.com/zeta-chain/node/releases/download/${BINARY_VERSION}/zetacored-darwin-amd64 -O /usr/local/bin/zetacored + chmod a+x /usr/local/bin/zetacored + zetacored version || echo "BINARY NOT INSTALLED" && exit 1 +} + +function chain_init() { + ZETACORED_DIR="$HOME/.zetacored" + # Check if the .zetacored directory exists + if [ -d "$ZETACORED_DIR" ]; then + echo ".zetacored directory already exists at $ZETACORED_DIR." + else + # Directory does not exist, initialize zetacored + zetacored init "$MONIKER" --chain-id "$CHAIN_ID" + echo ".zetacored initialized for $MONIKER with chain ID $CHAIN_ID." + fi +} + +function modify_chain_configs() { + sed -i -e "s/^enable = .*/enable = \"true\"/" /root/.zetacored/config/config.toml + sed -i -e "s/^rpc_servers = .*/rpc_servers = \"${RPC_STATE_SYNC_SERVERS}\"/" /root/.zetacored/config/config.toml + sed -i -e "s/^trust_height = .*/trust_height = \"${HEIGHT}\"/" /root/.zetacored/config/config.toml + sed -i -e "s/^trust_hash = .*/trust_hash = \"${TRUST_HASH}\"/" /root/.zetacored/config/config.toml + sed -i -e "s/^moniker = .*/moniker = \"${MONIKER}\"/" /root/.zetacored/config/config.toml + sed -i -e "s/^external_address = .*/external_address = \"${EXTERNAL_IP}:26656\"/" /root/.zetacored/config/config.toml + sed -i -e "s/^seeds = .*/seeds = \"${SEED}\"/" /root/.zetacored/config/config.toml + sed -i -e 's/^max_num_inbound_peers = .*/max_num_inbound_peers = 120/' /root/.zetacored/config/config.toml + sed -i -e 's/^max_num_outbound_peers = .*/max_num_outbound_peers = 60/' /root/.zetacored/config/config.toml + sed -i -e "s/^persistent_peers = .*/persistent_peers = \"${PERSISTENT_PEERS}\"/" /root/.zetacored/config/config.toml +} + +function setup_basic_keyring() { + if zetacored keys show "$MONIKER" --keyring-backend test > /dev/null 2>&1; then + echo "Key $MONIKER already exists." + else + zetacored keys add "$MONIKER" --keyring-backend test + echo "Key $MONIKER created." + fi +} + +function start_network() { + zetacored start --home /root/.zetacored/ \ + --log_level info \ + --moniker ${MONIKER} \ + --rpc.laddr tcp://0.0.0.0:26657 \ + --minimum-gas-prices 1.0azeta "--grpc.enable=true" +} + +function install_dependencies() { + apt-get update + apt-get install nano jq -y +} + +function check_configs_debug() { + logt "Check home config directory ensure configs present." + ls -lah /root/.zetacored/config + + logt "Check the zetacored binary is in /usr/local/bin" + ls -lah /usr/local/bin/ + + logt "Check zetacored root directory" + ls -lah /root/.zetacored + + logt "Config.toml" + cat /root/.zetacored/config/config.toml + logt "******" + + logt "Config.toml" + cat /root/.zetacored/config/app.toml + logt "******" + + logt "Config.toml" + cat /root/.zetacored/config/client.toml + logt "******" + + logt "Config.toml" + cat /root/.zetacored/config/genesis.json + logt "******" +} + +logt "Install Dependencies" +install_dependencies + +if [ "${DEBUG}" == "true" ]; then + check_configs_debug +fi + +logt "Setup script variables." +export STATE_SYNC_SERVER="${STATE_SYNC_SERVER}" +export TRUST_HEIGHT=$(curl -s http://${STATE_SYNC_SERVER}/block | jq -r '.result.block.header.height') +#export HEIGHT=$((TRUST_HEIGHT-40000)) +export HEIGHT=$((TRUST_HEIGHT-100)) +export TRUST_HASH=$(curl -s "http://${STATE_SYNC_SERVER}/block?height=${HEIGHT}" | jq -r '.result.block_id.hash') +export RPC_STATE_SYNC_SERVERS="${RPC_STATE_SYNC_SERVERS}" +export SEED="${SEED_NODE}" +export PERSISTENT_PEERS="${PEERS}" +export EXTERNAL_IP=$(curl -4 icanhazip.com) + +if [ "$DOWNLOAD_BINARY" = true ]; then + logt "Download chain binary" + download_binary +else + logt "User built binary." +fi + +logt "Init the chain directory" +chain_init + +logt "Modify chain configs." +modify_chain_configs + +if [ "${DEBUG}" == "true" ]; then + check_configs_debug +fi + +logt "Start network" +start_network diff --git a/contrib/local-mainnet/zetacored/docker-compose.yml b/contrib/local-mainnet/zetacored/docker-compose.yml new file mode 100644 index 0000000000..7dc44b736a --- /dev/null +++ b/contrib/local-mainnet/zetacored/docker-compose.yml @@ -0,0 +1,34 @@ +version: '3.8' +services: + zetachain_mainnet_rpc: + platform: linux/amd64 + build: + context: ../.. + dockerfile: Dockerfile + #image: golang:1.21 + container_name: zetachain_mainnet_rpc + environment: + CHAIN_ID: "zetachain_7000-1" + BINARY_VERSION: "N/A" + MONIKER: "main-rpc" + STATE_SYNC_SERVER: "34.69.20.168:26657" + RPC_STATE_SYNC_SERVERS: "34.69.20.168:26657,34.69.20.168:26657" + SEED_NODE: "8d93468c6022fb3b263963bdea46b0a131d247cd@34.28.196.79:26656" + PEERS: "e04ee1d6b5cc1aa24f7c1ab55139d1cec9962e39@52.45.59.77:26656" + DOWNLOAD_BINARY: "false" + DEBUG: "true" + ports: + - "26656:26656" + - "1317:1317" + - "8545:8545" + - "8546:8546" + - "26657:26657" + - "9090:9090" + - "9091:9091" + volumes: + - ./configs:/root/.zetacored/config + - zetacored_data:/root/.zetacored/data + entrypoint: bash /root/.zetacored/config/start.sh + +volumes: + zetacored_data: From b4d6080a85bae228acaeb065c8701bd44507b8ec Mon Sep 17 00:00:00 2001 From: kevinssgh <79858682+kevinssgh@users.noreply.github.com> Date: Tue, 13 Feb 2024 07:52:09 -0500 Subject: [PATCH 5/7] docs: add doc for encryption password entry (#1731) * added doc for encryption password entry * add changelog * added more details * Update docs/zetaclient/hotkey_tss_passwords.md Co-authored-by: Lucas Bertrand * Update docs/zetaclient/hotkey_tss_passwords.md Co-authored-by: Lucas Bertrand * update filename * rename file again * added docs section to changelog --------- Co-authored-by: Lucas Bertrand --- changelog.md | 4 +++ docs/zetaclient/migration_v12.2->v12.3.md | 34 +++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 docs/zetaclient/migration_v12.2->v12.3.md diff --git a/changelog.md b/changelog.md index 9684ba9546..e148ae47af 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,10 @@ * `zetaclientd start` : 2 inputs required from stdin * Added docker-compose and make commands for launching full nodes. `make mainnet-zetarpc-node` `make mainnet-bitcoind-node` +### Docs + +* [1731](https://github.com/zeta-chain/node/pull/1731) added doc for hotkey and tss key-share password prompts. + ### Refactor * [1630](https://github.com/zeta-chain/node/pull/1630) added password prompts for hotkey and tss keyshare in zetaclient diff --git a/docs/zetaclient/migration_v12.2->v12.3.md b/docs/zetaclient/migration_v12.2->v12.3.md new file mode 100644 index 0000000000..0ad72bd917 --- /dev/null +++ b/docs/zetaclient/migration_v12.2->v12.3.md @@ -0,0 +1,34 @@ +## Hot Key and TSS key-share Passwords + +### Zetaclient +Previously there were two environment variables being used to store passwords encrypting the tss key file and local operator keyring file: + +* HOTKEY_PASSWORD +* TSS_FRAGMENT_SEED + +With this new change, these variables will no longer be valid. +Instead, a series of prompts will appear asking for passwords using STDIN during the startup process. + +* Hot Key password +* TSS Key share password + +If your key files are already encrypted, you can use the same passwords you provided in the environment variables. + +**It's extremely important to take note of these passwords or commit them to memory.** + +### Hot Key + +#### File backend + +* The hot key will use the existing keyring that holds your operator key. The file will be encrypted with your existing password, +make sure to use this same password when starting the client. + +#### Test backend + +* You will still be prompted for a password, but you need to leave it blank which indicates the test backend is being used. + +### TSS Key-Share + +During key-gen, the password you enter will be used to encrypt the generated key-share file. The key data will be stored in +memory once the process is running. If the client needs to be restarted, this key-share file needs to be present on your +machine and will be decrypted using the password you've entered. \ No newline at end of file From 68f8ed97986be9874e2db60a356b6f61ac6aa210 Mon Sep 17 00:00:00 2001 From: Charlie Chen <34498985+ws4charlie@users.noreply.github.com> Date: Tue, 13 Feb 2024 07:10:49 -0600 Subject: [PATCH 6/7] fix: evm outtx hash mismatch (#1750) * fix evm outtx hash mismatch * clean cached stale block to fix evm outtx hash mismatch --------- Co-authored-by: Lucas Bertrand --- changelog.md | 3 ++- zetaclient/evm/evm_client.go | 8 +++++++ zetaclient/evm/evm_client_test.go | 37 +++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 zetaclient/evm/evm_client_test.go diff --git a/changelog.md b/changelog.md index e148ae47af..2d8f311c7c 100644 --- a/changelog.md +++ b/changelog.md @@ -16,12 +16,13 @@ ### Fixes +* [1678](https://github.com/zeta-chain/node/issues/1678) - clean cached stale block to fix evm outtx hash mismatch * [1690](https://github.com/zeta-chain/node/issues/1690) - double watched gas prices and fix btc scheduler * [1687](https://github.com/zeta-chain/node/pull/1687) - only use EVM supported chains for gas stability pool * [1692](https://github.com/zeta-chain/node/pull/1692) - fix get params query for emissions module * [1707](https://github.com/zeta-chain/node/issues/1707) - fix bitcoin fee rate estimation * [1712](https://github.com/zeta-chain/node/issues/1712) - increase EVM outtx inclusion timeout to 20 minutes -* [1733](https://github.com/zeta-chain/node/pull/1733)) - remove the unnecessary 2x multiplier in the convertGasToZeta RPC +* [1733](https://github.com/zeta-chain/node/pull/1733) - remove the unnecessary 2x multiplier in the convertGasToZeta RPC * [1721](https://github.com/zeta-chain/node/issues/1721) - zetaclient should provide bitcoin_chain_id when querying TSS address * [1744](https://github.com/zeta-chain/node/pull/1744) - added cmd to encrypt tss keyshare file, allowing empty tss password for backward compatibility. diff --git a/zetaclient/evm/evm_client.go b/zetaclient/evm/evm_client.go index 97b29495f6..db13df1f6f 100644 --- a/zetaclient/evm/evm_client.go +++ b/zetaclient/evm/evm_client.go @@ -799,6 +799,7 @@ func (ob *ChainClient) checkTxInclusion(tx *ethtypes.Transaction, blockNumber ui } txAtIndex := block.Transactions()[txIndex] if txAtIndex.Hash() != tx.Hash() { + ob.RemoveCachedBlock(blockNumber) // clean stale block from cache return fmt.Errorf("transaction at index %d has different hash %s, txHash %s nonce %d block %d", txIndex, txAtIndex.Hash().Hex(), tx.Hash(), tx.Nonce(), blockNumber) } @@ -810,6 +811,7 @@ func (ob *ChainClient) checkTxInclusion(tx *ethtypes.Transaction, blockNumber ui } txAtIndex := blockRPC.Transactions[txIndex] if ethcommon.HexToHash(txAtIndex.Hash) != tx.Hash() { + ob.RemoveCachedBlock(blockNumber) // clean stale block from cache return fmt.Errorf("transaction at index %d has different hash %s, txHash %s nonce %d block %d", txIndex, txAtIndex.Hash, tx.Hash(), tx.Nonce(), blockNumber) } @@ -1520,3 +1522,9 @@ func (ob *ChainClient) GetBlockByNumberCached(blockNumber uint64) (*ethtypes.Blo ob.blockCache.Add(blockNumber, block) return block, nil, false, false, nil } + +// RemoveCachedBlock remove block from cache +func (ob *ChainClient) RemoveCachedBlock(blockNumber uint64) { + ob.blockCache.Remove(blockNumber) + ob.blockCacheV3.Remove(blockNumber) +} diff --git a/zetaclient/evm/evm_client_test.go b/zetaclient/evm/evm_client_test.go new file mode 100644 index 0000000000..730cba83f9 --- /dev/null +++ b/zetaclient/evm/evm_client_test.go @@ -0,0 +1,37 @@ +package evm + +import ( + "math/big" + "testing" + + ethtypes "github.com/ethereum/go-ethereum/core/types" + lru "github.com/hashicorp/golang-lru" + "github.com/stretchr/testify/require" +) + +func TestEVMBlockCache(t *testing.T) { + // create client + blockCache, err := lru.New(1000) + require.NoError(t, err) + blockCacheV3, err := lru.New(1000) + require.NoError(t, err) + ob := ChainClient{ + blockCache: blockCache, + blockCacheV3: blockCacheV3, + } + + // delete non-existing block should not panic + blockNumber := int64(10388180) + // #nosec G701 possible nummber + ob.RemoveCachedBlock(uint64(blockNumber)) + + // add a block + header := ðtypes.Header{ + Number: big.NewInt(blockNumber), + } + block := ethtypes.NewBlock(header, nil, nil, nil, nil) + ob.blockCache.Add(blockNumber, block) + + // delete the block should not panic + ob.RemoveCachedBlock(uint64(blockNumber)) +} From 52ffaa871d9a707e211ca038cfde691d008bd7f0 Mon Sep 17 00:00:00 2001 From: Grant Zukel <80433392+gzukel@users.noreply.github.com> Date: Tue, 13 Feb 2024 06:36:49 -0700 Subject: [PATCH 7/7] ci: adding pipeline to build and push ubuntu, and macos docker images for zetacored (#1748) * ci: adding pipeline to build and push ubuntu, and macos docker images for zetacored * ci: adding pipeline to build and push ubuntu, and macos docker images for zetacored * adjusted timeouts and did a global vs individual timeout * updated change log location and added arm support --------- Co-authored-by: Lucas Bertrand --- .../build-docker-images-generic/action.yml | 71 +++++++++++ .github/workflows/docker-build-and-push.yml | 120 ++++++++++++++++++ changelog.md | 3 +- 3 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 .github/actions/build-docker-images-generic/action.yml create mode 100644 .github/workflows/docker-build-and-push.yml diff --git a/.github/actions/build-docker-images-generic/action.yml b/.github/actions/build-docker-images-generic/action.yml new file mode 100644 index 0000000000..245ac8a631 --- /dev/null +++ b/.github/actions/build-docker-images-generic/action.yml @@ -0,0 +1,71 @@ +name: 'Build Docker Images' +description: 'Builds Docker images and pushes them to any repository.' +inputs: + DOCKER_FILENAME: + description: 'Name of the docker file to use for the build' + required: true + REPOSITORY_NAME: + description: 'Name of the Repository' + required: true + IMAGE_TAG: + description: 'Image Tag' + required: true + REGISTRY: + description: 'Docker or ORG you want to push to.' + required: true + DOCKER_ORG: + description: 'Docker ORG you want to push to.' + required: false + USERNAME: + description: 'Username for GitHub Container Registry' + required: true + TOKEN: + description: 'Token for GitHub Container Registry' + required: true + DOCKER_FILE_DIRECTORY: + description: 'Directory for your Dockerfile' + required: true + DOCKER_BUILD_KIT: + description: "whether or not to use docker build kit." + required: true + TAG_LATEST: + description: "should the pipeline tag latest" + required: true +runs: + using: "composite" + + steps: + - name: Set Environment Variables" + run: | + echo "DOCKER_BUILDKIT=${{ inputs.DOCKER_BUILD_KIT }}" >> $GITHUB_ENV + shell: bash + + - name: Log in to the Docker Registry + uses: docker/login-action@v2 + with: + registry: ${{ inputs.REGISTRY }} + username: ${{ inputs.USERNAME }} + password: ${{ inputs.TOKEN }} + + - name: Build, tag, and push images + shell: bash + working-directory: ${{ inputs.DOCKER_FILE_DIRECTORY }} + run: | + if [ ! -z "${{ inputs.DOCKER_ORG }}" ]; then + echo "DOCKER ORG SPECIFIED SO USE DOCKER HUB" + docker build -f ${{ inputs.DOCKER_FILENAME }} -t ${{ inputs.DOCKER_ORG }}/${{ inputs.REPOSITORY_NAME }}:${{ inputs.IMAGE_TAG }} . + docker push ${{ inputs.DOCKER_ORG }}/${{ inputs.REPOSITORY_NAME }}:${{ inputs.IMAGE_TAG }} + + if [ "${{ inputs.TAG_LATEST }}" == "true" ]; then + docker tag ${{ inputs.DOCKER_ORG }}/${{ inputs.REPOSITORY_NAME }}:${{ inputs.IMAGE_TAG }} ${{ inputs.DOCKER_ORG }}/${{ inputs.REPOSITORY_NAME }}:latest + docker push ${{ inputs.DOCKER_ORG }}/${{ inputs.REPOSITORY_NAME }}:latest + fi + else + echo "DOCKER REGISTRY SPECIFIED WITH NO DOCKER_ORG USE NON ORG REGISTRIES" + docker build -f ${{ inputs.DOCKER_FILENAME }} -t ${{ inputs.REGISTRY }}/${{ inputs.REPOSITORY_NAME }}:${{ inputs.IMAGE_TAG }} . + docker push ${{ inputs.REGISTRY }}/${{ inputs.REPOSITORY_NAME }}:${{ inputs.IMAGE_TAG }} + if [ "${{ inputs.TAG_LATEST }}" == "true" ]; then + docker tag ${{ inputs.REGISTRY }}/${{ inputs.REPOSITORY_NAME }}:${{ inputs.IMAGE_TAG }} ${{ inputs.REGISTRY }}/${{ inputs.REPOSITORY_NAME }}:latest + docker push ${{ inputs.REGISTRY }}/${{ inputs.REPOSITORY_NAME }}:latest + fi + fi diff --git a/.github/workflows/docker-build-and-push.yml b/.github/workflows/docker-build-and-push.yml new file mode 100644 index 0000000000..6fc54ed7f9 --- /dev/null +++ b/.github/workflows/docker-build-and-push.yml @@ -0,0 +1,120 @@ +name: Zetacored-Docker-Build + +on: + pull_request: + types: + - closed + branches: + - 'main' + workflow_dispatch: + inputs: + version: + description: 'Docker Tag Version For Manual Execution' + required: false + default: '' + +concurrency: + group: Zetacored-Docker-Build + cancel-in-progress: false + +env: + DOCKER_REPO: "zeatcored" + DOCKER_ORG: "zetachain" + DOCKER_REGISTRY: "https://index.docker.io/v1/" + +jobs: + docker_build_ubuntu: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set Version from the PR title. + if: github.event_name == 'pull_request' + run: | + echo "GITHUB_TAG_MAJOR_VERSION=${{ github.event.pull_request.title }}" >> ${GITHUB_ENV} + + - name: Set Version for Hotfix Release from Input. + if: github.event_name != 'pull_request' + run: | + echo "GITHUB_TAG_MAJOR_VERSION=${{ github.event.inputs.version }}" >> ${GITHUB_ENV} + + - name: "BUILD:PUSH:MONITORING:DOCKER:IMAGE" + uses: ./.github/actions/build-docker-images-generic + with: + DOCKER_FILENAME: "Dockerfile" + REPOSITORY_NAME: "${{ env.DOCKER_REPO }}" + IMAGE_TAG: "ubuntu-${{ env.GITHUB_TAG_MAJOR_VERSION }}" + REGISTRY: "${{ env.DOCKER_REGISTRY }}" + DOCKER_ORG: "${{ env.DOCKER_ORG }}" + USERNAME: "${{ secrets.DOCKER_HUB_USERNAME }}" + TOKEN: "${{ secrets.DOCKERHUB_TOKEN }}" + DOCKER_FILE_DIRECTORY: "./" + DOCKER_BUILD_KIT: "0" + TAG_LATEST: "true" + + docker_build_mac: + runs-on: macos-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set Version from the PR title. + if: github.event_name == 'pull_request' + run: | + echo "GITHUB_TAG_MAJOR_VERSION=${{ github.event.pull_request.title }}" >> ${GITHUB_ENV} + + - name: Set Version for Hotfix Release from Input. + if: github.event_name != 'pull_request' + run: | + echo "GITHUB_TAG_MAJOR_VERSION=${{ github.event.inputs.version }}" >> ${GITHUB_ENV} + + - name: "BUILD:PUSH:MONITORING:DOCKER:IMAGE" + uses: ./.github/actions/build-docker-images-generic + with: + DOCKER_FILENAME: "Dockerfile" + REPOSITORY_NAME: "${{ env.DOCKER_REPO }}" + IMAGE_TAG: "mac-${{ env.GITHUB_TAG_MAJOR_VERSION }}" + REGISTRY: "${{ env.DOCKER_REGISTRY }}" + DOCKER_ORG: "${{ env.DOCKER_ORG }}" + USERNAME: "${{ secrets.DOCKER_HUB_USERNAME }}" + TOKEN: "${{ secrets.DOCKERHUB_TOKEN }}" + DOCKER_FILE_DIRECTORY: "./" + DOCKER_BUILD_KIT: "0" + TAG_LATEST: "false" + + docker_build_arm: + runs-on: buildjet-4vcpu-ubuntu-2204-arm + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set Version from the PR title. + if: github.event_name == 'pull_request' + run: | + echo "GITHUB_TAG_MAJOR_VERSION=${{ github.event.pull_request.title }}" >> ${GITHUB_ENV} + + - name: Set Version for Hotfix Release from Input. + if: github.event_name != 'pull_request' + run: | + echo "GITHUB_TAG_MAJOR_VERSION=${{ github.event.inputs.version }}" >> ${GITHUB_ENV} + + - name: "BUILD:PUSH:MONITORING:DOCKER:IMAGE" + uses: ./.github/actions/build-docker-images-generic + with: + DOCKER_FILENAME: "Dockerfile" + REPOSITORY_NAME: "${{ env.DOCKER_REPO }}" + IMAGE_TAG: "arm-${{ env.GITHUB_TAG_MAJOR_VERSION }}" + REGISTRY: "${{ env.DOCKER_REGISTRY }}" + DOCKER_ORG: "${{ env.DOCKER_ORG }}" + USERNAME: "${{ secrets.DOCKER_HUB_USERNAME }}" + TOKEN: "${{ secrets.DOCKERHUB_TOKEN }}" + DOCKER_FILE_DIRECTORY: "./" + DOCKER_BUILD_KIT: "0" + TAG_LATEST: "false" \ No newline at end of file diff --git a/changelog.md b/changelog.md index 2d8f311c7c..1ad986c8b5 100644 --- a/changelog.md +++ b/changelog.md @@ -3,7 +3,6 @@ ## Unreleased * `zetaclientd start` : 2 inputs required from stdin -* Added docker-compose and make commands for launching full nodes. `make mainnet-zetarpc-node` `make mainnet-bitcoind-node` ### Docs @@ -32,6 +31,8 @@ ### CI +* CI: adding pipeline to build and push docker images into dockerhub on release for ubuntu and macos. +* Added docker-compose and make commands for launching full nodes. `make mainnet-zetarpc-node` `make mainnet-bitcoind-node` * [1736](https://github.com/zeta-chain/node/pull/1736) - chore: add Ethermint endpoints to OpenAPI ### Chores