mirror of
https://github.com/bitcoinbook/bitcoinbook
synced 2024-12-26 08:28:15 +00:00
code examples
This commit is contained in:
parent
a8fafbc6c0
commit
aa332a7008
@ -1130,7 +1130,7 @@ ae74538baa914f3799081ba78429d5d84f36a0127438e9f721dff584ac17b346
|
||||
|
||||
As before, we can also examine this in more detail using the +getrawtransaction+ and +decodetransaction+ commands. These commands will return the exact same hex string that we produced and decoded previously just before we sent it on the network.
|
||||
|
||||
|
||||
[[alt_libraries]]
|
||||
=== Alternative clients, libraries and toolkits
|
||||
|
||||
Beyond the reference client, bitcoind, there are other clients and libraries that can be used to interact with the bitcoin network and data structures. These are implemented in a variety of programming languages, offering programmers native interfaces in their own language.
|
||||
|
109
ch04.asciidoc
109
ch04.asciidoc
@ -305,6 +305,35 @@ In bitcoin, most of the data presented to the user is Base58Check encoded to mak
|
||||
| BIP32 Extended Public Key | 0x0488B21E | xpub
|
||||
|=======
|
||||
|
||||
===== Creating a bitcoin address from a private key
|
||||
|
||||
Let's look at the complete process of creating a bitcoin address, from a private key, to a public key (a point on the elliptic curve), to a double-hashed address and finally the Base58Check encoding. The C++ code in <<addr_example>> shows the complete process, from private key, to Base58Check encoded bitcoin address, step-by-step. The code example uses the libbitcoin library introduced in <<alt_libraries>> for some helper functions:
|
||||
|
||||
[[addr_example]]
|
||||
.Creating a Base58Check encoded bitcoin address from a private key
|
||||
====
|
||||
[source, cpp]
|
||||
----
|
||||
include::code/addr.cpp[]
|
||||
----
|
||||
====
|
||||
|
||||
The code uses a pre-defined private key, so that it produces the same bitcoin address every time it is run:
|
||||
|
||||
[[addr_example_run]]
|
||||
.Compiling and running the addr code
|
||||
====
|
||||
[source,bash]
|
||||
----
|
||||
# Compile the addr.cpp code
|
||||
$ g++ -o addr addr.cpp $(pkg-config --cflags --libs libbitcoin)
|
||||
# Run the addr executable
|
||||
$ ./addr
|
||||
Public key: 0202a406624211f2abbdc68da3df929f938c3399dd79fac1b51b0e4ad1d26a47aa
|
||||
Address: 1PRTTaJesdNovgne6Ehcdu1fpEdX7913CK
|
||||
----
|
||||
====
|
||||
|
||||
==== Key Formats
|
||||
|
||||
[[priv_formats]]
|
||||
@ -442,13 +471,20 @@ Here's the same key, encoded in WIF and WIF-compressed formats
|
||||
|
||||
The most comprehensive bitcoin library in Python is "pybitcointools"by Vitalik Buterin (https://github.com/vbuterin/pybitcointools). In the following code example, we use the pybitcointools library (imported as "bitcoin") to generate and display keys and addresses in various formats:
|
||||
|
||||
[[key-to-address_script]]
|
||||
.Key and Address generation and formatting with the pybitcointools library
|
||||
====
|
||||
[source,python]
|
||||
----
|
||||
include::code/key-to-address-ecc-example.py[]
|
||||
----
|
||||
====
|
||||
|
||||
Here's the output from running this code:
|
||||
|
||||
[[key-to-address_script_run]]
|
||||
.Running key-to-address-ecc-example.py
|
||||
====
|
||||
[source,bash]
|
||||
----
|
||||
$ python key-to-address-ecc-example.py
|
||||
@ -475,8 +511,40 @@ Bitcoin Address (b58check) is:
|
||||
Compressed Bitcoin Address (b58check) is:
|
||||
14cxpo3MBCYYWCgF74SWTdcmxipnGUsPw3
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
Here's another example, using the Python ECDSA library for the Elliptic Curve math and without using any specialized bitcoin libraries:
|
||||
|
||||
[[ec_math]]
|
||||
.A script demonstrating Elliptic Curve math used for bitcoin keys
|
||||
====
|
||||
[source, python]
|
||||
----
|
||||
include::code/ec-math.py[]
|
||||
----
|
||||
====
|
||||
|
||||
Running the script:
|
||||
|
||||
[[ec_math_run]]
|
||||
.Installing the Python ECDSA library and running the ec_math.py script
|
||||
====
|
||||
[source,bash]
|
||||
----
|
||||
$ # Install Python PIP package manager
|
||||
$ sudo apt-get install python-pip
|
||||
$ # Install the Python ECDSA library
|
||||
$ sudo pip install ecdsa
|
||||
$ # Run the script
|
||||
$ python ec-math.py
|
||||
Secret: 38090835015954358862481132628887443905906204995912378278060168703580660294000
|
||||
EC point: (70048853531867179489857750497606966272382583471322935454624595540007269312627,\
|
||||
105262206478686743191060800263479589329920209527285803935736021686045542353380)
|
||||
BTC public key: 029ade3effb0a67d5c8609850d797366af428f4a0d5194cb221d807770a1522873
|
||||
----
|
||||
====
|
||||
|
||||
=== Wallets
|
||||
|
||||
Wallets are containers for private keys, usually implemented as structured files or simple databases.
|
||||
@ -834,6 +902,47 @@ As you can see, Eugenia won't be creating the vanity address "1KidsCharity" any
|
||||
|
||||
Another way to find a vanity address is to outsource the work to a pool of vanity-miners, such as the pool at vanitypool.appspot.com. A pool is a service that allows those with GPU hardware to earn bitcoin searching for vanity addresses for others. For a small payment (0.01 bitcoin or approximately $5 when this was written), Eugenia can outsource the search for a 7-character pattern vanity address and get results in a few hours instead of having to run a CPU search for months.
|
||||
|
||||
Generating a vanity address is a brute-force exercise: try a random key, check the resulting address to see if it matches the desired pattern, repeat until successful. Here's an example of a "vanity miner", a program designed to find vanity addresses, written in C++. The example uses the libbitcoin library, which we introduced in <<alt_libraries>>.
|
||||
|
||||
[[vanity_miner_code]]
|
||||
.Vanity Address Miner
|
||||
====
|
||||
[source,cpp]
|
||||
----
|
||||
include::code/vanity-miner.cpp[]
|
||||
----
|
||||
====
|
||||
|
||||
The example code must be compiled using a C++ compiler and linked against the libbitcoin library (which must be first installed on that system). To run the example, run the +vanity-minder+ executable with no parameters and it will attempt to find a vanity address starting with "1kid":
|
||||
|
||||
[[vanity_miner_run]]
|
||||
.Compiling and running the vanity-miner example
|
||||
====
|
||||
[source,bash]
|
||||
----
|
||||
$ # Compile the code with g++
|
||||
$ g++ -o vanity-miner vanity-miner.cpp $(pkg-config --cflags --libs libbitcoin)
|
||||
$ # Run the example
|
||||
$ ./vanity-miner
|
||||
Found vanity address! 1KiDzkG4MxmovZryZRj8tK81oQRhbZ46YT
|
||||
Secret: 57cc268a05f83a23ac9d930bc8565bac4e277055f4794cbd1a39e5e71c038f3f
|
||||
$ # Run it again for a different result
|
||||
$ ./vanity-miner
|
||||
Found vanity address! 1Kidxr3wsmMzzouwXibKfwTYs5Pau8TUFn
|
||||
Secret: 7f65bbbbe6d8caae74a0c6a0d2d7b5c6663d71b60337299a1a2cf34c04b2a623
|
||||
# Use "time" to see how long it takes to find a result
|
||||
$ time ./vanity-miner
|
||||
Found vanity address! 1KidPWhKgGRQWD5PP5TAnGfDyfWp5yceXM
|
||||
Secret: 2a802e7a53d8aa237cd059377b616d2bfcfa4b0140bc85fa008f2d3d4b225349
|
||||
|
||||
real 0m8.868s
|
||||
user 0m8.828s
|
||||
sys 0m0.035s
|
||||
----
|
||||
====
|
||||
|
||||
The example code will take a few seconds to find a match for the three-character pattern "kid", as we can see when we use the +time+ Unix command to measure the execution time. Change the +search+ pattern in the source code and see how much longer it takes for four- or five-character patterns!
|
||||
|
||||
===== Vanity Address Security
|
||||
|
||||
Vanity addresses can be used to enhance _and_ to defeat security measures, they are truly a double-edged sword. Used to improve security, a distinctive address makes it harder for adversaries to substitute their own address and fool your customers into paying them instead of you. Unfortunately, vanity addresses also make it possible for anyone to create an address that _resembles_ any random address, or even another vanity address, thereby fooling your customers.
|
||||
|
@ -189,6 +189,41 @@ To prove that a specific transaction is included in a block, a node only needs t
|
||||
.A Merkle Path used to prove inclusion of a data element
|
||||
image::images/MerkleTreePathToK.png["merkle_tree_path"]
|
||||
|
||||
|
||||
The code in <<merkle_example>> demonstrates the process of creating a merkle tree from the leaf-node hashes up to the root, using the libbitcoin library for some helper functions:
|
||||
|
||||
[[merkle_example]]
|
||||
.Building a merkle tree
|
||||
====
|
||||
[source, cpp]
|
||||
----
|
||||
include::code/merkle.cpp[]
|
||||
----
|
||||
====
|
||||
|
||||
Compiling and running the merkle code:
|
||||
|
||||
[[merkle_example_run]]
|
||||
.Compiling and running the merkle example code
|
||||
====
|
||||
[source,bash]
|
||||
----
|
||||
$ # Compile the merkle.cpp code
|
||||
$ g++ -o merkle merkle.cpp $(pkg-config --cflags --libs libbitcoin)
|
||||
$ # Run the merkle executable
|
||||
$ ./merkle
|
||||
Current merkle hash list:
|
||||
32650049a0418e4380db0af81788635d8b65424d397170b8499cdc28c4d27006
|
||||
30861db96905c8dc8b99398ca1cd5bd5b84ac3264a4e1b3e65afa1bcee7540c4
|
||||
|
||||
Current merkle hash list:
|
||||
d47780c084bad3830bcdaf6eace035e4c6cbf646d103795d22104fb105014ba3
|
||||
|
||||
Result: d47780c084bad3830bcdaf6eace035e4c6cbf646d103795d22104fb105014ba3
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
The efficiency of merkle trees becomes obvious as the scale increases. For example, proving that a transaction is part of a block requires:
|
||||
|
||||
[[block_structure]]
|
||||
|
@ -353,6 +353,32 @@ The next few hexadecimal digits (+03858402062+) are used to encode an extra _non
|
||||
|
||||
The final part of the coinbase data (+2f503253482f+) is the ASCII-encoded string "/P2SH/", which indicates that the mining node that mined this block supports the Pay-to-Script-Hash (P2SH) improvement defined in BIP0016. The introduction of the P2SH capability required a "vote" by miners to endorse either BIP0016 or BIP0017. Those endorsing the BIP0016 implementation were to include "/P2SH/" in their coinbase data. Those endorsing the BIP0017 implementation of P2SH were to include the string "p2sh/CHV" in their coinbase data. The BIP0016 was elected as the winner, and many miners continued including the string "/P2SH/" in their coinbase to indicate support for this feature.
|
||||
|
||||
The code example <<satoshi_words>> uses the libbitcoin library introduced in <<alt_libraries>> to extract the coinbase data from the genesis block, displaying Satoshi's message. Note that the libbitcoin library contains a static copy of the genesis block, so the example code below can use retrieve the genesis block directly from the library.
|
||||
|
||||
[[satoshi_words]]
|
||||
.Extract the coinbase data from the genesis block
|
||||
====
|
||||
[source, cpp]
|
||||
----
|
||||
include::code/satoshi-words.cpp[]
|
||||
----
|
||||
====
|
||||
|
||||
We compile the code with the GNU C++ compiler and run the resulting executable:
|
||||
|
||||
[[satoshi_words_run]]
|
||||
.Compiling and running the satoshi-words example code
|
||||
====
|
||||
[source,bash]
|
||||
----
|
||||
$ # Compile the code
|
||||
$ g++ -o satoshi-words satoshi-words.cpp $(pkg-config --cflags --libs libbitcoin)
|
||||
$ # Run the executable
|
||||
$ ./satoshi-words
|
||||
^D<><44><GS>^A^DEThe Times 03/Jan/2009 Chancellor on brink of second bailout for banks
|
||||
----
|
||||
====
|
||||
|
||||
=== Constructing the Block Header
|
||||
|
||||
To construct the block header, the mining node needs to fill in six fields:
|
||||
|
40
code/addr.cpp
Normal file
40
code/addr.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#include <bitcoin/bitcoin.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
// Private secret key.
|
||||
bc::ec_secret secret = bc::decode_hash(
|
||||
"038109007313a5807b2eccc082c8c3fbb988a973cacf1a7df9ce725c31b14776");
|
||||
// Get public key.
|
||||
bc::ec_point public_key = bc::secret_to_public_key(secret);
|
||||
std::cout << "Public key: " << bc::encode_hex(public_key) << std::endl;
|
||||
|
||||
// Create Bitcoin address.
|
||||
// Normally you can use:
|
||||
// bc::payment_address payaddr;
|
||||
// bc::set_public_key(payaddr, public_key);
|
||||
// const std::string address = payaddr.encoded();
|
||||
|
||||
// Compute hash of public key for P2PKH address.
|
||||
const bc::short_hash hash = bc::bitcoin_short_hash(public_key);
|
||||
|
||||
bc::data_chunk unencoded_address;
|
||||
// Reserve 25 bytes
|
||||
// [ version:1 ]
|
||||
// [ hash:20 ]
|
||||
// [ checksum:4 ]
|
||||
unencoded_address.reserve(25);
|
||||
// Version byte, 0 is normal BTC address (P2PKH).
|
||||
unencoded_address.push_back(0);
|
||||
// Hash data
|
||||
bc::extend_data(unencoded_address, hash);
|
||||
// Checksum is computed by hashing data, and adding 4 bytes from hash.
|
||||
bc::append_checksum(unencoded_address);
|
||||
// Finally we must encode the result in Bitcoin's base58 encoding
|
||||
assert(unencoded_address.size() == 25);
|
||||
const std::string address = bc::encode_base58(unencoded_address);
|
||||
|
||||
std::cout << "Address: " << address << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
53
code/ec-math.py
Normal file
53
code/ec-math.py
Normal file
@ -0,0 +1,53 @@
|
||||
import ecdsa
|
||||
import random
|
||||
from ecdsa.util import string_to_number, number_to_string
|
||||
|
||||
# secp256k1, http://www.oid-info.com/get/1.3.132.0.10
|
||||
_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL
|
||||
_r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141L
|
||||
_b = 0x0000000000000000000000000000000000000000000000000000000000000007L
|
||||
_a = 0x0000000000000000000000000000000000000000000000000000000000000000L
|
||||
_Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798L
|
||||
_Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8L
|
||||
curve_secp256k1 = ecdsa.ellipticcurve.CurveFp(_p, _a, _b)
|
||||
generator_secp256k1 = ecdsa.ellipticcurve.Point(curve_secp256k1, _Gx, _Gy, _r)
|
||||
oid_secp256k1 = (1, 3, 132, 0, 10)
|
||||
SECP256k1 = ecdsa.curves.Curve("SECP256k1", curve_secp256k1, generator_secp256k1, oid_secp256k1)
|
||||
ec_order = _r
|
||||
|
||||
curve = curve_secp256k1
|
||||
generator = generator_secp256k1
|
||||
|
||||
def random_secret():
|
||||
random_char = lambda: chr(random.randint(0, 255))
|
||||
convert_to_int = lambda array: int("".join(array).encode("hex"), 16)
|
||||
byte_array = [random_char() for i in range(32)]
|
||||
return convert_to_int(byte_array)
|
||||
|
||||
def get_point_pubkey(point):
|
||||
if point.y() & 1:
|
||||
key = '03' + '%064x' % point.x()
|
||||
else:
|
||||
key = '02' + '%064x' % point.x()
|
||||
return key.decode('hex')
|
||||
|
||||
def get_point_pubkey_uncompressed(point):
|
||||
key = '04' + \
|
||||
'%064x' % point.x() + \
|
||||
'%064x' % point.y()
|
||||
return key.decode('hex')
|
||||
|
||||
# Generate a new private key.
|
||||
secret = random_secret()
|
||||
print "Secret: ", secret
|
||||
|
||||
# Get the public key point.
|
||||
point = secret * generator
|
||||
print "EC point:", point
|
||||
|
||||
print "BTC public key:", get_point_pubkey(point).encode("hex")
|
||||
|
||||
# Given the point (x, y) we can create the object using:
|
||||
point1 = ecdsa.ellipticcurve.Point(curve, point.x(), point.y(), ec_order)
|
||||
assert point1 == point
|
||||
|
62
code/merkle.cpp
Normal file
62
code/merkle.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
#include <bitcoin/bitcoin.hpp>
|
||||
|
||||
bc::hash_digest create_merkle(bc::hash_digest_list& merkle)
|
||||
{
|
||||
// Stop if hash list is empty.
|
||||
if (merkle.empty())
|
||||
return bc::null_hash;
|
||||
else if (merkle.size() == 1)
|
||||
return merkle[0];
|
||||
|
||||
// While there is more than 1 hash in the list, keep looping...
|
||||
while (merkle.size() > 1)
|
||||
{
|
||||
// If number of hashes is odd, duplicate last hash in the list.
|
||||
if (merkle.size() % 2 != 0)
|
||||
merkle.push_back(merkle.back());
|
||||
// List size is now even.
|
||||
assert(merkle.size() % 2 == 0);
|
||||
|
||||
// New hash list.
|
||||
bc::hash_digest_list new_merkle;
|
||||
// Loop through hashes 2 at a time.
|
||||
for (auto it = merkle.begin(); it != merkle.end(); it += 2)
|
||||
{
|
||||
// Join both current hashes together (concatenate).
|
||||
bc::data_chunk concat_data(bc::hash_size * 2);
|
||||
auto concat = bc::make_serializer(concat_data.begin());
|
||||
concat.write_hash(*it);
|
||||
concat.write_hash(*(it + 1));
|
||||
assert(concat.iterator() == concat_data.end());
|
||||
// Hash both of the hashes.
|
||||
bc::hash_digest new_root = bc::bitcoin_hash(concat_data);
|
||||
// Add this to the new list.
|
||||
new_merkle.push_back(new_root);
|
||||
}
|
||||
// This is the new list.
|
||||
merkle = new_merkle;
|
||||
|
||||
// DEBUG output -------------------------------------
|
||||
std::cout << "Current merkle hash list:" << std::endl;
|
||||
for (const auto& hash: merkle)
|
||||
std::cout << " " << bc::encode_hex(hash) << std::endl;
|
||||
std::cout << std::endl;
|
||||
// --------------------------------------------------
|
||||
}
|
||||
// Finally we end up with a single item.
|
||||
return merkle[0];
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Replace these hashes with ones from a block to reproduce the same merkle root.
|
||||
bc::hash_digest_list tx_hashes{{
|
||||
bc::decode_hash("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
bc::decode_hash("0000000000000000000000000000000000000000000000000000000000000011"),
|
||||
bc::decode_hash("0000000000000000000000000000000000000000000000000000000000000022"),
|
||||
}};
|
||||
const bc::hash_digest merkle_root = create_merkle(tx_hashes);
|
||||
std::cout << "Result: " << bc::encode_hex(merkle_root) << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
28
code/satoshi-words.cpp
Normal file
28
code/satoshi-words.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
Display the genesis block message by Satoshi.
|
||||
*/
|
||||
#include <iostream>
|
||||
#include <bitcoin/bitcoin.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
// Create genesis block.
|
||||
bc::block_type block = bc::genesis_block();
|
||||
// Genesis block contains a single coinbase transaction.
|
||||
assert(block.transactions.size() == 1);
|
||||
// Get first transaction in block (coinbase).
|
||||
const bc::transaction_type& coinbase_tx = block.transactions[0];
|
||||
// Coinbase tx has a single input.
|
||||
assert(coinbase_tx.inputs.size() == 1);
|
||||
const bc::transaction_input_type& coinbase_input = coinbase_tx.inputs[0];
|
||||
// Convert the input script to its raw format.
|
||||
const bc::data_chunk& raw_message = save_script(coinbase_input.script);
|
||||
// Convert this to an std::string.
|
||||
std::string message;
|
||||
message.resize(raw_message.size());
|
||||
std::copy(raw_message.begin(), raw_message.end(), message.begin());
|
||||
// Display the genesis block message.
|
||||
std::cout << message << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
70
code/vanity-miner.cpp
Normal file
70
code/vanity-miner.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
#include <bitcoin/bitcoin.hpp>
|
||||
|
||||
// The string we are searching for
|
||||
const std::string search = "1kid";
|
||||
|
||||
// Generate a random secret key. A random 32 bytes.
|
||||
bc::ec_secret random_secret(std::default_random_engine& engine);
|
||||
// Extract the Bitcoin address from an EC secret.
|
||||
std::string bitcoin_address(const bc::ec_secret& secret);
|
||||
// Case insensitive comparison with the search string.
|
||||
bool match_found(const std::string& address);
|
||||
|
||||
int main()
|
||||
{
|
||||
std::random_device random;
|
||||
std::default_random_engine engine(random());
|
||||
// Loop continuously...
|
||||
while (true)
|
||||
{
|
||||
// Generate a random secret.
|
||||
bc::ec_secret secret = random_secret(engine);
|
||||
// Get the address.
|
||||
std::string address = bitcoin_address(secret);
|
||||
// Does it match our search string? (1kid)
|
||||
if (match_found(address))
|
||||
{
|
||||
// Success!
|
||||
std::cout << "Found vanity address! " << address << std::endl;
|
||||
std::cout << "Secret: " << bc::encode_hex(secret) << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// Should never reach here!
|
||||
return 0;
|
||||
}
|
||||
|
||||
bc::ec_secret random_secret(std::default_random_engine& engine)
|
||||
{
|
||||
// Create new secret...
|
||||
bc::ec_secret secret;
|
||||
// Iterate through every byte setting a random value...
|
||||
for (uint8_t& byte: secret)
|
||||
byte = engine() % std::numeric_limits<uint8_t>::max();
|
||||
// Return result.
|
||||
return secret;
|
||||
}
|
||||
|
||||
std::string bitcoin_address(const bc::ec_secret& secret)
|
||||
{
|
||||
// Convert secret to pubkey...
|
||||
bc::ec_point pubkey = bc::secret_to_public_key(secret);
|
||||
// Finally create address.
|
||||
bc::payment_address payaddr;
|
||||
bc::set_public_key(payaddr, pubkey);
|
||||
// Return encoded form.
|
||||
return payaddr.encoded();
|
||||
}
|
||||
|
||||
bool match_found(const std::string& address)
|
||||
{
|
||||
auto addr_it = address.begin();
|
||||
// Loop through the search string comparing it to the lower case
|
||||
// character of the supplied address.
|
||||
for (auto it = search.begin(); it != search.end(); ++it, ++addr_it)
|
||||
if (*it != std::tolower(*addr_it))
|
||||
return false;
|
||||
// Reached end of search string, so address matches.
|
||||
return true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user