继续源码的阅读,本文将对比特币源码中的公钥相关部分进行梳理。
在阅读代码前,先明确一个概念:公钥是如何定义和产生的?
我们已经知道,比特币的私钥就是一个256位二进制数字。
通过椭圆曲线乘法可以很容易的从私钥计算得到公钥,这是不可逆转的过程:
K = k * G
其中k是私钥,G是被称为生成点的常数点(xG,yG),而K是所得公钥。
比特币系统所使用的椭圆曲线叫做secp256k1
,具体参数如下:
通过椭圆曲线乘法得到的公钥K是二维离散域内的一个点(x,y)
因此公钥K包含两个256bit的二进制数,分别对应其x和y坐标。
需要注意的是,由同一个私钥生成的公钥是唯一的,但是同一个公钥可以有两种不同的格式:
压缩格式
和非压缩格式
(这是由于y可以由x推导出,于是公钥同时存储x和y就浪费了一半的空间,因此有了压缩格式的公钥)
而两种格式后续将会得到两个不同的比特币地址,但任何一个都是合法的。
为了标识公钥使用的哪种格式,需要前缀进行标识。
04
用于标识非压缩格式
02
或03
用于标识压缩格式,y为奇数用03
,y为偶数用02
以这样一个公钥为例,其坐标x和y如下:
x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB
如果用非压缩格式表达,是一个520比特的数字(8+256+256)。这个520比特的数字以前缀04开头,紧接着是x及y坐标,组成格式为04 x y:
K = 04F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE 52DDFE2E505BDB
如果用压缩格式表达,是一个264比特数字(8+256),其中前缀03表示y坐标是一个奇数:
K = 03F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
公钥的相关源码位于pubkey.h
和pubkey.cpp
中
pubkey.h
的源码如下:
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2018 The Bitcoin Core developers
// Copyright (c) 2017 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_PUBKEY_H
#define BITCOIN_PUBKEY_H
#include <hash.h>
#include <serialize.h>
#include <uint256.h>
#include <stdexcept>
#include <vector>
const unsigned int BIP32_EXTKEY_SIZE = 74; // 按照上一篇的分析,这应该是HD wallet使用的公钥的大小
/** * A reference to a CKey: the Hash160 of its serialized public key * CKeyID用来作为一个CPubKey公钥实例的ID,用公钥的Hash160(SHA-256 + RIPEMD-160)作为唯一引用ID * 因此CKeyID继承自定义类型uint160,本质就是一个160bit的hash二进制数组 * 通过实现来看,CKeyID = RIPEMD-160(SHA-256(publicKey)) * 因此CKeyID其实就是未经 Base58 Check 编码的比特币地址 */
class CKeyID : public uint160
{
public:
CKeyID() : uint160() {}
explicit CKeyID(const uint160& in) : uint160(in) {}
};
typedef uint256 ChainCode; // CExtPubKey中的成员变量
/** 封装的公钥. */
class CPubKey
{
public:
/** * secp256k1: */
static constexpr unsigned int PUBLIC_KEY_SIZE = 65; // 非压缩格式公钥的大小(65byte = 520bit)
static constexpr unsigned int COMPRESSED_PUBLIC_KEY_SIZE = 33; // 压缩格式公钥的大小(33byte = 264bit)
static constexpr unsigned int SIGNATURE_SIZE = 72;
static constexpr unsigned int COMPACT_SIGNATURE_SIZE = 65;
/** * see www.keylength.com * script supports up to 75 for single byte push */
static_assert(
PUBLIC_KEY_SIZE >= COMPRESSED_PUBLIC_KEY_SIZE,
"COMPRESSED_PUBLIC_KEY_SIZE is larger than PUBLIC_KEY_SIZE");
private:
/** * Just store the serialized data. 公钥数据 * Its length can very cheaply be computed from the first byte. * 由于第一个字节代表前缀,公钥的长度可以通过前缀轻松的得到 */
unsigned char vch[PUBLIC_KEY_SIZE];
//! Compute the length of a pubkey with a given first byte.
// 通过一个字节(前缀)得到公钥的长度
// 函数私有,程序应时刻维护前缀与实际数据相对应。
unsigned int static GetLen(unsigned char chHeader)
{
if (chHeader == 2 || chHeader == 3) // 2或3代表压缩格式公钥,2代表y为偶数,3代表y为奇数
return COMPRESSED_PUBLIC_KEY_SIZE;
if (chHeader == 4 || chHeader == 6 || chHeader == 7) // 4代表非压缩公钥,6和7???
return PUBLIC_KEY_SIZE;
return 0;
}
//! Set this key data to be invalid 使用0xFF前缀标识公钥无效
void Invalidate()
{
vch[0] = 0xFF;
}
public:
// 验证一个vector<unsigned char>数组的大小是否满足公钥的要求
bool static ValidSize(const std::vector<unsigned char> &vch) {
return vch.size() > 0 && GetLen(vch[0]) == vch.size();
}
//! Construct an invalid public key.
// 初始化一个空的公钥,状态为无效,需要使用Set函数赋值vch
CPubKey()
{
Invalidate();
}
//! Initialize a public key using begin/end iterators to byte data.
// 初始化公钥数据vsh的函数,通过迭代器 begin/end 的方式
template <typename T>
void Set(const T pbegin, const T pend)
{
int len = pend == pbegin ? 0 : GetLen(pbegin[0]);
if (len && len == (pend - pbegin))
memcpy(vch, (unsigned char*)&pbegin[0], len);
else
Invalidate(); // 公钥无效(提供数据长度为0 或 数据长度与前缀不一致)
}
//! Construct a public key using begin/end iterators to byte data.
template <typename T>
CPubKey(const T pbegin, const T pend)
{
Set(pbegin, pend);
}
//! Construct a public key from a byte vector.
// 直接用vector<unsigned char>构造
explicit CPubKey(const std::vector<unsigned char>& _vch)
{
Set(_vch.begin(), _vch.end());
}
//! Simple read-only vector-like interface to the pubkey data.
// 获取公钥数据的一些只读的接口
unsigned int size() const { return GetLen(vch[0]); }
const unsigned char* data() const { return vch; }
const unsigned char* begin() const { return vch; }
const unsigned char* end() const { return vch + size(); }
const unsigned char& operator[](unsigned int pos) const { return vch[pos]; }
//! Comparator implementation. 比较两个CPubKey
// vch是私有成员,因此定义为友元
friend bool operator==(const CPubKey& a, const CPubKey& b)
{
return a.vch[0] == b.vch[0] &&
memcmp(a.vch, b.vch, a.size()) == 0;
}
friend bool operator!=(const CPubKey& a, const CPubKey& b)
{
return !(a == b);
}
// 比较两个公钥可以理解,为什么要比较大小???
friend bool operator<(const CPubKey& a, const CPubKey& b)
{
return a.vch[0] < b.vch[0] ||
(a.vch[0] == b.vch[0] && memcmp(a.vch, b.vch, a.size()) < 0);
}
//! Implement serialization, as if this was a byte vector.
template <typename Stream>
void Serialize(Stream& s) const
{
unsigned int len = size();
::WriteCompactSize(s, len); // 函数定义于serialize.h
s.write((char*)vch, len);
}
template <typename Stream>
void Unserialize(Stream& s)
{
unsigned int len = ::ReadCompactSize(s); // 函数定义于serialize.h
if (len <= PUBLIC_KEY_SIZE) {
s.read((char*)vch, len); // 读取Stream中的数据
} else {
// invalid pubkey, skip available data(将Stream中的数据丢弃)
char dummy;
while (len--)
s.read(&dummy, 1);
Invalidate();
}
}
//! Get the KeyID of this public key (hash of its serialization)
// 获得公钥数据的Hash160(SHA-256 + RIPEMD-160)作为ID
// 由此看来,CKeyID其实就是公钥对应的未经Base58校验编码的比特币地址
CKeyID GetID() const
{
return CKeyID(Hash160(vch, vch + size())); // Hash160函数定义于hash.h中
}
//! Get the 256-bit hash of this public key.
uint256 GetHash() const
{
return Hash(vch, vch + size());
}
/* * Check syntactic correctness. 检查公钥是否有效 * IsValid()仅仅按照协议查看前缀,只要不是0xFF,说明有效 * Note that this is consensus critical as CheckSig() calls it! */
bool IsValid() const
{
return size() > 0;
}
//! fully validate whether this is a valid public key (more expensive than IsValid())
// 完全验证这是否是有效的公钥
// IsFullyValid()比IsValid()的验证更彻底,进行数学上的验证。
bool IsFullyValid() const;
//! Check whether this is a compressed public key.
// 本质是检查第一个字节的标识符
bool IsCompressed() const
{
return size() == COMPRESSED_PUBLIC_KEY_SIZE;
}
/** * Verify a DER signature (~72 bytes). * If this public key is not fully valid, the return value will be false. */
bool Verify(const uint256& hash, const std::vector<unsigned char>& vchSig) const;
/** * Check whether a signature is normalized (lower-S). */
static bool CheckLowS(const std::vector<unsigned char>& vchSig);
//! Recover a public key from a compact signature.
bool RecoverCompact(const uint256& hash, const std::vector<unsigned char>& vchSig);
//! Turn this public key into an uncompressed public key.
bool Decompress();
//! Derive BIP32 child pubkey.
bool Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;
};
struct CExtPubKey {
unsigned char nDepth;
unsigned char vchFingerprint[4];
unsigned int nChild;
ChainCode chaincode;
CPubKey pubkey;
friend bool operator==(const CExtPubKey &a, const CExtPubKey &b)
{
return a.nDepth == b.nDepth &&
memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], sizeof(vchFingerprint)) == 0 &&
a.nChild == b.nChild &&
a.chaincode == b.chaincode &&
a.pubkey == b.pubkey;
}
void Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const;
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]);
bool Derive(CExtPubKey& out, unsigned int nChild) const;
void Serialize(CSizeComputer& s) const
{
// Optimized implementation for ::GetSerializeSize that avoids copying.
s.seek(BIP32_EXTKEY_SIZE + 1); // add one byte for the size (compact int)
}
template <typename Stream>
void Serialize(Stream& s) const
{
unsigned int len = BIP32_EXTKEY_SIZE;
::WriteCompactSize(s, len);
unsigned char code[BIP32_EXTKEY_SIZE];
Encode(code);
s.write((const char *)&code[0], len);
}
template <typename Stream>
void Unserialize(Stream& s)
{
unsigned int len = ::ReadCompactSize(s);
unsigned char code[BIP32_EXTKEY_SIZE];
if (len != BIP32_EXTKEY_SIZE)
throw std::runtime_error("Invalid extended key size\n");
s.read((char *)&code[0], len);
Decode(code);
}
};
/** Users of this module must hold an ECCVerifyHandle. The constructor and * destructor of these are not allowed to run in parallel, though. */
class ECCVerifyHandle
{
static int refcount;
public:
ECCVerifyHandle();
~ECCVerifyHandle();
};
#endif // BITCOIN_PUBKEY_H