This adds support for timelocked contracts which can be used to atomically swap hash preimages over the blockchain. It is very similar to the script used by the lightning network.
Written in collaboration with Pieter Wuille.
This adds support for timelocked contracts which can be used to atomically swap hash preimages over the blockchain. It is very similar to the script used by the lightning network.
Written in collaboration with Pieter Wuille.
Interesting!
Haven't looked at it in detail, but would this affect "policy" by adding the TX_HTLC as to the standard templates?
@jonasschnelli All P2SH is already standard.
@sipa: Ah. Right. Then – I guess – the standard.cpp -> Solver() changes are only because we want to detect these types of P2SH scripts in the wallet.
@jonasschnelli To be clear: this indeed does make the HTLC type transaction standard in non-P2SH setting; it's however still smaller and cheaper than the largest raw multisig that is allowed. Perhaps that indeed shouldn't happen.
Concept ACK.
concept ACK
Interestingly, this is useful for Paypub, as well as ZKCP, among other things.
Concept ACK
@sipa For Paypub, having the payments be visible in the blockchain for the receiver is extremely useful; w/o the ability to do so in scriptPubKey you'd likely implement it with an extra OP_RETURN output (maybe until we have a better Bitmessage to use instead). Not necessarily an argument either for or against doing so, but worth noting.
Concept ACK
Some tests fail with:
Running testscript wallet.py ...
Initializing test directory /tmp/testr_T8PJ
start_node: bitcoind started, calling bitcoin-cli -rpcwait getblockcount
start_node: calling bitcoin-cli -rpcwait getblockcount returned
JSONRPC error: preimage must be hexadecimal string (not '')
I can't reproduce locally 8)
Concept ACK
Needs some tests. Needs BIP for new OP_ANYDATA.
Concept ACK.
1.) Not sure if this requires a BIP, I guess we don't need a BIP for every contract template and I can't find the other template matching params (like OP_SMALLINTEGER) in the BIPs.
I also concept ACK the IsStandard rule for HTLC to allow blockchain-visible HTLC, though, this might require a BIP.
OP_ANYDATA is just local wallet logic, it uses a range of script opcodes that are disabled in execution, and is only used inside the matching templates (I think it's a bad idea to mix them in the same CScript type...), so I disagree with needing a BIP for that.
IsStandard changes so far have also been done without a BIP, but maybe we can postpone making raw HTLC standard for now.
Certainly agree we need tests.
I agree IsStandard changes do not need a BIP, but it seems any transaction type supported by the reference implementation necessarily involves coordination with other software/people, and therefore should have a BIP written describing how it works.
Is there a benefit to bare HTLC over P2SH (or SegWit) HTLC? If not, it seems pointless to expand the policy to allow for it. Note that many users do not enable bare multisig (and it should probably be disabled by default).
I don't think initially we should make this standard outside P2SH (yet), so that's definitely a change that should be made to this PR.
This work so far allows you to "import" a preimage into CWallet (so that you can redeem the atomic swap branch of the script) but it does not persist this to disk, and I'm curious if others think it should. (I'm skeptical of making bitcoind store secrets, especially if for some use cases they are ephemeral.)
As an example of what this was written to enable: https://bitcoincore.org/en/2016/02/26/zero-knowledge-contingent-payments-announcement/
Wow, concept ACK.
We should probably also wait until CSV is in, and add support for it in the constructed scripts here.
838 | @@ -839,3 +839,4 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp) 839 | 840 | return hashTx.GetHex(); 841 | } 842 | +
This new line?
Additionally, supporting RIPEMD160 for ZKCPs is a must. I will try to find some time to write an arithmetic circuit for it.
After segwit lands (?) I will try to rebase and clean this PR up.
CSV is active on mainnet!
Rebased this on to master. Now supports RIPEMD160. Switched to CSV (block denominated mode). Added some RPC tests.
It can be trivially extended to use CLTV in the RPC, I just need to come up with a solid UX for it. The same goes for "relative" time strings like "10h" or "5d." Also, still need to bring over the "preimage" display that showed in listtransactions.
I'll try to work alongside #7534 for the remaining work, including submitting a BIP.
62 | + const opcodetype accepted_timeout_ops[] = {OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY}; 63 | + 64 | + BOOST_FOREACH(opcodetype hasher, accepted_hashers) { 65 | + BOOST_FOREACH(opcodetype timeout_op, accepted_timeout_ops) { 66 | + mTemplates.insert(make_pair(TX_HTLC, CScript() 67 | + << hasher << OP_ANYDATA << OP_EQUAL
In the "ELSE" case any relay node may replace the top stack with anything without invalidating the tx. See related discussion at https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2016-August/013036.html
Needs rebase
Still needs rebase.
Before I rebase, I need to understand the consensus regarding preventing malleability of these scripts.
https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2016-August/013036.html
Multiple suggestions were made and I'm not personally qualified to pick from them.
@ebfull: I'd suggest this one. But you may want to discuss it on mailing list to make sure it is compatible with other implementations (if any)
OP_IF [HASHOP] <digest> OP_EQUALVERIFY <seller pubkey> OP_ELSE <num> [TIMEOUTOP] OP_DROP <buyer pubkey> OP_ENDIF OP_CHECKSIG
45 | @@ -46,6 +46,8 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool w 46 | return false; 47 | if (m < 1 || m > n) 48 | return false; 49 | + } else if (whichType == TX_HTLC) { 50 | + return false;
It looks like you're assuming all TX_HTLC transactions are DOS attacks? Why reject them all?
@arielgabizon Would this actually reject all TX_HTLC items? I had the impression or understand that there was a design such that these would actually be completed in most instances. In lnd, a description of this was given as follows: "If payment/htlc amount is too small, than such output is called dust. If for some reason channel have been force closed during payment, and dust htlc/payment haven't been settled (both sides haven't agreed to remove the htlc and change the balances accordingly), th(e)n we (do) not include it in commitment transaction (thereby giv(ing it) as a fee to miners) in order to make it valid." For reference to this comment (from a contributor in lnd) see: https://github.com/lightningnetwork/lnd/pull/115#issuecomment-305739226
With that said, I'm not sure of how the differences will play out between how bitcoin core is planning on handling it vs. how it is handled presently in the lnd development process.
cc @ebfull
107 | @@ -108,6 +108,30 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP 108 | ret.push_back(valtype()); // workaround CHECKMULTISIG bug 109 | return (SignN(vSolutions, creator, scriptPubKey, ret, sigversion)); 110 | 111 | + case TX_HTLC: 112 | + { 113 | + std::vector<unsigned char> image(vSolutions[0]); 114 | + std::vector<unsigned char> preimage;
can reserve preimage.
124 | @@ -125,6 +125,19 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& 125 | break; 126 | } 127 | 128 | + case TX_HTLC: 129 | + { 130 | + // Only consider HTLC's "mine" if we own ALL the keys 131 | + // involved. 132 | + vector<valtype> keys;
Please reserve keys.
63 | + make_pair(OP_SHA256, OP_BLOB32), 64 | + make_pair(OP_RIPEMD160, OP_BLOB20) 65 | + }; 66 | + const opcodetype accepted_timeout_ops[] = {OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY}; 67 | + 68 | + BOOST_FOREACH(auto hasher, accepted_hashers) {
Please don't introduce any new BOOST_FOREACH
178 | @@ -179,6 +179,10 @@ enum opcodetype 179 | 180 | 181 | // template matching params 182 | + OP_BLOB20 = 0xf7,
Prefer to not make any new template matching parameters, but I see how it would be inconvenient to otherwise.
380 | + uint32_t timeout, 381 | + opcodetype hasher_type, 382 | + opcodetype timeout_type) 383 | +{ 384 | + CScript script; 385 | +
Check that hasher_type and image.size() match?
345 | + "It returns a json object with the address and redeemScript.\n" 346 | + 347 | + "\nArguments:\n" 348 | + "1. seller_key (string, required) The public key of the possessor of the preimage.\n" 349 | + "2. refund_key (string, required) The public key of the recipient of the refund.\n" 350 | + "3. hash (string, required) SHA256 or RIPEMD160 hash of the preimage.\n"
Maybe hash should be optional, and if not supplied a GetRandHash preimage should be selected and returned to the caller.
114 | @@ -102,6 +115,16 @@ class CBasicKeyStore : public CKeyStore 115 | virtual bool HaveCScript(const CScriptID &hash) const; 116 | virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const; 117 | 118 | + bool GetPreimage( 119 | + const std::vector<unsigned char>& image, 120 | + std::vector<unsigned char>& preimage 121 | + ) const; 122 | + 123 | + bool AddPreimage(
I think that you should persist the PreimageMap to disk.
122 | + 123 | + bool AddPreimage( 124 | + const std::vector<unsigned char>& image, 125 | + const std::vector<unsigned char>& preimage 126 | + ); 127 | +
Maybe add a RemovePreimage, LockPreimage, and UnlockPreimage call, which could be used to ensure that a node no longer has the ability to solve a particular HTLC or does not until explicitly allowed. These should also go to disk.
646 | + return NullUniValue; 647 | + 648 | + if (request.fHelp || request.params.size() != 1) 649 | + throw runtime_error( 650 | + "importpreimage \"data\"\n" 651 | + "\nImports a preimage for use in HTLC transactions. Only remains in memory.\n"
If you add persistence, be sure to update here.
124 | @@ -125,6 +125,19 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& 125 | break; 126 | } 127 | 128 | + case TX_HTLC: 129 | + { 130 | + // Only consider HTLC's "mine" if we own ALL the keys 131 | + // involved. 132 | + vector<valtype> keys; 133 | + keys.push_back(vSolutions[1]);
Maybe just for safety, assert(vSolutions.size() > 3)
Concept Ack.
I did some code review, overall looks very clean and concise.
I was slightly concerned with the use of new template parameters, but I think that the proposed new ones are reasonable & general purpose.
I think that the preimage map should persist entries; but perhaps that can be done as a later PR. I would also suggest adding a Lock/Unlock feature in RPC, because I think that that would ensure safer use of entries stored in the preimage map for users.
Thanks for the reviews @arielg and @JeremyRubin. I intend to rebase this again and clean it up.
Also, BIP199 has been submitted to start standardizing what is in this PR.
@ebfull Thanks for mentioning... that was my question, regarding BIP 199, when reading this thread.
A few questions actually -
This still alive?
Unfortunately I'm too busy to carry the torch on this PR. :(
I'll close this then. If someone wants to restart/pickup this work they can cherry pick/reference and open a new PR.
28 | + 29 | + def activateCSV(self): 30 | + # activation should happen at block height 432 (3 periods) 31 | + min_activation_height = 432 32 | + height = self.nodes[0].getblockcount() 33 | + assert(height < 432)
can we use assert(height < min_activation_height) here instead?
I think we can.