-
Notifications
You must be signed in to change notification settings - Fork 405
Feat/support cosmos evm #1814
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Feat/support cosmos evm #1814
Changes from all commits
f80d43d
36d2e10
f811448
c640c39
a489a7b
ce7e8b9
2b9d6ac
b303742
d15842c
8ff0e05
eaa0ebf
54b103c
bbffd36
5b4a48a
fada481
03fa926
17ad7ba
5fee884
2949808
e77ae7a
5636a48
4a5fd80
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,7 +4,9 @@ import { arrayContentStartsWith } from "@cosmjs/utils"; | |
|
|
||
| import { | ||
| Ed25519Pubkey, | ||
| EthSecp256k1Pubkey, | ||
| isEd25519Pubkey, | ||
| isEthSecp256k1Pubkey, | ||
| isMultisigThresholdPubkey, | ||
| isSecp256k1Pubkey, | ||
| MultisigThresholdPubkey, | ||
|
|
@@ -41,10 +43,25 @@ export function encodeEd25519Pubkey(pubkey: Uint8Array): Ed25519Pubkey { | |
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Takes a EthSecp256k1 public key as raw bytes and returns the Amino JSON | ||
| * representation of it (the type/value wrapper object). | ||
| */ | ||
| export function encodeEthSecp256k1Pubkey(pubkey: Uint8Array): EthSecp256k1Pubkey { | ||
| if (pubkey.length !== 33 || (pubkey[0] !== 0x02 && pubkey[0] !== 0x03)) { | ||
| throw new Error("Public key must be compressed secp256k1, i.e. 33 bytes starting with 0x02 or 0x03"); | ||
| } | ||
| return { | ||
| type: pubkeyType.ethsecp256k1, | ||
| value: toBase64(pubkey), | ||
| }; | ||
| } | ||
|
|
||
| // As discussed in https://github.com/binance-chain/javascript-sdk/issues/163 | ||
| // Prefixes listed here: https://github.com/tendermint/tendermint/blob/d419fffe18531317c28c29a292ad7d253f6cafdf/docs/spec/blockchain/encoding.md#public-key-cryptography | ||
| // Last bytes is varint-encoded length prefix | ||
| const pubkeyAminoPrefixSecp256k1 = fromHex("eb5ae987" + "21" /* fixed length */); | ||
| const pubkeyAminoPrefixEthSecp256k1 = fromHex("5D7423DF" + "21" /* fixed length */); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh sweet, we now got a new Amino prefix for that? Could you add a reference to any documentation or source code where this is coming from? This was a major blocker in earlier attempts to add EVM support to CosmJS There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prove
|
||
| const pubkeyAminoPrefixEd25519 = fromHex("1624de64" + "20" /* fixed length */); | ||
| const pubkeyAminoPrefixSr25519 = fromHex("0dfb1005" + "20" /* fixed length */); | ||
| /** See https://github.com/tendermint/tendermint/commit/38b401657e4ad7a7eeb3c30a3cbf512037df3740 */ | ||
|
|
@@ -207,6 +224,8 @@ export function encodeAminoPubkey(pubkey: Pubkey): Uint8Array { | |
| return new Uint8Array([...pubkeyAminoPrefixEd25519, ...fromBase64(pubkey.value)]); | ||
| } else if (isSecp256k1Pubkey(pubkey)) { | ||
| return new Uint8Array([...pubkeyAminoPrefixSecp256k1, ...fromBase64(pubkey.value)]); | ||
| } else if (isEthSecp256k1Pubkey(pubkey)) { | ||
| return new Uint8Array([...pubkeyAminoPrefixEthSecp256k1, ...fromBase64(pubkey.value)]); | ||
| } else { | ||
| throw new Error("Unsupported pubkey type"); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,10 +24,21 @@ export function isSecp256k1Pubkey(pubkey: Pubkey): pubkey is Secp256k1Pubkey { | |
| return (pubkey as Secp256k1Pubkey).type === "tendermint/PubKeySecp256k1"; | ||
| } | ||
|
|
||
| export interface EthSecp256k1Pubkey extends SinglePubkey { | ||
| readonly type: "os/PubKeyEthSecp256k1"; | ||
| readonly value: string; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: is this a compressed or uncompressed encoding of the pubkey? In earlier versions of Ethermint this was uncompressed (but barely documented). Now looking at the code above it seems to be compressed. |
||
| } | ||
|
|
||
| export function isEthSecp256k1Pubkey(pubkey: Pubkey): pubkey is EthSecp256k1Pubkey { | ||
| return (pubkey as EthSecp256k1Pubkey).type === "os/PubKeyEthSecp256k1"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a typo? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. What does "os/" mean in this context? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe a legacy from evmOS? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems so. That is the earliest example of it I can see publicly within github. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should just use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we also remove type casting in this line? https://github.com/cosmos/cosmjs/blob/main/packages/amino/src/pubkeys.ts#L24 return (pubkey as Secp256k1Pubkey).type === "tendermint/PubKeySecp256k1";There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right! #1847 |
||
| } | ||
|
|
||
| export const pubkeyType = { | ||
| /** @see https://github.com/tendermint/tendermint/blob/v0.33.0/crypto/ed25519/ed25519.go#L22 */ | ||
| secp256k1: "tendermint/PubKeySecp256k1" as const, | ||
| /** @see https://github.com/tendermint/tendermint/blob/v0.33.0/crypto/secp256k1/secp256k1.go#L23 */ | ||
| secp256k1: "tendermint/PubKeySecp256k1" as const, | ||
| /** @see https://github.com/cosmos/evm/blob/v1.0.0-rc2/crypto/ethsecp256k1/ethsecp256k1.go#L35-L36 */ | ||
| ethsecp256k1: "os/PubKeyEthSecp256k1" as const, | ||
| /** @see https://github.com/tendermint/tendermint/blob/v0.33.0/crypto/ed25519/ed25519.go#L22 */ | ||
| ed25519: "tendermint/PubKeyEd25519" as const, | ||
| /** @see https://github.com/tendermint/tendermint/blob/v0.33.0/crypto/sr25519/codec.go#L12 */ | ||
| sr25519: "tendermint/PubKeySr25519" as const, | ||
|
|
@@ -53,7 +64,12 @@ export interface SinglePubkey extends Pubkey { | |
| } | ||
|
|
||
| export function isSinglePubkey(pubkey: Pubkey): pubkey is SinglePubkey { | ||
| const singPubkeyTypes: string[] = [pubkeyType.ed25519, pubkeyType.secp256k1, pubkeyType.sr25519]; | ||
| const singPubkeyTypes: string[] = [ | ||
| pubkeyType.ed25519, | ||
| pubkeyType.secp256k1, | ||
| pubkeyType.ethsecp256k1, | ||
| pubkeyType.sr25519, | ||
| ]; | ||
| return singPubkeyTypes.includes(pubkey.type); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| /** | ||
| * Utility functions for working with signer accounts and algorithm detection. | ||
| */ | ||
|
|
||
| import { encodeEthSecp256k1Pubkey, encodeSecp256k1Pubkey } from "./encoding"; | ||
| import { EthSecp256k1Pubkey, Secp256k1Pubkey } from "./pubkeys"; | ||
| import type { AccountData } from "./signer"; | ||
|
|
||
| /** | ||
| * Checks if an account uses Ethereum secp256k1 keys by examining the algorithm name. | ||
| * | ||
| * Handle Ethereum secp256k1 keys with dual naming convention support: | ||
| * Different wallets and chains report Ethereum key algorithms inconsistently: | ||
| * - "eth_secp256k1" (with underscore) - de facto standard used by Keplr wallet, CosmJS, some Cosmos SDK chains | ||
| * - "ethsecp256k1" (without underscore) - used by Evmos, Cronos, and other EVM-compatible chains | ||
| * Both represent the same Ethereum-compatible secp256k1 keys that require keccak256 address derivation | ||
| * | ||
| * @param account The account data from a signer | ||
| * @returns true if the account uses Ethereum secp256k1 keys, false otherwise | ||
| */ | ||
| export function isEthereumSecp256k1Account(account: AccountData): boolean { | ||
| return account.algo === "eth_secp256k1" || account.algo === "ethsecp256k1"; | ||
| } | ||
|
|
||
| /** | ||
| * Gets the correctly encoded amino pubkey for an account based on its algorithm. | ||
| * | ||
| * This utility automatically selects the appropriate encoding function based on whether | ||
| * the account uses Ethereum secp256k1 keys or standard secp256k1 keys. | ||
| * | ||
| * @param account The account data from a signer | ||
| * @returns The amino-encoded pubkey (EthSecp256k1Pubkey or Secp256k1Pubkey) | ||
| */ | ||
| export function getAminoPubkey(account: AccountData): EthSecp256k1Pubkey | Secp256k1Pubkey { | ||
| if (isEthereumSecp256k1Account(account)) { | ||
| return encodeEthSecp256k1Pubkey(account.pubkey); | ||
| } else { | ||
| return encodeSecp256k1Pubkey(account.pubkey); | ||
| } | ||
| } |


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An "raw Eth Secp256k1 Pubkey" is uncompressed by convention. All Ethereum ecosystem uses uncompressed pubkeys. So either the implementation is wrong but maybe it is a question of findung a better name for the functionI am wrong I guess and compressed pubkeys are used consostently in Cosmos, also evm code. Any thoughts on that matter?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found the codes in cosmos/evm
ethsecp256k1.go, the PubKey() method returns a 33-byte compressed pubkey.But when it derives an address, the Address() method first decompresses the pubkey, then uses the uncompressed pubkey to derive an address.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, that helps. Sounds like a reasonable approach for the Cosmos Stack