1
0
mirror of https://github.com/bitcoinbook/bitcoinbook synced 2024-12-26 08:28:15 +00:00

code examples

This commit is contained in:
Andreas M. Antonopoulos 2014-09-28 11:52:50 -04:00
parent a8fafbc6c0
commit aa332a7008
9 changed files with 424 additions and 1 deletions

View File

@ -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.

View File

@ -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.

View File

@ -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]]

View File

@ -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
View 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
View 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
View 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
View 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
View 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;
}