Basic getrawtransaction patch proposal for bitcoind

Filed under: Bitcoin, Software — Jacob Welsh @ 17:35

I present a bitcoind patch, of my own authorship, for consideration: a basic but functional getrawtransaction RPC command. I expect the need for it is clear: if it's your node then you shouldn't accept any sort of "I'm sorry Dave, I'm afraid I can't do that" especially regarding the data it already has handy.

To speak plainly about some deficits, this time not my own: the Real Bitcoin project presently rests on some shaky foundations. Despite apparently intricate efforts, a Keccak based vtree has seen little progress, in that the original patch signers besides mod6 have not signed on to the regrinds of their work, and some recommended patches have not been reground at all, updated for the very necessary manifest spec, or included in the mainline collection. Further, the sorry state of the inherited code such as magic numbers everywhere has seen little improvement. Perhaps I will have to take up some of these burdens in time; for now I'll leave it as an entreaty to the elder hands to please find a way to make it happen!

The patch

As in the original introduced somewhere around PRB 0.7.0, this command takes a transaction ID (256 bits of hex), searches first the node's own memory pool then the confirmed transaction database (blkindex.dat) and returns a hex dump of the encoded transaction. Unlike the original it does not support a "verbose" option to give a JSON representation of the data. This task seems to me better suited to an external tool, but I could see including it here if the implementation is concise and obviously correct.

Backporting the original was not possible due to the many intervening changes, though I did consult it to confirm I hadn't missed anything important and matched its numeric error code.

Based on the overall idea in the V version control system of building yggdrasil, I'm breaking from one of the project's prior naming conventions by including "bitcoin" but not the author in the patch name; the author is still recorded in the enclosed manifest file. Due to the problems noted earlier with the prior patch tree it's also not a proper vpatch yet.

Download: bitcoin_getrawtransaction.draft.patch.

To try this you will need to:

  1. Perform a press to asciilifeform_whogaveblox.vpatch;
  2. Manually apply mod6_phexdigit_fix.vpatch (which could be missed otherwise due to lacking a manifest entry);
  3. Manually apply the patch in question.

In detail

I added a manifest entry for the phexdigit fix, to make its inclusion explicit:

--- a/bitcoin/manifest.txt
+++ b/bitcoin/manifest.txt
@@ -28,3 +28,5 @@
 542413 asciilifeform_aggressive_pushgetblocks asciilifeform Issue PushGetBlocks command to any peer that issues 'version' command
 542413 mod6_excise_hash_truncation mod6 Regrind of ben_vulpes original; removes truncation of hashes printed to TRB log file
 543661 asciilifeform_whogaveblox asciilifeform Record the origin of every incoming candidate block (whether accepted or rejected)
+606789 mod6_phexdigit_fix mod6 Fix decoding LUT which wrongly accepted certain invalid characters as hex
+606789 bitcoin_getrawtransaction jfw Add RPC to get transactions from memory pool or database (hex only)

The RPC itself is about as simple as it gets in this codebase. First we try the mempool, as this should be fast and may contain unconfirmed transactions.(i)

--- a/bitcoin/src/bitcoinrpc.cpp
+++ b/bitcoin/src/bitcoinrpc.cpp
@@ -1351,7 +1351,31 @@
     return entry;

+Value getrawtransaction(const Array& params, bool fHelp)
+    if (fHelp || params.size() != 1)
+        throw runtime_error(
+            "getrawtransaction <txid>\n"
+            "Get hex serialization of <txid> from memory pool or database.");

+    uint256 hash;
+    map<uint256, CTransaction>::iterator it;
+    CTransaction tx;
+    CDataStream ssTx;
+    hash.SetHex(params[0].get_str());
+    it = mapTransactions.find(hash);
+    if (it != mapTransactions.end())
+        tx = it->second;

Functions everywhere have to open their own database connections - though some have the luxury of getting one passed in - which then implies a whole caching mechanism so as not to be horribly inefficient. Odin knows why there couldn't just be one global (or at least per-thread) "This Is The Database; Use It" object.

+    else {
+        CTxDB txdb("r");
+        if (!txdb.ReadDiskTx(hash, tx))
+            throw JSONRPCError(-5, "Transaction not found in memory pool or database.");
+    }
+    ssTx << tx;
+    return HexStr(ssTx.begin(), ssTx.end());
 Value backupwallet(const Array& params, bool fHelp)
     if (fHelp || params.size() != 1)

