比特币账户是如何生成的呢?这里详细介绍了比特币从私钥,公钥,到地址的详细步骤,以及公钥到私钥的算法逻辑, 想不想亲自生成一个比特币账号?跟着下面一步步做,你也可以哦。
但需要注意的是,比特币使用的 是 椭圆曲线secp256k1
的(ECDSA算法)用来生成密钥对,以及交易签名和交易验证,但是我这里使用了椭圆曲线ed25519
来生成密钥对,是另一种签名算法,但是 ed25519相对于ECDSA有多个优点,它的运算速度通常比ECDSA快,因为本人喜欢比较吊的,所以替换为了ed25519
椭圆曲线用来生成密钥对。
如果想真正生成可以使用的比特币账户,还是需要使用secp256k1
算法生成密钥对,对于下面的代码只需更改密钥对生成的算法库即可。
添加依赖
以下代码用到了这些库
ini
ed25519-dalek = { version = "2.1.1", features = ["rand_core"] }
rand = "0.8.5"
bs58 = "0.5.0"
ripemd = "0.1.3"
sha256 = { version = "1.5.0" }
生成密钥对
首先,我们需要生成一对密钥:一个公钥和一个私钥。在比特币系统中,私钥用于签署交易,证明资金的所有权,而公钥用于接收比特币。这里使用了ed25519
来生成密钥对。
rust
pub struct Keypair(ed25519_dalek::SigningKey);
impl Keypair {
pub fn new() -> Self {
Self(ed25519_dalek::SigningKey::generate(&mut OsRng))
}
}
在上面的代码中,Keypair::new()
函数使用 ed25519_dalek::SigningKey
来生成一个新的密钥对。OsRng
是一个操作系统随机数生成器,用于确保生成的密钥具有高度的随机性和安全性。
公钥和私钥
一旦我们有了密钥对,就可以轻松获取公钥和私钥:
rust
pub fn public_key(&self) -> PublicKey {
self.0.verifying_key().to_bytes()
}
pub fn private_key(&self) -> PrivateKey {
self.0.to_bytes()
}
这里的公钥和私钥都以字节数组的形式返回,这是加密操作中常见的数据格式。
生成地址
比特币地址是公钥的哈希版本,通过一系列的加密操作生成。这个地址可以公开分享,让其他用户向你发送比特币。
rust
pub fn address(&self) -> String {
let mut hash_160 = Self::pub_key_hash(self).to_vec();
hash_160.insert(0, VERSION);
let rt = [hash_160.clone(), checksum(hash_160.as_slice())].concat();
base58_encode(rt.as_slice())
}
生成地址的步骤包括:
步骤1:计算公钥的SHA-256哈希,然后使用RIPEMD-160哈希
在比特币地址生成过程中,首先需要对公钥进行SHA-256哈希运算,然后对结果应用RIPEMD-160哈希运算。这样做可以缩短哈希长度,同时保持足够的安全性。
在您的代码中,这一步是通过pub_key_hash
函数实现的:
rust
pub fn pub_key_hash(&self) -> Ripemd160Hash {
let hash = self.public_key().digest();
ripemd160_digest(hash.as_bytes())
}
这个函数首先使用digest()
方法对公钥进行SHA-256哈希运算,然后调用ripemd160_digest
函数对结果进行RIPEMD-160哈希运算。
rust
pub fn ripemd160_digest(data: &[u8]) -> Ripemd160Hash {
let mut ripemd160 = Ripemd160::new();
ripemd160.update(data);
let ret = ripemd160.finalize();
ret[..].try_into().unwrap()
}
步骤2:添加版本字节
比特币地址中的版本字节(通常为0x00)用于标识地址类型。在您的代码中,这一步是在address
函数中完成的:
rust
let mut hash_160 = Self::pub_key_hash(self).to_vec();
hash_160.insert(0, VERSION);
这里,VERSION
是一个常量,代表比特币主网络的地址类型,其值通常设为0x00
。通过在哈希值前添加这个版本字节,我们能够区分不同类型的比特币地址。
步骤3:计算校验和
计算校验和是为了防止地址在传输过程中的误输入。通过对地址哈希加上版本字节的结果再进行两次SHA-256哈希运算,并取前四个字节作为校验和。
在您的代码中,校验和的计算是这样实现的:
rust
fn checksum(payload: &[u8]) -> Vec<u8> {
let first_sha = payload.digest();
let second_sha = first_sha.digest();
second_sha[0..ADDRESS_CHECK_SUM_LEN].to_vec()
}
步骤4:拼接并进行Base58编码
最后一步是将版本字节、地址哈希和校验和拼接在一起,然后使用Base58编码转换为一串字符,形成最终的比特币地址。
这个步骤发生在address
函数的最后:
rust
let rt = [hash_160.clone(), checksum(hash_160.as_slice())].concat();
base58_encode(rt.as_slice())
这里,base58_encode
函数将拼接后的结果转换为Base58编码的字符串,这是比特币地址的标准格式,易于人类阅读且避免了容易混淆的字符。
完整账户信息
我们定义了一个Account
结构体来存储完整的账户信息,包括私钥、公钥和地址。
rust
pub struct Account {
private_key: PrivateKey,
pubkey: PublicKey,
address: Address,
}
通过地址验证公钥哈希
通常来说我们在验证交易的时候,只知道一个地址,比特币因为其签名算法,可以从地址获得他的公钥哈希,所以竟是通过这个原理来验证签名交易,但是这里暂时不做对交易签名和验证的讲解。首先从address
使用base58_decode
获得原始地址,再通过去除开头的版本信息和结尾的校验和,取中间的呢公钥哈希进行对比
rust
pub fn address_verify(addr: &Address, pubkeyhash: Ripemd160Hash) -> bool {
let payload = base58_decode(addr);
let pub_key_hash: Ripemd160Hash = payload[1..payload.len() - ADDRESS_CHECK_SUM_LEN]
.try_into()
.unwrap();
pubkeyhash.eq(&pub_key_hash)
}
创建新账户
通过调用Account::new()
,我们可以轻松创建一个新的比特币账户。这个函数内部调用Keypair::new()
来生成密钥对,然后使用这些密钥来创建地址。
rust
let account = Account::new();
let private_key = account.private_key();
let public_key = account.public_key();
测试和验证
最后,我们通过实现一个简单的测试来验证账户生成的正确性。这个测试检查地址验证函数address_verify
,确保生成的地址与其公钥哈希匹配。
rust
#[test]
fn test() {
let account = Account::new();
let private_key = account.private_key();
let public_key = account.public_key();
let pub_key_hash = account.pub_key_hash();
let address = account.address();
let ret = address_verify(&address, pub_key_hash);
dbg!(ret); // 应为true
}
注意
通过以上步骤,就可以生成一个比特币账户,该账户可以用来接收比特币,并且会产生与比特币网络一致的私钥长度,和公钥哈希,但是因为算法不同,无法用来验证和交易。所以千万不要拿上面的代码直接写钱包应用哈,要不钱找不回来了,除非你自己改成secp256k1
,也不复杂,直接换底层密码库就行了,其他没什么变化。 至于原因:是因为 比特币协比特币网络及其节点软件是基于secp256k1
椭圆曲线加密算法设计的。这意味着网络上的交易验证、签名过程、以及公私钥的处理都是依据secp256k1
算法的规则来实现的。如果你使用ed25519
算法生成的密钥对,这些密钥对将无法在比特币网络中正常工作,因为:
- 签名不兼容:比特币网络的节点无法识别或验证使用ed25519算法生成的签名。交易在传播到网络时会因为签名验证失败而被拒绝。
- 地址格式问题:尽管可以从ed25519公钥生成符合比特币地址格式的地址(通过相应的哈希函数和编码过程),但这个地址背后的密钥和签名机制与网络预期的不同,会导致实际的交易验证失败。
上面代码是用rust实现比特币网络项目的账户部分,完整可以参考源码(虽然还没写完,还改了椭圆曲线为ed25519
):github.com/ChainThemAl...
所以上面代码只作为学习用。如果想实际使用需要替换生成 secp256k1 密钥对的密钥算法。from Pomelo_刘金,转载请注明原文链接。感谢!