Skip to content

Commit

Permalink
Add spv_refundhtlcall (#686)
Browse files Browse the repository at this point in the history
* Add spv_refundhtlcall

* sendmessage only populated on failure

* sendmessage only populated on error

* Set block heights in test to meet refund requirement

* Fix lints

* Update help test

Co-authored-by: Prasanna Loganathar <[email protected]>
  • Loading branch information
Bushstar and prasannavl authored Oct 22, 2021
1 parent 724ce59 commit 20b2549
Show file tree
Hide file tree
Showing 9 changed files with 244 additions and 107 deletions.
1 change: 1 addition & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ static const CRPCConvertParam vRPCConvertParams[] =

{ "spv_claimhtlc", 3, "feerate" },
{ "spv_refundhtlc", 2, "feerate" },
{ "spv_refundhtlcall", 1, "feerate" },
{ "decodecustomtx", 1, "iswitness" },

{ "setmockcheckpoint", 0, "height" },
Expand Down
6 changes: 3 additions & 3 deletions src/spv/bitcoin/BRTransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,7 @@ int BRTransactionIsSigned(const BRTransaction *tx)
// adds signatures to any inputs with NULL signatures that can be signed with any keys
// forkId is 0 for bitcoin, 0x40 for b-cash, 0x4f for b-gold
// returns true if tx is signed
int BRTransactionSign(BRTransaction *tx, int forkId, BRKey keys[], size_t keysCount, HTLCScriptType htlcType, const uint8_t* seed, const uint8_t *redeemScript)
int BRTransactionSign(BRTransaction *tx, int forkId, BRKey keys[], size_t keysCount, HTLCScriptType htlcType, const uint8_t* seed)
{
UInt160 pkh[keysCount];
size_t i, j;
Expand Down Expand Up @@ -694,7 +694,7 @@ int BRTransactionSign(BRTransaction *tx, int forkId, BRKey keys[], size_t keysCo
size_t elemsCount = BRScriptElements(elems, sizeof(elems)/sizeof(*elems), input->script, input->scriptLen);
uint8_t pubKey[BRKeyPubKey(&keys[j], NULL, 0)];
size_t pkLen = BRKeyPubKey(&keys[j], pubKey, sizeof(pubKey));
uint8_t sig[73], script[1 + sizeof(sig) + 1 + (seed ? seed[0] + (htlcType == ScriptTypeSeller ? 1 /* OP_1 */ : 0) + 1 /* pushdata */ + redeemScript[0]: sizeof(pubKey))];
uint8_t sig[73], script[1 + sizeof(sig) + 1 + (seed ? seed[0] + (htlcType == ScriptTypeSeller ? 1 /* OP_1 */ : 0) + 1 /* pushdata */ + input->scriptLen /* length */ + input->script[0] : sizeof(pubKey))];
size_t sigLen, scriptLen;
UInt256 md = UINT256_ZERO;

Expand Down Expand Up @@ -747,7 +747,7 @@ int BRTransactionSign(BRTransaction *tx, int forkId, BRKey keys[], size_t keysCo
}

// Add redeemscript
scriptLen += BRScriptPushData(&script[scriptLen], sizeof(script) - scriptLen, &redeemScript[1], redeemScript[0]);
scriptLen += BRScriptPushData(&script[scriptLen], sizeof(script) - scriptLen, input->script, input->scriptLen);

BRTxInputSetSignature(input, script, scriptLen);
BRTxInputSetWitness(input, script, 0);
Expand Down
2 changes: 1 addition & 1 deletion src/spv/bitcoin/BRTransaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ int BRTransactionIsSigned(const BRTransaction *tx);
// adds signatures to any inputs with NULL signatures that can be signed with any keys
// forkId is 0 for bitcoin, 0x40 for b-cash, 0x4f for b-gold
// returns true if tx is signed
int BRTransactionSign(BRTransaction *tx, int forkId, BRKey keys[], size_t keysCount, HTLCScriptType htlcType = ScriptTypeNone, const uint8_t* seed = nullptr, const uint8_t* redeemScript = nullptr);
int BRTransactionSign(BRTransaction *tx, int forkId, BRKey keys[], size_t keysCount, HTLCScriptType htlcType = ScriptTypeNone, const uint8_t* seed = nullptr);

// true if tx meets IsStandard() rules: https://bitcoin.org/en/developer-guide#standard-transactions
int BRTransactionIsStandard(const BRTransaction *tx);
Expand Down
14 changes: 8 additions & 6 deletions src/spv/bitcoin/BRWallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,12 +230,12 @@ static int _BRWalletContainsUserTx(BRWallet *wallet, const BRTransaction *tx, co
return r;
}

static bool _BRWalletContainsHTLCOutput(BRWallet *wallet, const BRTransaction *tx, size_t &output, const UInt160& addressFilter)
static bool _BRWalletContainsHTLCOutput(BRWallet *wallet, const BRTransaction *tx, std::vector<size_t> &output, const UInt160& addressFilter)
{
bool r = false;
const uint8_t *pkh;

for (size_t i = 0; ! r && i < tx->outCount; i++)
for (size_t i = 0; i < tx->outCount; i++)
{
pkh = BRScriptPKH(tx->outputs[i].script, tx->outputs[i].scriptLen);
if (pkh)
Expand All @@ -250,7 +250,7 @@ static bool _BRWalletContainsHTLCOutput(BRWallet *wallet, const BRTransaction *t
continue;
}

output = i;
output.push_back(i);
r = true;
}
}
Expand Down Expand Up @@ -345,15 +345,17 @@ std::vector<std::pair<BRTransaction*, size_t>> BRListHTLCReceived(BRWallet *wall
{
std::vector<std::pair<BRTransaction*, size_t>> htlcTransactions;
BRTransaction *tx;
size_t output{0};

for (size_t i = 0; i < array_count(wallet->transactions); ++i)
{
std::vector<size_t> outputs;
tx = wallet->transactions[i];

if (_BRWalletContainsHTLCOutput(wallet, tx, output, addr))
if (_BRWalletContainsHTLCOutput(wallet, tx, outputs, addr))
{
htlcTransactions.emplace_back(tx, output);
for (const auto& out : outputs) {
htlcTransactions.emplace_back(tx, out);
}
}
}

Expand Down
57 changes: 52 additions & 5 deletions src/spv/spv_rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ UniValue spv_createanchor(const JSONRPCRequest& request)
result.pushKV("cost", cost);
if (send) {
result.pushKV("sendResult", sendResult);
result.pushKV("sendMessage", DecodeSendResult(sendResult));
result.pushKV("sendMessage", sendResult != 0 ? DecodeSendResult(sendResult) : "");
}

return result;
Expand Down Expand Up @@ -1047,7 +1047,7 @@ UniValue spv_claimhtlc(const JSONRPCRequest& request)
RPCResult{
"{\n"
" \"txid\" (string) The transaction id\n"
" \"sendmessage\" (string) Send message result\n"
" \"sendmessage\" (string) Error message on failure\n"
"}\n"
},
RPCExamples{
Expand All @@ -1066,8 +1066,13 @@ UniValue spv_claimhtlc(const JSONRPCRequest& request)
throw JSONRPCError(RPC_MISC_ERROR, "spv not connected");
}

return spv::pspv->CreateHTLCTransaction(pwallet, request.params[0].get_str().c_str(), request.params[1].get_str().c_str(),
const auto pair = spv::pspv->PrepareHTLCTransaction(pwallet, request.params[0].get_str().c_str(), request.params[1].get_str().c_str(),
request.params[2].get_str(), request.params[3].isNull() ? spv::DEFAULT_BTC_FEE_PER_KB : request.params[3].get_int64(), true);

UniValue result(UniValue::VOBJ);
result.pushKV("txid", pair.first);
result.pushKV("sendmessage", pair.second);
return result;
}

UniValue spv_refundhtlc(const JSONRPCRequest& request)
Expand All @@ -1084,7 +1089,7 @@ UniValue spv_refundhtlc(const JSONRPCRequest& request)
RPCResult{
"{\n"
" \"txid\" (string) The transaction id\n"
" \"sendmessage\" (string) Send message result\n"
" \"sendmessage\" (string) Error message on failure\n"
"}\n"
},
RPCExamples{
Expand All @@ -1103,8 +1108,49 @@ UniValue spv_refundhtlc(const JSONRPCRequest& request)
throw JSONRPCError(RPC_MISC_ERROR, "spv not connected");
}

return spv::pspv->CreateHTLCTransaction(pwallet, request.params[0].get_str().c_str(), request.params[1].get_str().c_str(),
const auto pair = spv::pspv->PrepareHTLCTransaction(pwallet, request.params[0].get_str().c_str(), request.params[1].get_str().c_str(),
"", request.params[3].isNull() ? spv::DEFAULT_BTC_FEE_PER_KB : request.params[3].get_int64(), false);

UniValue result(UniValue::VOBJ);
result.pushKV("txid", pair.first);
result.pushKV("sendmessage", pair.second);
return result;
}

UniValue spv_refundhtlcall(const JSONRPCRequest& request)
{
CWallet* const pwallet = GetWallet(request);

RPCHelpMan{"spv_refundhtlcall",
"\nGets all HTLC contracts stored in wallet and creates refunds transactions for all that have expired\n",
{
{"destinationaddress", RPCArg::Type::STR, RPCArg::Optional::NO, "Destination for funds in the HTLC"},
{"feerate", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "Feerate (satoshis) per KB (Default: " + std::to_string(spv::DEFAULT_BTC_FEE_PER_KB) + ")"},
},
RPCResult{
"{\n"
" \"txid\" (string) The transaction id\n"
"}\n"
},
RPCExamples{
HelpExampleCli("spv_refundhtlcall", "100000")
+ HelpExampleRpc("spv_refundhtlcall", "100000")
},
}.Check(request);

if (!spv::pspv)
{
throw JSONRPCError(RPC_INVALID_REQUEST, "spv module disabled");
}

if (!spv::pspv->IsConnected())
{
throw JSONRPCError(RPC_MISC_ERROR, "spv not connected");
}

const auto feeRate = request.params[1].isNull() ? spv::DEFAULT_BTC_FEE_PER_KB : request.params[1].get_int64();

return spv::pspv->RefundAllHTLC(pwallet, request.params[0].get_str().c_str(), feeRate);
}

UniValue spv_fundaddress(const JSONRPCRequest& request)
Expand Down Expand Up @@ -1546,6 +1592,7 @@ static const CRPCCommand commands[] =
{ "spv", "spv_createhtlc", &spv_createhtlc, { "seller_key", "refund_key", "hash", "timeout" } },
{ "spv", "spv_claimhtlc", &spv_claimhtlc, { "scriptaddress", "destinationaddress", "seed", "feerate" } },
{ "spv", "spv_refundhtlc", &spv_refundhtlc, { "scriptaddress", "destinationaddress", "feerate" } },
{ "spv", "spv_refundhtlcall", &spv_refundhtlcall, { "destinationaddress", "feerate" } },
{ "spv", "spv_listhtlcoutputs", &spv_listhtlcoutputs, { "address" } },
{ "spv", "spv_decodehtlcscript", &spv_decodehtlcscript, { "redeemscript" } },
{ "spv", "spv_gethtlcseed", &spv_gethtlcseed, { "address" } },
Expand Down
Loading

0 comments on commit 20b2549

Please sign in to comment.