Wiring the function into the RPC dispatch table (I don't recall how I chose where to insert it, as the list was already non-alphabetical; probably based on where it seemed sensible in the help listing):

@@ -1865,6 +1889,7 @@
     make_pair("getreceivedbyaccount",   &getreceivedbyaccount),
     make_pair("listreceivedbyaddress",  &listreceivedbyaddress),
     make_pair("listreceivedbyaccount",  &listreceivedbyaccount),
+    make_pair("getrawtransaction",      &getrawtransaction),
     make_pair("backupwallet",           &backupwallet),
     make_pair("keypoolrefill",          &keypoolrefill),
     make_pair("walletpassphrase",       &walletpassphrase),

The mempool object now needs to be visible between compilation units. I suggest doing a grep to verify this introduces no name conflicts.

--- a/bitcoin/src/main.cpp
+++ b/bitcoin/src/main.cpp
@@ -26,7 +26,7 @@

 CCriticalSection cs_main;

-static map<uint256, CTransaction> mapTransactions;
+map<uint256, CTransaction> mapTransactions;
 CCriticalSection cs_mapTransactions;
 unsigned int nTransactionsUpdated = 0;
 map<COutPoint, CInPoint> mapNextTx;
--- a/bitcoin/src/main.h
+++ b/bitcoin/src/main.h
@@ -46,6 +46,7 @@

 extern CCriticalSection cs_main;
+extern std::map<uint256, CTransaction> mapTransactions;
 extern std::map<uint256, CBlockIndex*> mapBlockIndex;
 extern uint256 hashGenesisBlock;
 extern CBlockIndex* pindexGenesisBlock;

I tested that it builds, successfully fetches transactions from both mempool and database, and returns the expected errors for missing argument or transaction not found. It does accept invalid hex strings, perhaps a flaw in that SetHex method. I've been running the patch in production since around August 10th of this year.

  1. The original cause for my writing the patch was a stuck transaction that wasn't getting relayed to miners or block explorers for unknown reasons. Upon fishing the raw tx from the mempool and submitting it to one such site, a useful error was finally obtained identifying the problem as the S-value normalization mess; the -lows option provided a workaround, after double-spending to self for safety, which was a whole other pain. [^]


Keccak background

Filed under: Bitcoin, Historia, Software — Jacob Welsh @ 18:52

"Keccak" is a cryptographic hash function, or rather, some primitives for constructing such functions in a desired size and shape, of relatively recent design as these things go. It was brought to the attention of the forum in early 2016 in the context of contemplating changes to the Bitcoin protocol,(i) (ii) (iii) and subsequently differentiated from SHA3.(iv)

Compared to the prevailing standards at the time - mostly variants on the MD4 concept, processing blocks of input through an iterated compression function - Keccak is based on a large pseudorandom permutation (1600 bits, though the spec also defines smaller variants). As this is readily invertible, the desired "one-way" property is provided by a "sponge construction", mixing in blocks of input and extracting output while iterating the permutation and keeping some number of its bits secret as internal state. This number is called the capacity (or by complement the rate, the two summing to the permutation bit width) and can be tuned for the desired balance of security and computational intensity. The construction can take unlimited input, or produce unlimited output as a kind of stream cipher.(v)

I started out in 2017 playing with a C implementation found in the wild, supposedly a "readable and compact" version written by the original team. With some cleanup I got it into a state that could be described as compact, but I couldn't get very far in reading it, at least without having first digested the spec. And it had the unfortunate limitation of requiring the full input and output to exist in memory, no streaming. My confidence as an applied cryptographer was growing and I soon implemented a number of classical hash functions, but set Keccak aside as not being an immediate necessity. Meanwhile, Diana Coman produced and incrementally published a very nice and documented reference implementation in Ada, which was adopted for use in V and soon became non-optional.

While I was well convinced by the Republican rationale for Ada, I was much less keen on introducing GNAT, the flagship implementation, into my environment. It was a million-plus-line-of-code beast that I wouldn't stand a chance to ever really understand; making matters worse, it was a "Thompsonism", a circular dependency requiring existing binaries in order to build from source and thus dubiously "open source" at all. While I already depended on one such thing - the C compiler - I was hoping to somehow keep this to ONE thing, or at least ensure a way to work with the crucial V on existing machines without pulling all this in.

Stay tuned for the result.

  1. mircea_popescu: actually i wouldn't go to war over keccak.
    mircea_popescu: letting bitfury & friends eat 100mn in unrecoupable engineering costs would provide exactly the correct lesson as to what it's a good idea to say and when it's a good idea to shut the fuck up and toe the line.


  2. The necessary prerequisite for any change to the Bitcoin protocol [^]

  3. mircea_popescu: << at least it wasn;t fucking developed by teh nsa.
    assbot: Logged on 01-02-2016 19:29:18; ascii_butugychag: ;;later tell mircea_popescu in what sense is adoptinc keccak a rejection of usg standards? it was actually adopted as sha3...
    mircea_popescu: as far as we know. whatevs. minor point.
    ascii_butugychag: btw between that thread and now i went and read the keccak spec
    ascii_butugychag: it is mighty spiffy.
    ascii_butugychag: accordionizes to size.
    mircea_popescu: :)
    mircea_popescu: i don't need to explain what i meant by not finite then ?
    ascii_butugychag: aha.
    ascii_butugychag: other hashes also accept infinite bits but they eat where they shit.
    mircea_popescu: quite.
    mircea_popescu: and mind that while in no means do i propose this is "Asic resistant", from a designer perspective you must appreciate i'm giving you a fun job to do.
    mircea_popescu: at least therer's that.
    mircea_popescu: always make sure everyone's having fun.
    ascii_butugychag: quite! nobody will be plagiarizing old verilog from fpga docs to bake this one.
    ascii_butugychag: very asian-resistant.
    ascii_butugychag: which is a mega-plus.


  4. asciilifeform: holyshit the original keccak www is gone
    asciilifeform: replaced with some horrorshow
    asciilifeform: ada code -- gone
    asciilifeform: fortunately still on my hdd
    asciilifeform: check this out, now forwards to buncha tards
    asciilifeform: < original
    shinohai: Notice that happened after declared their spec
    asciilifeform: shinohai: not immediately , iirc was still intact last yr
    asciilifeform: incidentally shinohai keccak != usg.sha3
    asciilifeform: they adopted ~particular params~ of keccak as the new national whatever
    asciilifeform: orig is ~family~ of functions.
    asciilifeform: see also << since 'unhappened' article
    asciilifeform: ' The SHA-3 version of Keccak being proposed appears to provide essentially the same level of security guarantees as SHA-2, its predecessor. If we are going to develop a next generation hash, there certainly should be standardized versions that provide a higher security level than the older hash functions! NIST, in the original call for submissions, specifically asked for four versions in each submission, with at least two that would
    asciilifeform: be stronger than what was currently available, so it's hard to understand this post-competition weakening.'
    asciilifeform: didjaknow.
    asciilifeform: notice how 'everyone' nao thinks 'oh, keccak? that's called sha3 nao' [^]
  5. Since state is still finite, output will of course repeat eventually; one would hope this cycle length approaches that order of 21600. [^]


