Bitcoin source reading - Wallet address - bitcoin address generation process

1, Bitcoin address generation flow chart


A bitcoin wallet contains a series of key pairs, each of which includes a private key and a public key. The private key (k) is a number, usually with
Machine selected. With the private key, we can use the one-way encryption function of elliptic curve multiplication to generate A public key (K). With the public key (K), we can use A one-way encryption hash function to generate the bitcoin address (A).

2, Specific steps

1 generate private key
	More precisely, the private key can be any number between 1 and n-1, where n is a constant (n=1.158 * 10^77, slightly less than 2 ^ 256),
It is defined as the order of the elliptic curve used by bitcoin (see the explanation of elliptic curve cryptography).
1> The core of bitcoin firstly generates a random number m through OpenSSL's RNG random number generator and operating system random number generator.   		
2> The value k of 256 bits is obtained by SHA256 hash operation on m
 3> Verify whether K conforms to secp256k1's private key standard. If it conforms to the standard, it can continue as a private key. If it does not conform to the standard, 1 > will be returned.

2 generate the corresponding public key according to the private key

Starting from the private key k generated above, we multiply it by the predetermined generation point G on the curve to obtain another point on the curve, that is, the corresponding
 The public key K. The generation point is a part of secp256k1 standard. The generation points of bitcoin keys are the same: {K = k * G}
Where k is the private key, G is the generating point, and the point K obtained on the curve is the public key. Because all bitcoin users have the same generation point, one
 Multiply the private key K by G to get the same public key K. The relationship between K and K is fixed, but only one-way operation, that is, to get k from K. That's what you can do
 The reason why a bitcoin address (derived from K) is shared with anyone without revealing the private key (k).

3. Process the public key to get the bitcoin address

1> The hash of public key K is obtained by Hash160 operation.
2> Do the following for the hash of K
	1) Add the version number to the header.
	2) Two more SHA256 operations are performed on the value containing version number, and the first four bytes are added to the end of the value as the check sum.
3> Base 58 code (version number + K hash + checksum) to get the final bitcoin address.

3, Corresponding code process

1 generate private key

getnewaddress() -> CWallet::GetKeyFromPool() -> CWallet::GenerateNewKey() -> CKey::MakeNewKey() 
->Getstrongrandbytes() -- this function uses OpenSSL's RNG random number generator and operating system random number generator to get a
 Random number (64 bytes)
1> Generate the private key and check whether it meets the standard
void CKey::MakeNewKey(bool fCompressedIn) {
    do {
        GetStrongRandBytes(keydata.data(), keydata.size()); // Get random number
    } while (!Check(keydata.data())); // Check whether the private key meets the standard through secp256k1
    fValid = true;
    fCompressed = fCompressedIn;
}
2> Generate random number
void GetStrongRandBytes(unsigned char* out, int num)
{
    assert(num <= 32);
    CSHA512 hasher;
    unsigned char buf[64];

    // First source: OpenSSL's RNG
    RandAddSeedPerfmon();
    GetRandBytes(buf, 32);
    hasher.Write(buf, 32);

    // Second source: OS RNG
    GetOSRand(buf);
    hasher.Write(buf, 32);
    ...
}

2 generate public key based on private key

CPubKey CWallet::GenerateNewKey(CWalletDB &walletdb, bool internal)
{
    AssertLockHeld(cs_wallet); // mapKeyMetadata
    bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets

    CKey secret;

    // Create new metadata
    int64_t nCreationTime = GetTime();
    CKeyMetadata metadata(nCreationTime);

    // use HD key derivation if HD was enabled during wallet creation
    if (IsHDEnabled()) {
        DeriveNewChildKey(walletdb, metadata, secret, (CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false));
    } else {
        secret.MakeNewKey(fCompressed);
    }

    // Compressed public keys were introduced in version 0.6.0
    if (fCompressed) {
        SetMinVersion(FEATURE_COMPRPUBKEY);
    }

	// Generate public key
    CPubKey pubkey = secret.GetPubKey();
    assert(secret.VerifyPubKey(pubkey));

    mapKeyMetadata[pubkey.GetID()] = metadata;
    UpdateTimeFirstKey(nCreationTime);

    if (!AddKeyPubKeyWithDB(walletdb, secret, pubkey)) {
        throw std::runtime_error(std::string(__func__) + ": AddKey failed");
    }
    return pubkey;
}
In fact, the process of public key generation is based on secp256k1 library
CPubKey CKey::GetPubKey() const {
    assert(fValid);
    secp256k1_pubkey pubkey;
    size_t clen = 65;
    CPubKey result;
    int ret = secp256k1_ec_pubkey_create(secp256k1_context_sign, &pubkey, begin());
    assert(ret);
    secp256k1_ec_pubkey_serialize(secp256k1_context_sign, (unsigned char*)result.begin(), &clen, &pubkey, fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);
	...
    return result;
}

3. Handle the public key accordingly

UniValue getnewaddress(const JSONRPCRequest& request)
{
    CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
    if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
        return NullUniValue;
    }
    
    ...
    
    // Generate a new key that is added to wallet
    CPubKey newKey;
    if (!pwallet->GetKeyFromPool(newKey)) {
        throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
    }
    // Perform the Hash160 operation on the public key K in the CPubKey::GetID function to get the keyID
    CKeyID keyID = newKey.GetID();
    pwallet->SetAddressBook(keyID, strAccount, "receive");
    return EncodeDestination(keyID);
}
1> Getid function is to Hash160 the public key and return the operation result
CKeyID GetID() const {
        return CKeyID(Hash160(vch, vch + size()));
}
2> Add a version number to the header
std::string DestinationEncoder::operator()(const CKeyID& id) const {
        std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
        data.insert(data.end(), id.begin(), id.end()); // Add version number to header
        return EncodeBase58Check(data);
}
3> Two SHA256 operations are performed on the value with good version, and the first four bytes of the result are added to the end of the value, and then Base58 encoding is performed
std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn)
{
    // add 4-byte hash check to the end
    std::vector<unsigned char> vch(vchIn);
    uint256 hash = Hash(vch.begin(), vch.end());
    vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4);
    return EncodeBase58(vch);
}

Base58 is used for encoding to make the address readable. Base58 actually means that Base64 removes some characters that are not easy to recognize. 0 and O in uppercase are easy to be confused, 1 and l are easy to be confused, etc.

Reference books: < mastering bitcoin > >

*Note: This article is only for personal notes. If there is something wrong, please correct it.

Welcome to the public attention [lazy idling] public number, looking forward to sharing technology with you.

24 original articles published, 34 praised, 120000 visitors+
Private letter follow

Tags: OpenSSL encoding less

Posted on Sun, 02 Feb 2020 01:31:15 -0800 by JimStrosky