Académique Documents
Professionnel Documents
Culture Documents
Address generation
In order to be part of the Bitcoin network, it's necessary to
have an address from which you can send and receive funds.
Bitcoin uses public key cryptography and an address is
basically a hashed version of a public key that has been
derived from a secret private key. Surprisingly, and unlike
most public key cryptography, the public key is also kept
secret until funds are sent from the address - but more on
that later.
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 1 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 2 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
def get_private_key(hex_string):
return bytes.fromhex(hex_string.zfill(64))
# pad the hex string to the required 64
characters
def get_public_key(private_key):
# this returns the concatenated x and y
coordinates for the supplied private address
# the prepended 04 is used to signify that
it's uncompressed
return (bytes.fromhex("04") +
SigningKey.from_string(private_key,
curve=SECP256k1).verifying_key.to_string())
private_key =
get_private_key("FEEDB0BDEADBEEF")
public_key = get_public_key(private_key)
00000000000000000000000000000000000000000000000
00feedb0bdeadbeef
04d077e18fd45c031e0d256d75dfa8c3c21c589a861c4c3
3b99e64cf613113fcff9fc9d90a9d81346bcac64d3c01e6
e0ef0828543edad73c0e257b845812cc8d28
The 0x04 that prepends the public key signifies that this is an
uncompressed public key, meaning that the x and y
coordinates from the ECDSA are simply concatenated.
Because of the way ECSDA works, if you know the x value,
the y value can only take two values, one even and one odd.
Using this information, it is possible to express a public key
using x and the polarity of y. This reduces the public key size
from 65 bits to 33 bits and the key (and subsequent
computed address) are referred to as compressed. For
compressed public keys, the prepended value will be 0x02 or
0x03 depending on the polarity of y. Uncompressed public
keys are most commonly used in Bitcoin, so that's what I'll
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 3 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
import hashlib
def get_public_address(public_key):
address =
hashlib.sha256(public_key).digest()
h = hashlib.new('ripemd160')
h.update(address)
address = h.digest()
return address
public_address = get_public_address(public_key)
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 4 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
result = int.from_bytes(payload,
byteorder="big")
print(result)
while result != 0:
result, remainder = divmod(result, 58)
encoded.append(BASE58_ALPHABET[remainder])
bitcoin_address = base58_encode("00",
public_address)
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 5 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
Bootstrapping
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 6 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
import socket
version = 70014
services = 1 # not a full node, cant provide
any data
timestamp = int(time.time())
addr_recvservices = 1
addr_recvipaddress =
socket.inet_pton(socket.AF_INET6,
"::ffff:127.0.0.1") #ip address of receiving
node in big endian
addr_recvport = 8333
addr_transservices = 1
addr_transipaddress =
socket.inet_pton(socket.AF_INET6,
"::ffff:127.0.0.1")
addr_transport = 8333
nonce = 0
user_agentbytes = 0
start_height = 329167
relay = 0
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 7 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
With the data packed into the right format, and the header
attached, it can be sent off to our peer!
s = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
s.connect((node, 8333))
s.send(get_bitcoin_message("version", payload))
print(s.recv(1024))
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 8 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
don't, I'll disregard their version message and not send them
the acknowledgement. Sending the Version message as I
connect is enough to allow me to later send more
meaningful messages.
b'\xf9\xbe\xb4\xd9version\x00\x00\x00\x00\x00f\
x00\x00\x00\xf8\xdd\x9aL\x7f\x11\x01\x00\r\x00\
x00\x00\x00\x00\x00\x00\xddR1Y\x00\x00\x00\x00\
x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\xff\xff\xcb\xce\x1d\xf
c\xe9j\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0
0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x
00\x00\x00\x00\x06\xb8>*\x88@I\x8e\x10/Satoshi:
0.14.0/t)\x07\x00\x01\xf9\xbe\xb4\xd9verack\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00]\xf6\xe0\x
e2'
Bitcoin transactions
To transfer Bitcoin it's necessary to broadcast a transaction
to the Bitcoin network.
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 9 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 10 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
address 1QGNXLzGXhWTKF3HTSjuBMpQyUYFkWfgVC.
Field Description
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 11 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
Ignoring the signature script and pubkey script for now, it's
quite easy to see what should go in the other fields of the
raw transaction. To send the funds in my FEEDB0BDEADBEEF
address to my BADCAFEFABC0FFEE address, I look at the
transaction that was created by the exchange. This gives
me:
The transaction id is
95855ba9f46c6936d7b5ee6733c81e715ac92199938ce30ac3e1214b8c2
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 12 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
OP_DUP
OP_HASH160
<Length of address in bytes>
<Bitcoin address>
OP_EQUALVERIFY
OP_CHECKSIG
0x76
0xA9
0x14
0xFF33195EC053D6E58D5FD3CC67747D3E1C71B280
0x88
0xAC
There are two separate, but somewhat related, uses for the
signature script in a (p2pk) transaction:
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 13 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
def get_p2pkh_script(pub_key):
"""
This is the standard 'pay to pubkey hash'
script
"""
# OP_DUP then OP_HASH160 then 20 bytes (pub
address length)
script = bytes.fromhex("76a914")
return script
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 14 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
transaction["sig_script"] =
get_p2pkh_script(from_addr)
transaction["sequence"] = 0xffffffff
transaction["num_outputs"] = 1
transaction["satoshis"] = satoshis_spend
transaction["pubkey_length"] = 25
transaction["pubkey_script"] =
get_p2pkh_script(to_addr)
transaction["lock_time"] = 0
transaction["hash_code_type"] = 1
return transaction
Calling the code with the following values creates the raw
transaction that I'm interested in making.
private_key =
address_utils.get_private_key("FEEDB0BDEADBEEF"
)
public_key =
address_utils.get_public_key(private_key)
from_address =
address_utils.get_public_address(public_key)
to_address =
address_utils.get_public_address(address_utils.
get_public_key(address_utils.get_private_key("B
ADCAFEFABC0FFEE")))
transaction_id =
"95855ba9f46c6936d7b5ee6733c81e715ac92199938ce3
0ac3e1214b8c2cd8d7"
satoshis = 380000
output_index = 1
raw = get_raw_transaction(from_address,
to_address, transaction_id, output_index,
satoshis)
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 15 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
def get_transaction_signature(transaction,
private_key):
"""
Gets the sigscript of a raw transaction
private_key should be in bytes form
"""
packed_raw_transaction =
get_packed_transaction(transaction)
hash =
hashlib.sha256(hashlib.sha256(packed_raw_transa
ction).digest()).digest()
public_key =
address_utils.get_public_key(private_key)
key = SigningKey.from_string(private_key,
curve=SECP256k1)
signature = key.sign_digest(hash,
sigencode=util.sigencode_der)
signature += bytes.fromhex("01") #hash code
type
sigscript = struct.pack("<B",
len(signature))
sigscript += signature
sigscript += struct.pack("<B",
len(public_key))
sigscript += public_key
return sigscript
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 16 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
OP_EQUALVERIFY
OP_CHECKSIG
This script will fail if the provided public key doesn't hash to
the public key hash in the script or if the provided signature
doesn't match the provided public key. This ensures that
only the person that holds the private key for the address in
the pubkey script is able to spend the output.
You can see that here is the first time I have needed to
provide my public key anywhere. Up until this point, only my
public address has been published. It's necessary to provide
the public key here as it is allows verification of the signature
that the transaction has been signed with.
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 17 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
signature = get_transaction_signature(raw,
private_key )
raw["sig_script_length"] = len(signature)
raw["sig_script"] = signature
del raw["hash_code_type"]
transaction = get_packed_transaction(raw)
s = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
s.connect((get_bitcoin_peer(), 8333))
s.send(get_bitcoin_message("version",
get_version_payload())
s.send(get_bitcoin_message("tx", transaction)
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 18 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
Conclusion
I hope you've gained some appreciation of how Bitcoin
works through reading this article, I know I certainly did
during the months it took me to put all of this together!
While most of the information presented here isn't too
practicably applicable - you'd normally just use a client that
does it all for you - I think having a greater understanding of
how things work gives you a better appreciation of what's
happening under the covers and gives makes you a more
confident user of the technology.
If you've read this far you might have realised that the
380000 Satoshi (or 0.0038 BTC) that I transferred into
1QGNXLzGXhWTKF3HTSjuBMpQyUYFkWfgVC can, with enough
smarts, be taken by anyone.. as the private key for the
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 19 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
Further resources
If you found this article interesting, some further resources
to check out:
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 20 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
Back to Home
Comments Community !
1 Login
Sort by Best
$ Recommend 13 Share
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 21 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 23 of 24
A peek under Bitcoin's hood | Sam Lewis 12/06/2017, 10)42
Subscribe
d Add Disqus to your siteAdd DisqusAdd ) Privacy
http://www.samlewis.me/2017/06/a-peek-under-bitcoins-hood/ Page 24 of 24