diff -uNr a/gbw-node/README b/gbw-node/README --- a/gbw-node/README cab21dccc33b4fa780ba768de2c40b9aa1f811cc9b4e260b00b43374ff707c85fa37bf2f836108059047c002dad2455850392a26e65fc1ff37ff23b61f9520c7 +++ b/gbw-node/README 0b76b48c6dc8143232742bf405a824a863298c9cd20967b4a20e6ba5721f56a1d3ba1b1baaa72a4f10b66fa8861fcf6de2fc6eecea0cb756f55478cc732345ee @@ -14,7 +14,7 @@ ------------- - sqlite 3.7.0 or newer including the sqlite3 shell -- python 2.7.x including the sqlite3 module +- python 2.7.x including the sqlite3 module (2.5+ might suffice, but untested) - Bitcoin node including at least the dumpblock fix ( http://fixpoint.welshcomputing.com/2021/alert-dumpblock-flaw-in-bitcoind-can-cause-gbw-node-to-incorrectly-report-transactions/ ) Installation diff -uNr a/gbw-node/command/gbw-node b/gbw-node/command/gbw-node --- a/gbw-node/command/gbw-node 87f5dff61f194f21839f5c0b2560382812016957525fa35cbaaaeaea95757c72b4cc2a1b4e281db65040c3fc9d7bda0d7bfb7a3da045e136c0f7776238958163 +++ b/gbw-node/command/gbw-node 47a402e270f590661d65b8300c3529294504f5535801969a15e25110aedccd9767749eaf5792ef479e77b12b1707358ce560b2e633e8a585a4505c8ad5aaa4f3 @@ -13,6 +13,7 @@ from decimal import Decimal from inspect import getdoc from getpass import getpass +from cStringIO import StringIO import errno import signal import string @@ -192,77 +193,70 @@ ################################################# # Bitcoin data parsing -# "load" functions take a memoryview and return the object and number of bytes consumed. +# "load_" functions take a seekable input stream, consuming the next object and returning it as a decoded, broken-out structure. -def load_compactsize(v): +def load_compactsize(stream): # serialize.h WriteCompactSize - size = ord(v[0]) + size = ord(stream.read(1)) if size < 253: - return size, 1 + return size elif size == 253: - return unpack_u16(v[1:3])[0], 3 + return unpack_u16(stream.read(2))[0] elif size == 254: - return unpack_u32(v[1:5])[0], 5 + return unpack_u32(stream.read(4))[0] else: - return unpack_u64(v[1:9])[0], 9 + return unpack_u64(stream.read(8))[0] -def load_string(v): +def load_string(stream): # serialize.h Serialize, std::basic_string and CScript overloads - n, i = load_compactsize(v) - return v[i:i+n].tobytes(), i+n + n = load_compactsize(stream) + return stream.read(n) def vector_loader(load_element): # serialize.h Serialize_impl - def load_vector(v): - n, i = load_compactsize(v) - r = [None]*n - for elem in xrange(n): - r[elem], delta = load_element(v[i:]) - i += delta - return r, i + def load_vector(stream): + n = load_compactsize(stream) + return [load_element(stream) for _ in range(n)] return load_vector -def load_txin(v): +def load_txin(stream): # main.h CTxIn - i = 36 - txid, pos = unpack_outpoint(v[:i]) - scriptsig, delta = load_string(v[i:]) - i += delta - i += 4 # skipping sequence - return (txid, pos, scriptsig), i + txid, pos = unpack_outpoint(stream.read(36)) + scriptsig = load_string(stream) + stream.read(4) # skipping sequence + return (txid, pos, scriptsig) load_txins = vector_loader(load_txin) -def load_txout(v): +def load_txout(stream): # main.h CTxOut - i = 8 - value, = unpack_s64(v[:i]) - scriptpubkey, delta = load_string(v[i:]) - return (value, scriptpubkey), i+delta + value, = unpack_s64(stream.read(8)) + scriptpubkey = load_string(stream) + return (value, scriptpubkey) load_txouts = vector_loader(load_txout) -def load_transaction(v): +def load_transaction(stream): # main.h CTransaction - i = 4 # skipping version - txins, delta = load_txins(v[i:]) - i += delta - txouts, delta = load_txouts(v[i:]) - i += delta - i += 4 # skipping locktime - hash = sha256d(v[:i]) - return (hash, i, txins, txouts), i + start = stream.tell() + stream.read(4) # skipping version + txins = load_txins(stream) + txouts = load_txouts(stream) + stream.read(4) # skipping locktime + size = stream.tell() - start + stream.seek(start) + hash = sha256d(stream.read(size)) + return (hash, size, txins, txouts) load_transactions = vector_loader(load_transaction) -def load_block(v): +def load_block(stream): # main.h CBlock - i = 80 - head = v[:i] + head = stream.read(80) version, prev, root, time, target, nonce = unpack_header(head) hash = sha256d(head) - txs, delta = load_transactions(v[i:]) - return (hash, prev, time, target, txs), i+delta + txs = load_transactions(stream) + return (hash, prev, time, target, txs) def out_script_address(s): # Standard P2PKH script: OP_DUP OP_HASH160 20 ... OP_EQUALVERIFY OP_CHECKSIG @@ -454,9 +448,9 @@ ################################################## # Command implementations -def scan_block(height, v): +def scan_block(height, block_data): stdout.write('block %s' % height) - (blkhash, prev, time, target, txs), size = load_block(v) + blkhash, prev, time, target, txs = load_block(StringIO(block_data)) count_out = 0 n_tx = 0 @@ -524,7 +518,7 @@ last_blockcount = blockcount while height < blockcount: height += 1 - scan_block(height, memoryview(getblock(height))) + scan_block(height, getblock(height)) db.execute('UPDATE state SET scan_height = ?', (height,)) db.commit() diff -uNr a/gbw-node/manifest b/gbw-node/manifest --- a/gbw-node/manifest 076e5507ae3f63c412bd160b5b2eeb17cef8fc3bcb2dfd949220528f85aa08b7db585c223391de5cbf74eaf5241d3573789c0a5fbb14ca3803370c8c7756e318 +++ b/gbw-node/manifest e772e11336101afba7c6a85b4656d229e59d1fbdb2a3b484d902d0e74ae9721bd5a6e82375e332f7c046371486ba94034f6002d8ae4d1712fba92366edeba447 @@ -3,3 +3,4 @@ 783265 gbw-node_error_reporting jfw Fix undefined reference on reporting a bad address in cmd_tags. More generally, simplify arglist processing and fix the silent ignoring of excess CLI arguments, by having Python unpack the arglist automatically into named parameters of each command handler function. Report more context for address decoding errors. Clarify help text regarding input format for watch and push commands. 783269 gbw-node_error_tolerance jfw Don't die immediately on bad input lines. 783270 gbw-node_db_auto_init jfw Automate database initialization from schema script; update and expand related documentation. +783272 gbw-node_memoryview_replacement jfw Replace memoryview with cStringIO for loading block data, eliminating the main cause of Python 2.7 dependence, simplifying the code and even somewhat improving performance.