Curve secp256k1

The elliptic curve domain parameters over $$$F_p$$$ associated with a Koblitz curve secp256k1 are specified by the sextuple $$$T = (p,a,b,G,n,h)$$$ where the finite field $$$F_p$$$ is defined by:

p = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F

= $$$2^{256} - 2^{32} - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1$$$

The curve $$$E: y^2 = x^3+ax+b$$$ over $$$F_p$$$ is defined by:

a = 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
b = 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000007

The base point $$$G$$$ in compressed form is:

G = 02 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798

and in uncompressed form is:

G = 04 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798 
       483ADA77 26A3C465 5DA4FBFC 0E1108A8 FD17B448 A6855419 9C47D08F FB10D4B8

Finally the order $$$n$$$ of $$$G$$$ and the cofactor are:

n = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141
h = 01

Public key to BTC address



Private key to Addr

$$$\text{Extneded} = \text{0x80}\, ||\, \overbrace{\text{Private\, Key}}^{256\,bit}\, ||\, \text{01}\\
\text{Checksum} = \text{SHA256(SHA256(Extended)).subBytes(0, 4)}\\
\text{Private\, WIF} = \text{Base58(Extened\, ||\, Checksum)}
$$$

public class BTCAddressGenerator {

    public static void main(String[] args) {
        String keyHex = "F1A679C2DFF382267856E40066F083ED8A78D6F02DDD6CA8C30B27AC691F0109";

        ECNamedCurveParameterSpec ecn = ECNamedCurveTable.getParameterSpec("secp256k1");
        BigInteger k = new BigInteger(1, Bytes.fromHex(keyHex).bytes());
        ECPoint pubK = ecn.getG().multiply(k).normalize();
        System.out.println("X: " + Bytes.from(pubK.getAffineXCoord().getEncoded()).asHex());
        System.out.println("Y: " + Bytes.from(pubK.getAffineYCoord().getEncoded()).asHex());

        Bytes ripemd160sha256 = Bytes.from(pubK.getEncoded(false)).digest(Digests.sha256()).digest(Digests.ripemd160());
        Bytes addr1 = Bytes.fromHex("00").concat(ripemd160sha256);
        Bytes addr1sha256sha256 = addr1.digest(Digests.sha256()).digest(Digests.sha256());
        Bytes finalAddr = addr1.concat(addr1sha256sha256.head(4));
        System.out.println("ADDR: " + finalAddr.asBase58());
        {
            Bytes extended = Bytes.fromHex("80").concat(Bytes.fromHex(keyHex));
            Bytes checksum = extended.digest(Digests.sha256()).digest(Digests.sha256()).head(4);
            System.out.println("WIF: " + extended.concat(checksum).asBase58());
        }
        {
            Bytes extended = Bytes.fromHex("80").concat(Bytes.fromHex(keyHex)).concat(Bytes.fromHex("01"));
            Bytes checksum = extended.digest(Digests.sha256()).digest(Digests.sha256()).head(4);
            System.out.println("WIF (compressed): " + extended.concat(checksum).asBase58());
        }
    }
}

Outputs:

X: 71e035f316b8bd48071060c397c1aa3c916a63f0ad6250f1250e03b7864bd8d4
Y: de40c5becb54be8ea34b0b7579c2b1e1ec9e4d987561cdce871798a9975ccfa4
ADDR: 1JxtdVzeksoDDLBtcRSdsuDGBrKWptdSmc
WIF: 5KeiDN58eVgkn9BoeTemHeHC32hFDf8pmCP2UGkVu5UaPaPabhk
WIF (compressed): L5KSua1EkVcUEF8gtLdgUTYThxUdksKXp5EDVNWzYrkiNCwqu2P8

和比特币类似,一个以太坊账户就是一个公钥哈希后得到的地址,它是由一个私钥推导出对应的公钥,然后再计算出地址。其中,私钥与公钥算法与比特币完全相同,均为 secp256k1 椭圆曲线,但和比特币不同的是,以太坊采用非压缩公钥,然后直接对公钥做 keccak256 哈希,得到32字节的哈希值,取后20字节加上 0x 前缀即为地址:



通过规范 ERC-55 可以对 ETH 地址进行校验。


use keccak_hash::keccak_256;
use secp256k1::{PublicKey, Secp256k1};

fn make_eth_address(public_key: &PublicKey) -> String {
    let public_key_bytes = public_key.serialize_uncompressed().to_vec();

    let mut output = [0_u8; 32];
    keccak_256(&public_key_bytes[1..], &mut output);

    let addr_without_prefix_0x = hex::encode(&output[32 - 20..]);
    let mut addr_keccak_256_output = [0_u8; 32];
    keccak_256(
        addr_without_prefix_0x.as_bytes(),
        &mut addr_keccak_256_output,
    );
    let addr_keccak_256_hex = hex::encode(&addr_keccak_256_output);

    let mut addr = String::with_capacity(42);
    addr.push_str("0x");
    let addr_keccak_256_chars = addr_keccak_256_hex.chars().collect::<Vec<_>>();
    let addr_chars = addr_without_prefix_0x.chars().collect::<Vec<_>>();
    for i in 0..addr_chars.len() {
        if addr_keccak_256_chars[i] >= '8' {
            addr.push(addr_chars[i].to_ascii_uppercase());
        } else {
            addr.push(addr_chars[i])
        }
    }
    addr
}

Full code sample: https://git.hatter.ink/hatter/simple-rust-tests/src/branch/master/__crypto/eth-address/src/main.rs