Skip to content

Commit

Permalink
Add Ethereum doc and ckb-auth-cli supported
Browse files Browse the repository at this point in the history
  • Loading branch information
joii2020 committed Sep 18, 2023
1 parent 3382007 commit e32f68e
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ throughout the process.
The following blockchains are supported:

* [Bitcoin](./docs/bitcoin.md)
* Ethereum
* [Ethereum](./docs/ethereum.md)
* EOS
* Tron
* Dogecoin
Expand Down
67 changes: 67 additions & 0 deletions docs/ethereum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# [Ethereum Lock](../README.md)

Here, we are conducting comparative testing using the official Ethereum tool [go-ethereum](https://geth.ethereum.org/) (Version 1.12.2).

## Install
You can download the binary files following the [official guide](https://geth.ethereum.org/docs/getting-started/installing-geth). Alternatively, you can compile from [source](https://github.com/ethereum/go-ethereum) (requires a golang environment).

Since we are only performing `signature` and `verify`, there is no need to configure geth.
We will be using:
* `geth` for creating accounts and generating addresses.
* `ethkey` for signature and verification.

## Address
First, you need to create a test account (you will need to set a password):
```shell
./geth account new
```

Once created, look for this line in the output:
```
Public address of the key: <BIN>
Path of the secret key file: <FILE_PATH>
```

Here, the account's address and private key file are displayed. If you didn't take note of it at the time, you can use `geth account list` to query it.

In Ethereum, the `Address` is a 20-byte fixed-length array. When used in programs (geth and ckb-auth-cli), it will handle the leading `0x`.

## Signature

Ethereum's message is calculated using sha3: `Ethereum Signed Message:\n` + 'message' hash. While ckb-auth's message is a fixed length of 32 bytes, so here, we use a 32-character string for the message.

You can generate a ckb-auth-compatible message using the following command:
```shell
my_key_file=
message=00112233445566778899001122334455
./ethkey signmessage $my_key_file $message
```
output:
```
Signature: 2d87792d122d9187433bffee9723483cca9c8f848d14a9b772f247ff75637103448d825ff0366a1b6572f48b03ef28705feedeb009e9d95c190922435ae271f401
```

After signing, you can verify it using geth to prevent any basic errors:
```shell
./ethkey verifymessage 0x027a5b3c90216149a42ceaa0431ac7179d0e663b 2d87792d122d9187433bffee9723483cca9c8f848d14a9b772f247ff75637103448d825ff0366a1b6572f48b03ef28705feedeb009e9d95c190922435ae271f401 $message
```

## Verify

As mentioned earlier, ckb-auth uses a 32-byte message, while geth uses text. You can convert the message used by geth into ckb-auth's format using ckb-auth-cli.

Since ckb-auth-cli has already processed the message, you can directly use the 32-character message used when signing with geth, or you can use the 64-character message generated using `ckb-auth-cli ethereum parse`. So, you can directly use geth's signature for verification:

```shell
ckb-auth-cli ethereum parse -m 00112233445566778899001122334455
```

output
```
3030313132323333343435353636373738383939303031313232333334343535
```

Here, we use ckb-auth to verify the signature from geth:
```shell
ckb-auth-cli ethereum verify -a 027a5b3c90216149a42ceaa0431ac7179d0e663b -s 2d87792d122d9187433bffee9723483cca9c8f848d14a9b772f247ff75637103448d825ff0366a1b6572f48b03ef28705feedeb009e9d95c190922435ae271f401 -m 3030313132323333343435353636373738383939303031313232333334343535
```
2 changes: 1 addition & 1 deletion tools/ckb-auth-cli/rust-toolchain
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.71
1.71.1
93 changes: 93 additions & 0 deletions tools/ckb-auth-cli/src/ethereum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use super::{BlockChain, BlockChainArgs};
use anyhow::{anyhow, Error};
use ckb_auth_rs::AlgorithmType;
use clap::{arg, ArgMatches, Command};
use hex::decode;

pub struct EthereumLockArgs {}

impl BlockChainArgs for EthereumLockArgs {
fn block_chain_name(&self) -> &'static str {
"ethereum"
}

fn reg_parse_args(&self, cmd: Command) -> Command {
cmd.arg(arg!(-m --message <PUBKEYHASH> "The signature message"))
}
fn reg_generate_args(&self, cmd: Command) -> Command {
cmd
}
fn reg_verify_args(&self, cmd: Command) -> Command {
cmd.arg(arg!(-a --address <PUBKEYHASH> "The ethereum address"))
.arg(arg!(-s --signature <SIGNATURE> "The signature to verify"))
.arg(arg!(-m --message <MESSAGE> "The signature message"))
}

fn get_block_chain(&self) -> Box<dyn BlockChain> {
Box::new(BitcoinLock {})
}
}

pub struct BitcoinLock {}

impl BlockChain for BitcoinLock {
fn parse(&self, operate_mathches: &ArgMatches) -> Result<(), Error> {
let message = operate_mathches
.get_one::<String>("message")
.expect("Get signature message");
if message.len() != 32 {
return Err(anyhow!("Signature length must be 32"));
}
println!("{}", hex::encode(message.as_bytes()));
Ok(())
}

fn generate(&self, _operate_mathches: &ArgMatches) -> Result<(), Error> {
Err(anyhow!("ethereum does not generate"))
}

fn verify(&self, operate_mathches: &ArgMatches) -> Result<(), Error> {
let mut address = operate_mathches
.get_one::<String>("address")
.expect("Get address from args")
.clone();
if address.starts_with("0x") {
address = address[2..].to_string();
}
let address = decode(&address).expect("decode address");

let signature = decode(
operate_mathches
.get_one::<String>("signature")
.expect("Get signature from args"),
)
.expect("decode ethereum signature");

let message = operate_mathches
.get_one::<String>("message")
.expect("Get message from args");

let message = if message.len() == 32 {
message.as_bytes().to_vec()
} else if message.len() == 64 {
decode(message).expect("Decode ethereum message")
} else {
return Err(anyhow!("ethereum message size is not 32"));
};

if address.len() != 20 {
return Err(anyhow!("ethereum address invalidate"));
}
if signature.len() != 65 {
return Err(anyhow!("ethereum signature size is not 65"));
}
if message.len() != 32 {
return Err(anyhow!("ethereum message size is not 32"));
}

super::auth_script::run_auth_exec(AlgorithmType::Ethereum, &address, &message, &signature)?;

println!("Ethereum Signature verification succeeded!");
Ok(())
}
}
3 changes: 3 additions & 0 deletions tools/ckb-auth-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod auth_script;
mod bitcoin;
mod cardano;
mod ethereum;
mod litecoin;
mod monero;
mod ripple;
Expand All @@ -10,6 +11,7 @@ mod utils;
use crate::monero::MoneroLockArgs;
use bitcoin::BitcoinLockArgs;
use cardano::CardanoLockArgs;
use ethereum::EthereumLockArgs;
use litecoin::LitecoinLockArgs;
use ripple::RippleLockArgs;
use solana::SolanaLockArgs;
Expand Down Expand Up @@ -83,6 +85,7 @@ fn main() -> Result<(), Error> {
Box::new(SolanaLockArgs {}) as Box<dyn BlockChainArgs>,
Box::new(RippleLockArgs {}) as Box<dyn BlockChainArgs>,
Box::new(BitcoinLockArgs {}) as Box<dyn BlockChainArgs>,
Box::new(EthereumLockArgs {}) as Box<dyn BlockChainArgs>,
];

let matches = cli(block_chain_args.as_slice()).get_matches();
Expand Down

0 comments on commit e32f68e

Please sign in to comment.