Gales Bitcoin Wallet spec and battle plan

Filed under: Bitcoin, Software — Jacob Welsh @ 04:51

This proposal will detail the software part of my wallet under development,(i) consisting of a security critical offline part and less sensitive online part.

All monetary values are input and displayed in decimal BTC but kept internally in exact integer (satoshi) form; conversion must be lossless.


Wallet data is stored in a directory tree containing a keys subdirectory and outputs, change, fee, and transactions files.

Private keys are represented in hex, with one key per file in the keys directory. (Probably little-endian; need to check existing conventions.)

Each key file is named by the Base58Check-encoded Bitcoin address corresponding to the key. (This implies a case-sensitive filesystem.) Support for compressed public keys may be added using an extra tag byte in the private key.(ii)

A list of outputs believed to be spendable is maintained in the tabular text file outputs, with records delimited by linefeeds and the following fields delimited by one or more space or tab characters:

  • Address (Base58Check)
  • Value (decimal BTC)
  • TXID (hash of transaction containing the output, in the conventional little-endian hex format)
  • Index (position in the transaction's output vector, decimal integer)
  • Comment (optional; may contain spaces and tabs)

Encryption is supported by keeping these in a tmpfs and persisting with tar+gpg or similar.

The offline wallet program provides the following commands:

  • gen-key - generate a new key file and print the resulting address to stdout. (Nice to have: numeric argument to generate many.)
  • send ADDR VAL ... - generate a signed transaction sending the given addresses the given amounts in BTC, pairwise. Inputs are selected from the outputs table in the order they appear. The change address is read from the change file; the fee in BTC per 1000 bytes is read from the fee file. The hex-encoded raw transaction is appended to the transactions file. (Nice to have: summarize the proposed transaction and prompt for confirmation.) The outputs file is overwritten to remove the spent ones and add the change and any others that pay the wallet's own addresses.

Once transferred, the transactions file can be removed or truncated at will.


The online part has a database that serves to index a lightweight subset of blockchain data pertinent to a single offline wallet. It tracks the following objects:

  • Watched addresses
  • Confirmed outputs funding them and inputs spending from them
  • Confirmed transaction metadata (hash, block hash+height+index, size, fee, comment)
  • Raw transactions submitted by the operator
  • Scan state

The online wallet program communicates with a Bitcoin node to populate its database and transmit new transactions. It provides sync commands:

  • scan - iterate blocks, filter for relevant transactions and update database.
  • reset - clear the scan pointer, e.g. to pick up past transactions affecting newly watched addresses. (Nice to have: argument to set to a given height)

Input/output commands:

  • unspent-outs - print unspent output table in the format used by the offline wallet. (Nice to have: other views like all outputs or only new outputs)
  • watch - import new addresses to watch from stdin, with optional comments.
  • push - import raw transaction(s) to push from stdin, with optional comments.

Accounting commands:

  • balance - print the sum of unspent output values.
  • register - print transaction history, format TBD.

Plan and time estimates

Write SQLite schema (currently drafted on paper): 1h
Import RPC client, block and transaction parsing code (I considered using python-bitcoinlib from Garzik et al. here; happily I have original implementations of the necessary parts): 1h
Block fetching ("dumpblock" from TRB to named pipe): 1h
Sync commands: 3h
I/O commands: 3h
Accounting commands (using something quick and dirty for "register" format for v1): 2h
Write manual: 3h
Total online part: 14h
Proposed deadline: Sunday, Dec 1.

Port transaction signing prototype to Scheme: 2h
Key file I/O: 1h
BTC numeric I/O: 1h
Outputs table I/O: 3h
Update "gen-key" command for new spec: 1h
"send" command: 3h
Sample tar+gpg wallet open and close scripts: 1h
Design and implement tests: 5h
Write manual: 3h
Total offline part: 20h
Proposed deadline: Thursday, Dec 12.

"sendrawtransaction" RPC for TRB: 5h

Total: 39h
Deadline: Friday, Dec 13.

Now: I see 40 available hours remaining in my schedule for this time interval. Given that I'm far from confident in my estimation abilities here and there's always something unexpected, this is worrying. Perhaps the documentation would be a light enough job at this point to handle on the plane. Perhaps there's a "sendrawtransaction" patch floating around somewhere. Perhaps someone can be motivated to lend a hand - this would be the most separable task of the pile.

  1. Through further consideration of the coding tasks and data dependencies, in particular mapping out the online part as a relational database, I've realized my earlier notion of transactions as the central data structure had introduced unnecessary complications. If accounting of historical transactions is left to the online database, the offline signing part need only be aware of currently unspent outputs, which reduces its storage needs to a flat table, no S-expressions or JSON or similar. I believe this also brings it in closer alignment with the ideal. A second simplification is in the storage of addresses and keys, following a philosophy of "the filesystem is a database; use it", and naming of keys has been dropped. [^]
  2. This may be desirable to import keys or sweep funds from legacy wallets without having to execute their code. If used by default it would reduce outbound transaction size, thus perhaps fees, though as ever miner behavior is uncertain. [^]


Next steps in wallet planning

Filed under: Bitcoin, Software — Jacob Welsh @ 18:40

The planning in my last article left a fair amount to the imagination, and in particular several pending decisions without sufficient detail to move discussion forward. The picture coming into view is that through a habit of insufficiently structured communications between engineering and management, I've been attempting to bear more weight of decision-making than is my due, trying to optimize things from the technical point of view which, while not unimportant, does not always align with the needs of a business to survive and grow in a dynamic market. I'll also take the opportunity to encourage present or future management to lean on me for support should it happen that I wander off into my own world.

With that in mind, let's expand a bit on the remaining tasks and decisions.

Switching to the now-builtin bignum support

I noted this as something that could potentially be deferred due to time constraints, because it does work as is. Unfortunately upon testing I've found that performance is noticeably degraded from what it used to be, at 14 seconds to derive an address from a private key on my dev system where I recall this having been closer to 5 seconds on earlier versions of the interpreter. (Transaction signing would be several times this.) I suspect a big part of the slowdown is the now-redundant layers of type and overflow checking; I'm unsure how to confirm this other than just trying the change. FWIW, I don't anticipate it taking all that long; maybe I take an hour and see if I can get it done.

Unspent output accounting; import of inbound or previously unknown outbound payments

The wallet tracks three types of data: private keys, the public keys and addresses derived from them, and transactions affecting its addresses. Queries such as balance and unspent outputs can be resolved by scanning the list of transactions, or tracked separately. As I see it, the "full scan" will have to work anyway so separate tracking, while desirable from an algorithmic scalability standpoint, would be a premature optimization at this stage. Without any such derived data to maintain, the "import" operation can simply mean appending lines containing encoded transaction data to a text file. Which brings us to:

External representation of transaction data (S-expression or maybe JSON)

I'm presently thinking S-exprs are the way to go; it keeps things simple on the Scheme side, and the Python side would only need to emit them, not parse them. What I'm not in a position to see is whether interoperability with other JSON-based tools would be desirable. I do have a parser in Scheme for a simplified form of JSON - omitting some Unicode details - that would suffice here.

Transaction input selection (menu-based?)

A possible cut here is to skip any sort of interface and use a simple selection strategy like FIFO. A manual interface could be added later. The idea here was to print a numbered listing of outputs by address/age/value and prompt for which ones to spend until sufficient value is accumulated.

ECDSA S-value normalization?

Turns out I've already got this: "As (r, -s mod n) is also a valid signature, the result is canonicalized to use the lesser of the two possible s-values." Thanks, 2017-me! (Some background.)

Fetching blocks from TRB (new RPC? Bitcoin protocol?)

In fact this could be done using the existing "dumpblock", perhaps using a named pipe. An RPC to return a block directly still seems preferable to me, but patching TRB isn't free. The "Bitcoin protocol" angle here meant connecting to port 8333 and using the p2p protocol commands to fetch a block, which is probably overkill if this is to be used in a batched or "pull" style.

Pushing raw transactions to TRB (likewise)

This one I don't think can be avoided.

Tracking watched addresses and synchronization state (SQL?)

The simple approach here, if indeed we're going for a batch model, would be a text file listing addresses and another that gives the last scanned block number/hash. When you run a scan, new transactions affecting the given addresses are emitted to a text file for transfer offline, and the last-scanned-block file is rewritten. If whatever data gets lost, you just rescan from an older block. Dealing with reorgs is where it could all get messier; one approach could be to insist on, say, 6 confirmations, and do a full rescan in the rare event of a deeper fork. How exactly one detects this and what could happen if one doesn't seems to warrant further consideration.


Gales Bitcoin Wallet: status, preliminary work plan and code dump

Filed under: Bitcoin, Software — Jacob Welsh @ 21:01

The item in question is the software component of a combination of hardware, software and operational practices with the goal of sending and receiving Bitcoin while excluding the possibility of remote compromise of keys (so long as underlying cryptographic assumptions hold). Proper airgapping is the foundation, but it's easier said than done when it comes to Bitcoin because of the dependency on data from the outside world for the inputs of a transaction. Popular "cold storage" solutions may involve storing keys offline, but have windows of vulnerability when the keys are generated and funds are swept using an online (i.e. insecure) machine.

Our approach is to allow data to jump the gap as selectively as possible, using a simple and low-speed channel (serial port) with optical isolation and manually switched simplex operation (one way at a time). The system is intended for manual, low-frequency usage; thus we're not especially concerned with performance or timing side channels.(i) At the same time, we're very concerned with correctness, which informs a preference for straightforward "textbook" implementations when possible, minimizing external dependencies and overall system complexity, maximizing auditability (e.g. avoiding any sort of processor-specific "crypto acceleration"), and publishing code.(ii)

Design points include:

  • An offline machine, running the offline component of the wallet. The machine is to have no swap, be operated on battery power during key operations, and powered off after use.
  • A set of private keys, generated offline by explicit request and associated with names of the operator's choice, stored in a tmpfs during operation and GPG-encrypted on disk using a strong password when at rest.
  • A true (i.e. hardware) random number generator, used for all key generation including the ephemeral keys used in ECDSA signing.
  • Transactions constructed offline (potentially reducing round trips through the airgap).
  • An online machine running a TRB node and the online component of the wallet, which pushes outbound transactions, scans for sufficiently-confirmed new transactions affecting the set of watched addresses, and produces minimal data structures for updating state of the offline component.


I've used my own Scheme system for implementation of the offline component so far, after an earlier prototype in the much larger (albeit more battle-tested) Python. Much of the code falls into one of two categories: math, or shuffling data from one format to another.

On the first category, I've implemented the following algorithms, with reference primarily to Menezes, van Oorschot and Vanstone and the SECG standards.

  • Bignum arithmetic: addition/subtraction, multiplication, division/remainder. My first pass here was intended more as an exercise, a "userspace" (pure Scheme) implementation supporting only unsigned values. I subsequently redid this, with signed integers and plugging into the generic arithmetic operators, still mostly in Scheme. The wallet code is presently still using the former. This is a conventional arbitrary-precision framework and thus does not support constant-time operations.
  • Modular inversion (extended Euclidean GCD).
  • Unbiased random integer in a given range.
  • Elliptic curve point addition and scalar multiplication, for the secp256k1 curve required by Bitcoin. Scalar multiplication is the most intensive operation, done once for key generation and several times for signing, and comes out noticeably slow; one optimization was to precompute iterated doublings of the generator point.
  • ECDSA signing.
  • SHA256 and RIPEMD160 hash functions.

On the second, the following, with reference primarily to the TRB and OpenSSL code (and sometimes the Bitcoin Wiki which was almost always a mistake):

  • Base58 and Base58Check
  • Little-endian hex and octet encoding
  • Fixed and variable-length integer byte encodings as seen in Bitcoin
  • The OpenSSL EC point (public key) encoding
  • The subset of ASN.1 DER as used by OpenSSL for ECDSA signatures
  • Bitcoin script, with code to construct p2pkh and p2sh outputs(iii)

These are presently wrapped up into commands by which one can generate private key files, the associated addresses, and (in case of emergency) export private keys to the "WIF" format for import to other wallet software.

The aforementioned Python prototype also implements the somewhat convoluted Bitcoin transaction signing algorithm, and I have Python code for deserializing blocks and transactions.

Remaining tasks

This list will surely need some revision upon contact with reality. The practical deadline so as not to wreck my own holiday is December 13.

For the offline component:

  • Porting transaction signing to Scheme
  • External representation of transaction data (S-expression or maybe JSON)
  • Switching to the now-builtin bignum support
  • Unspent output accounting; import of inbound or previously unknown outbound payments
  • Transaction input selection (menu-based?)
  • ECDSA S-value normalization?
  • Frontend commands
  • Shell scripts for "opening/closing" the wallet (GPG operations)
  • More tests

The online component, for which I'll probably stick to Python and make use of available libraries:

  • Fetching blocks from TRB (new RPC? Bitcoin protocol?)
  • Pushing raw transactions to TRB (likewise)
  • Tracking watched addresses and synchronization state (SQL?)
  • Export of new payment data for transfer offline
  • Alert for unexpected outbound payments

Partial code dump

Python prototype:

Offline component, Scheme code:

  • pkg.scm - basic import/export mechanism used by the remaining
  • bignum.scm - Unsigned "userspace" bignum library
  • ecdsa.scm
  • bit-ops.scm - some rather sad complexity for implementing fixed-width arithmetic and bitwise operations, which in hindsight would have been better done as interpreter extensions
  • hashes.scm - the RIPEMD and SHA families, based on the above
  • wallet.scm
  1. Certainly it would be preferable to eliminate these. [^]
  2. At least when it comes to cryptography, you don't gain much from obscuring your algorithms or implementation, because chances are you're not as clever as you think: all the ways to mess it up are already known to someone and will eventually be probed. [^]
  3. The latter might be a contentious point; it does not involve any support for "multisig", "segwit" or similar on my side, but enables one to transact with those who insist on these Gavinistic 3-addresses. [^]

Powered by MP-WP. Copyright Jacob Welsh.