├── README.md ├── keyUtils.py ├── makeAddr.py ├── makeTransaction.py ├── mine.py ├── minimalPeerConnection.py ├── minimalSendTxn.py ├── msgUtils.py ├── sendBitcoinTransaction.py ├── txnUtils.py └── utils.py /README.md: -------------------------------------------------------------------------------- 1 | Example Python files for 2 | Bitcoins the hard way: Using the raw Bitcoin protocol 3 | http://www.righto.com/2014/02/bitcoins-hard-way-using-raw-bitcoin.html 4 | 5 | These files are for illustration of how the Bitcoin protocol works 6 | and should not be used for real purposes. 7 | -------------------------------------------------------------------------------- /keyUtils.py: -------------------------------------------------------------------------------- 1 | # https://pypi.python.org/pypi/ecdsa/0.10 2 | import ecdsa 3 | import ecdsa.der 4 | import ecdsa.util 5 | import hashlib 6 | import unittest 7 | import random 8 | import re 9 | import struct 10 | 11 | import utils 12 | 13 | # https://en.bitcoin.it/wiki/Wallet_import_format 14 | def privateKeyToWif(key_hex): 15 | return utils.base58CheckEncode(0x80, key_hex.decode('hex')) 16 | 17 | def wifToPrivateKey(s): 18 | b = utils.base58CheckDecode(s) 19 | return b.encode('hex') 20 | 21 | # Input is a hex-encoded, DER-encoded signature 22 | # Output is a 64-byte hex-encoded signature 23 | def derSigToHexSig(s): 24 | s, junk = ecdsa.der.remove_sequence(s.decode('hex')) 25 | if junk != '': 26 | print 'JUNK', junk.encode('hex') 27 | assert(junk == '') 28 | x, s = ecdsa.der.remove_integer(s) 29 | y, s = ecdsa.der.remove_integer(s) 30 | return '%064x%064x' % (x, y) 31 | 32 | # Input is hex string 33 | def privateKeyToPublicKey(s): 34 | sk = ecdsa.SigningKey.from_string(s.decode('hex'), curve=ecdsa.SECP256k1) 35 | vk = sk.verifying_key 36 | return ('\04' + sk.verifying_key.to_string()).encode('hex') 37 | 38 | # Input is hex string 39 | def keyToAddr(s): 40 | return pubKeyToAddr(privateKeyToPublicKey(s)) 41 | 42 | def pubKeyToAddr(s): 43 | ripemd160 = hashlib.new('ripemd160') 44 | ripemd160.update(hashlib.sha256(s.decode('hex')).digest()) 45 | return utils.base58CheckEncode(0, ripemd160.digest()) 46 | 47 | def addrHashToScriptPubKey(b58str): 48 | assert(len(b58str) == 34) 49 | # 76 A9 14 (20 bytes) 88 AC 50 | return '76a914' + utils.base58CheckDecode(b58str).encode('hex') + '88ac' 51 | 52 | 53 | class TestKey(unittest.TestCase): 54 | 55 | def test_privateKeyToWif(self): 56 | w = privateKeyToWif("0C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D") 57 | self.assertEqual(w, "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ") 58 | 59 | def test_WifToPrivateKey(self): 60 | k = wifToPrivateKey("5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ") 61 | self.assertEqual(k.upper(), "0C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D") 62 | 63 | def test_keyToAddr(self): 64 | a = keyToAddr("18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725") 65 | self.assertEqual(a, "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM") 66 | 67 | def test_pairs1(self): 68 | #blockchain.info 69 | wallet_addr = "1EyBEhrriJeghX4iqATQEWDq38Ae8ubBJe" 70 | wallet_private = "8tnArBrrp4KHVjv8WA6HiX4ev56WDhqGA16XJCHJzhNH" 71 | wallet_key = utils.base256encode(utils.base58decode(wallet_private)).encode('hex') 72 | self.assertEqual(keyToAddr(wallet_key), wallet_addr) 73 | 74 | # can import into multibit 75 | bitcoin_qt = "5Jhw8B9J9QLaMmcBRfz7x8KkD9gwbNoyBMfWyANqiDwm3FFwgGC" 76 | wallet_key = utils.base58CheckDecode(bitcoin_qt).encode('hex') 77 | self.assertEqual(keyToAddr(wallet_key), wallet_addr) 78 | 79 | wallet_key = "754580de93eea21579441b58e0c9b09f54f6005fc71135f5cfac027394b22caa" 80 | self.assertEqual(keyToAddr(wallet_key), wallet_addr) 81 | 82 | def test_pairs2(self): 83 | #http://gobittest.appspot.com/Address 84 | # Cannot import into multibit 85 | wallet_private = "BB08A897EA1E422F989D36DE8D8186D8406BE25E577FD2A66976BF172325CDC9" 86 | wallet_addr = "1MZ1nxFpvUgaPYYWaLPkLGAtKjRqcCwbGh" 87 | self.assertEqual(keyToAddr(wallet_private), wallet_addr) 88 | 89 | def test_pairs3(self): 90 | # Can import into multibit 91 | # http://bitaddress.org 92 | wallet_private = "5J8PhneLEaL9qEPvW5voRgrELeXcmM12B6FbiA9wZAwDMnJMb2L" 93 | wallet_addr = "1Q2SuNLDXDtda7DPnBTocQWtUg1v4xZMrV" 94 | self.assertEqual(keyToAddr(utils.base58CheckDecode(wallet_private).encode('hex')), wallet_addr) 95 | 96 | def test_der(self): 97 | self.assertEqual(ecdsa.der.encode_sequence( 98 | ecdsa.der.encode_integer(0x123456), 99 | ecdsa.der.encode_integer(0x89abcd)).encode('hex'), 100 | "300b020312345602040089abcd") 101 | def test_derSigToHexSig(self): 102 | derSig = "304502204c01fee2d724fb2e34930c658f585d49be2f6ac87c126506c0179e6977716093022100faad0afd3ae536cfe11f83afaba9a8914fc0e70d4c6d1495333b2fb3df6e8cae" 103 | self.assertEqual("4c01fee2d724fb2e34930c658f585d49be2f6ac87c126506c0179e6977716093faad0afd3ae536cfe11f83afaba9a8914fc0e70d4c6d1495333b2fb3df6e8cae", 104 | derSigToHexSig(derSig)) 105 | 106 | 107 | txn = ("0100000001a97830933769fe33c6155286ffae34db44c6b8783a2d8ca52ebee6414d399ec300000000" + 108 | "8a47" + 109 | "304402202c2e1a746c556546f2c959e92f2d0bd2678274823cc55e11628284e4a13016f80220797e716835f9dbcddb752cd0115a970a022ea6f2d8edafff6e087f928e41baac01" + 110 | "41" + 111 | "04392b964e911955ed50e4e368a9476bc3f9dcc134280e15636430eb91145dab739f0d68b82cf33003379d885a0b212ac95e9cddfd2d391807934d25995468bc55" + 112 | "ffffffff02015f0000000000001976a914c8e90996c7c6080ee06284600c684ed904d14c5c88ac204e000000000000" + 113 | "1976a914348514b329fda7bd33c7b2336cf7cd1fc9544c0588ac00000000") 114 | myTxn_forSig = ("0100000001a97830933769fe33c6155286ffae34db44c6b8783a2d8ca52ebee6414d399ec300000000" + 115 | "1976a914" + "167c74f7491fe552ce9e1912810a984355b8ee07" + "88ac" + 116 | "ffffffff02015f0000000000001976a914c8e90996c7c6080ee06284600c684ed904d14c5c88ac204e000000000000" + 117 | "1976a914348514b329fda7bd33c7b2336cf7cd1fc9544c0588ac00000000" + 118 | "01000000") 119 | public_key = "04392b964e911955ed50e4e368a9476bc3f9dcc134280e15636430eb91145dab739f0d68b82cf33003379d885a0b212ac95e9cddfd2d391807934d25995468bc55" 120 | hashToSign = hashlib.sha256(hashlib.sha256(myTxn_forSig.decode('hex')).digest()).digest().encode('hex') 121 | sig_der = "304402202c2e1a746c556546f2c959e92f2d0bd2678274823cc55e11628284e4a13016f80220797e716835f9dbcddb752cd0115a970a022ea6f2d8edafff6e087f928e41baac01"[:-2] 122 | sig = derSigToHexSig(sig_der) 123 | 124 | vk = ecdsa.VerifyingKey.from_string(public_key[2:].decode('hex'), curve=ecdsa.SECP256k1) 125 | self.assertEquals(vk.verify_digest(sig.decode('hex'), hashToSign.decode('hex')), True) 126 | #OP_DUP OP_HASH160 167c74f7491fe552ce9e1912810a984355b8ee07 OP_EQUALVERIFY OP_CHECKSIG 127 | 128 | if __name__ == '__main__': 129 | unittest.main() 130 | -------------------------------------------------------------------------------- /makeAddr.py: -------------------------------------------------------------------------------- 1 | import random, keyUtils 2 | 3 | private_key = ''.join(['%x' % random.randrange(16) for x in range(0, 64)]) 4 | print keyUtils.privateKeyToWif(private_key) 5 | print keyUtils.keyToAddr(private_key) 6 | 7 | -------------------------------------------------------------------------------- /makeTransaction.py: -------------------------------------------------------------------------------- 1 | import utils 2 | import keyUtils 3 | import txnUtils 4 | 5 | 6 | privateKey = keyUtils.wifToPrivateKey("5HusYj2b2x4nroApgfvaSfKYZhRbKFH41bVyPooymbC6KfgSXdD") #1MMMM 7 | 8 | signed_txn = txnUtils.makeSignedTransaction(privateKey, 9 | "81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48", # output (prev) transaction hash 10 | 0, # sourceIndex 11 | keyUtils.addrHashToScriptPubKey("1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5"), 12 | [[91234, #satoshis 13 | keyUtils.addrHashToScriptPubKey("1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa")]] 14 | ) 15 | 16 | txnUtils.verifyTxnSignature(signed_txn) 17 | print 'SIGNED TXN', signed_txn 18 | -------------------------------------------------------------------------------- /mine.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import struct 3 | import time 4 | 5 | 6 | # block 272784 7 | # http://blockexplorer.com/block/0000000000000003ce284b6b4244552242e8e64efc2df1bdadb93f392ade1a35 8 | 9 | ver = 2 10 | prev_block = "000000000000000027d4d014f6245170390ea904cec17d1761021d28049c20b9" 11 | mrkl_root = "57ebfd5b7fed40fc07120b1b03550ea1069d74b2dfc8cf81dc25bb48cee4221f" 12 | time_ = 1389075720 # Mon Jan 06 22:22:00 2014 13 | bits = 419628831 14 | p = '' 15 | 16 | # https://en.bitcoin.it/wiki/Difficulty 17 | exp = bits >> 24 18 | mant = bits & 0xffffff 19 | target = mant * (1<<(8*(exp - 3))) 20 | target_hexstr = '%064x' % target 21 | print target_hexstr 22 | target_str = target_hexstr.decode('hex') 23 | print repr(target_str) 24 | 25 | nonce = 100000000 26 | while 1: 27 | nonce += 1 28 | header = ( struct.pack(" 0: 25 | chunk = sock.recv(payload_len) 26 | if len(chunk) == 0: break 27 | buf += chunk 28 | payload_len -= len(chunk) 29 | print 'got chunk of', len(chunk) 30 | msgUtils.processChunk(header, buf) 31 | 32 | #if step == 0: 33 | # sock.send(msg_getblocks) 34 | step += 1 35 | 36 | if step == 5: 37 | msg = msgUtils.getAddrMsg() 38 | sock.send(msg) 39 | print 'SENT', msg.encode('hex') 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /txnUtils.py: -------------------------------------------------------------------------------- 1 | # https://pypi.python.org/pypi/ecdsa/0.10 2 | 3 | import ecdsa 4 | import hashlib 5 | import struct 6 | import unittest 7 | 8 | import utils 9 | import keyUtils 10 | 11 | # Makes a transaction from the inputs 12 | # outputs is a list of [redemptionSatoshis, outputScript] 13 | def makeRawTransaction(outputTransactionHash, sourceIndex, scriptSig, outputs): 14 | def makeOutput(data): 15 | redemptionSatoshis, outputScript = data 16 | return (struct.pack("4sH', ipaddr, port)) 27 | # return value, len 28 | def processVarInt(payload): 29 | n0 = ord(payload[0]) 30 | if n0 < 0xfd: 31 | return [n0, 1] 32 | elif n0 == 0xfd: 33 | return [struct.unpack('= 26) 47 | return '%d.%d.%d.%d:%d' % (ord(payload[20]), ord(payload[21]), 48 | ord(payload[22]), ord(payload[23]), 49 | struct.unpack('!H', payload[24:26])[0]) 50 | 51 | 52 | def base58encode(n): 53 | result = '' 54 | while n > 0: 55 | result = b58[n%58] + result 56 | n /= 58 57 | return result 58 | 59 | def base58decode(s): 60 | result = 0 61 | for i in range(0, len(s)): 62 | result = result * 58 + b58.index(s[i]) 63 | return result 64 | 65 | def base256encode(n): 66 | result = '' 67 | while n > 0: 68 | result = chr(n % 256) + result 69 | n /= 256 70 | return result 71 | 72 | def base256decode(s): 73 | result = 0 74 | for c in s: 75 | result = result * 256 + ord(c) 76 | return result 77 | 78 | def countLeadingChars(s, ch): 79 | count = 0 80 | for c in s: 81 | if c == ch: 82 | count += 1 83 | else: 84 | break 85 | return count 86 | 87 | # https://en.bitcoin.it/wiki/Base58Check_encoding 88 | def base58CheckEncode(version, payload): 89 | s = chr(version) + payload 90 | checksum = hashlib.sha256(hashlib.sha256(s).digest()).digest()[0:4] 91 | result = s + checksum 92 | leadingZeros = countLeadingChars(result, '\0') 93 | return '1' * leadingZeros + base58encode(base256decode(result)) 94 | 95 | def base58CheckDecode(s): 96 | leadingOnes = countLeadingChars(s, '1') 97 | s = base256encode(base58decode(s)) 98 | result = '\0' * leadingOnes + s[:-4] 99 | chk = s[-4:] 100 | checksum = hashlib.sha256(hashlib.sha256(result).digest()).digest()[0:4] 101 | assert(chk == checksum) 102 | version = result[0] 103 | return result[1:] 104 | 105 | class TestUtils(unittest.TestCase): 106 | def test_varint(self): 107 | self.assertEqual(varint(0x42), '\x42') 108 | self.assertEqual(varint(0x123), '\xfd\x23\x01') 109 | self.assertEqual(varint(0x12345678), '\xfe\x78\x56\x34\x12') 110 | self.assertEqual(processVarInt(varint(0x42)), [0x42, 1]) 111 | self.assertEqual(processVarInt(varint(0x1234)), [0x1234, 3]) 112 | 113 | def test_varstr(self): 114 | self.assertEqual(varstr('abc'), '\x03abc') 115 | self.assertEqual(processVarStr('\x03abc'), ['abc', 4]) 116 | 117 | def test_processAddr(self): 118 | self.assertEqual(processAddr('x'*20 + '\x62\x91\x98\x16\x20\x8d'), 119 | '98.145.152.22:8333') 120 | 121 | def test_countLeadingCharacters(self): 122 | self.assertEqual(countLeadingChars('a\0bcd\0', '\0'), 0) 123 | self.assertEqual(countLeadingChars('\0\0a\0bcd\0', '\0'), 2) 124 | self.assertEqual(countLeadingChars('1a\0bcd\0', '1'), 1) 125 | 126 | def test_base256(self): 127 | self.assertEqual(base256encode(base256decode('abc')), 'abc') 128 | self.assertEqual(base256encode(0x4142), 'AB') 129 | self.assertEqual(base256decode('AB'), 0x4142) 130 | 131 | def test_base58(self): 132 | self.assertEqual(base58encode(base58decode('abc')), 'abc') 133 | self.assertEqual(base58decode('121'), 58) 134 | self.assertEqual(base58decode('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'), 135 | 0x800C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D507A5B8D) 136 | 137 | def test_base58check(self): 138 | self.assertEqual(base58CheckDecode(base58CheckEncode(42, 'abc')), 'abc') 139 | self.assertEqual(base58CheckDecode(base58CheckEncode(0, '\0\0abc')), '\0\0abc') 140 | s = base256encode(0x0C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D) 141 | b = base58CheckEncode(0x80, s) 142 | self.assertEqual(b, "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ") 143 | 144 | if __name__ == '__main__': 145 | unittest.main() 146 | --------------------------------------------------------------------------------