Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SPV Bitcoin HTLC #308

Merged
merged 3 commits into from
Apr 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/masternodes/mn_rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.

#include <masternodes/mn_rpc.h>

#include <base58.h>
#include <policy/settings.h>

extern bool EnsureWalletIsAvailable(bool avoidException); // in rpcwallet.cpp
Expand Down Expand Up @@ -378,6 +380,39 @@ CWallet* GetWallet(const JSONRPCRequest& request) {
return pwallet;
}

CPubKey PublickeyFromString(const std::string &pubkey)
{
if (!IsHex(pubkey) || (pubkey.length() != 66 && pubkey.length() != 130))
{
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid public key: " + pubkey);
}

return HexToPubKey(pubkey);
}

CScript CreateScriptForHTLC(const JSONRPCRequest& request, uint32_t& blocks, std::vector<unsigned char>& image)
{
CPubKey seller_key = PublickeyFromString(request.params[0].get_str());
CPubKey refund_key = PublickeyFromString(request.params[1].get_str());

{
UniValue timeout;
if (!timeout.read(std::string("[") + request.params[2].get_str() + std::string("]")) || !timeout.isArray() || timeout.size() != 1)
{
throw JSONRPCError(RPC_TYPE_ERROR, "Error parsing JSON: " + request.params[3].get_str());
}

blocks = timeout[0].get_int();
}

if (blocks >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG)
{
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid block denominated relative timeout");
}

return GetScriptForHTLC(seller_key, refund_key, image, blocks);
}

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

Expand Down
1 change: 1 addition & 0 deletions src/masternodes/mn_rpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,6 @@ std::vector<CTxIn> GetAuthInputsSmart(CWallet* const pwallet, int32_t txVersion,
std::string ScriptToString(CScript const& script);
CAccounts GetAllMineAccounts(CWallet* const pwallet);
CAccounts SelectAccountsByTargetBalances(const CAccounts& accounts, const CBalances& targetBalances, AccountSelectionMode selectionMode);
CScript CreateScriptForHTLC(const JSONRPCRequest& request, uint32_t &blocks, std::vector<unsigned char>& image);

#endif // DEFI_MASTERNODES_MN_RPC_H
3 changes: 1 addition & 2 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,14 +220,13 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "spv_estimateanchorcost", 0, "feerate" },
{ "spv_rescan", 0, "height" },
{ "spv_gettxconfirmations", 0, "txhash" },
{ "spv_splitutxo", 0, "parts" },
{ "spv_splitutxo", 1, "amount" },
{ "spv_setlastheight", 0, "height" },
{ "spv_listanchors", 0, "minBtcHeight" },
{ "spv_listanchors", 1, "maxBtcHeight" },
{ "spv_listanchors", 2, "minConfs" },
{ "spv_listanchors", 3, "maxConfs" },
{ "spv_sendtoaddress", 1, "amount" },
{ "spv_sendtoaddress", 2, "feerate" },

{ "createpoolpair", 0, "metadata" },
{ "createpoolpair", 1, "inputs" },
Expand Down
24 changes: 24 additions & 0 deletions src/script/standard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,30 @@ CScript GetScriptForWitness(const CScript& redeemscript)
return GetScriptForDestination(WitnessV0ScriptHash(redeemscript));
}

CScript GetScriptForHTLC(const CPubKey& seller, const CPubKey& refund, const std::vector<unsigned char> image, uint32_t timeout)
{
CScript script;

script << OP_IF;
script << OP_SHA256 << image << OP_EQUALVERIFY << ToByteVector(seller);
script << OP_ELSE;

if (timeout <= 16)
{
script << CScript::EncodeOP_N(timeout);
}
else
{
script << CScriptNum(timeout);
}

script << OP_CHECKSEQUENCEVERIFY << OP_DROP << ToByteVector(refund);
script << OP_ENDIF;
script << OP_CHECKSIG;

return script;
}

bool IsValidDestination(const CTxDestination& dest) {
return dest.which() != 0;
}
3 changes: 3 additions & 0 deletions src/script/standard.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ CScript GetScriptForRawPubKey(const CPubKey& pubkey);
/** Generate a multisig script. */
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);

/** Generate a Hash-Timelock Script script. */
CScript GetScriptForHTLC(const CPubKey& seller, const CPubKey& refund, const std::vector<unsigned char> image, uint32_t timeout);

/**
* Generate a pay-to-witness script for the given redeem script. If the redeem
* script is P2PK or P2PKH, this returns a P2WPKH script, otherwise it returns a
Expand Down
81 changes: 72 additions & 9 deletions src/spv/bitcoin/BRTransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@

#include "BRTransaction.h"
#include "BRKey.h"
#include "BRAddress.h"
#include "BRArray.h"
#include <stdlib.h>
#include <inttypes.h>
#include <limits.h>
#include <time.h>

#define TX_VERSION 0x00000001
#include <util/strencodings.h>

#define TX_LOCKTIME 0x00000000
#define SIGHASH_ALL 0x01 // default, sign all of the outputs
#define SIGHASH_NONE 0x02 // sign none of the outputs, I don't care where the bitcoins go
Expand Down Expand Up @@ -351,12 +351,12 @@ static size_t _BRTransactionData(const BRTransaction *tx, uint8_t *data, size_t
}

// returns a newly allocated empty transaction that must be freed by calling BRTransactionFree()
BRTransaction *BRTransactionNew(void)
BRTransaction *BRTransactionNew(uint32_t version)
{
BRTransaction *tx = (BRTransaction *)calloc(1, sizeof(*tx));

assert(tx != NULL);
tx->version = TX_VERSION;
tx->version = version;
array_new(tx->inputs, 1);
array_new(tx->outputs, 2);
tx->lockTime = TX_LOCKTIME;
Expand Down Expand Up @@ -585,6 +585,28 @@ size_t BRTransactionSize(const BRTransaction *tx)
return size + witSize;
}

size_t BRTransactionHTLCSize(const BRTransaction *tx, const size_t sigSize)
{
BRTxInput *input;
size_t size;

size = (tx) ? 8 + BRVarIntSize(tx->inCount) + BRVarIntSize(tx->outCount) : 0;

for (size_t i = 0; i < tx->inCount; i++)
{
input = &tx->inputs[i];

size += sigSize + TX_HTLC_INPUT_NOSIG;
}

for (size_t i = 0; i < tx->outCount; i++)
{
size += sizeof(uint64_t) + BRVarIntSize(tx->outputs[i].scriptLen) + tx->outputs[i].scriptLen;
}

return size;
}

// virtual transaction size as defined by BIP141: https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
size_t BRTransactionVSize(const BRTransaction *tx)
{
Expand Down Expand Up @@ -637,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)
int BRTransactionSign(BRTransaction *tx, int forkId, BRKey keys[], size_t keysCount, HTLCScriptType htlcType, const uint8_t* seed, const uint8_t *redeemScript)
{
UInt160 pkh[keysCount];
size_t i, j;
Expand All @@ -651,17 +673,28 @@ int BRTransactionSign(BRTransaction *tx, int forkId, BRKey keys[], size_t keysCo

for (i = 0; tx && i < tx->inCount; i++) {
BRTxInput *input = &tx->inputs[i];
const uint8_t *hash = BRScriptPKH(input->script, input->scriptLen);
const uint8_t *hash;
UInt160 hash160 = UINT160_ZERO;

if (htlcType == ScriptTypeNone)
{
hash = BRScriptPKH(input->script, input->scriptLen);
}
else
{
hash160 = BRHTLCScriptPKH(input->script, input->scriptLen, htlcType);
hash = &hash160.u8[0];
Bushstar marked this conversation as resolved.
Show resolved Hide resolved
}

j = 0;
while (j < keysCount && (! hash || ! UInt160Eq(pkh[j], UInt160Get(hash)))) j++;
if (j >= keysCount) continue;

const uint8_t *elems[BRScriptElements(NULL, 0, input->script, input->scriptLen)];
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 + 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))];
size_t sigLen, scriptLen;
UInt256 md = UINT256_ZERO;

Expand Down Expand Up @@ -689,6 +722,36 @@ int BRTransactionSign(BRTransaction *tx, int forkId, BRKey keys[], size_t keysCo
BRTxInputSetSignature(input, script, scriptLen);
BRTxInputSetWitness(input, script, 0);
}
else if (elemsCount == 12 && *elems[0] == OP_IF && *elems[1] == OP_SHA256 && *elems[3] == OP_EQUALVERIFY && // HTLC
*elems[5] == OP_ELSE && *elems[7] == OP_CHECKSEQUENCEVERIFY && *elems[8] == OP_DROP &&
*elems[10] == OP_ENDIF && *elems[11] == OP_CHECKSIG)
{
uint8_t data[_BRTransactionData(tx, NULL, 0, i, forkId | SIGHASH_ALL)];
size_t dataLen = _BRTransactionData(tx, data, sizeof(data), i, forkId | SIGHASH_ALL);

BRSHA256_2(&md, data, dataLen);
sigLen = BRKeySign(&keys[j], sig, sizeof(sig) - 1, md);
sig[sigLen++] = forkId | SIGHASH_ALL;

// Add signature
scriptLen = BRScriptPushData(script, sizeof(script), sig, sigLen);

// Add seed
scriptLen += BRScriptPushData(&script[scriptLen], sizeof(script) - scriptLen, &seed[1], seed[0]);

if (htlcType == ScriptTypeSeller)
{
// Add OP_1 after seed
script[scriptLen] = OP_1;
++scriptLen;
}

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

BRTxInputSetSignature(input, script, scriptLen);
BRTxInputSetWitness(input, script, 0);
}
else { // pay-to-pubkey
uint8_t data[_BRTransactionData(tx, NULL, 0, i, forkId | SIGHASH_ALL)];
size_t dataLen = _BRTransactionData(tx, data, sizeof(data), i, forkId | SIGHASH_ALL);
Expand All @@ -706,7 +769,7 @@ int BRTransactionSign(BRTransaction *tx, int forkId, BRKey keys[], size_t keysCo
uint8_t data[BRTransactionSerialize(tx, NULL, 0)];
size_t len = BRTransactionSerialize(tx, data, sizeof(data));
BRTransaction *t = BRTransactionParse(data, len);

if (t) tx->txHash = t->txHash, tx->wtxHash = t->wtxHash;
if (t) BRTransactionFree(t);
return 1;
Expand Down
11 changes: 9 additions & 2 deletions src/spv/bitcoin/BRTransaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#define BRTransaction_h

#include "BRKey.h"
#include "BRAddress.h"
#include "BRInt.h"

#include <stddef.h>
Expand All @@ -36,9 +37,12 @@
extern "C" {
#endif

#define TX_VERSION 0x00000001
#define TX_VERSION_V2 0x00000002
#define TX_FEE_PER_KB 1000ULL // standard tx fee per kb of tx size (defid 0.12 default min-relay fee-rate)
#define TX_OUTPUT_SIZE 34 // estimated size for a typical transaction output
#define TX_INPUT_SIZE 148 // estimated size for a typical compact pubkey transaction input
#define TX_HTLC_INPUT_NOSIG 42 // approx. size of input without signature
#define TX_MIN_OUTPUT_AMOUNT (TX_FEE_PER_KB*3*(TX_OUTPUT_SIZE + TX_INPUT_SIZE)/1000) //no txout can be below this amount
#define TX_MAX_SIZE 100000 // no tx can be larger than this size in bytes
#define TX_UNCONFIRMED INT32_MAX // block height indicating transaction is unconfirmed
Expand Down Expand Up @@ -96,7 +100,7 @@ struct BRTransactionStruct {
typedef struct BRTransactionStruct BRTransaction;

// returns a newly allocated empty transaction that must be freed by calling BRTransactionFree()
BRTransaction *BRTransactionNew(void);
BRTransaction *BRTransactionNew(uint32_t version = TX_VERSION);

// returns a deep copy of tx and that must be freed by calling BRTransactionFree()
BRTransaction *BRTransactionCopy(const BRTransaction *tx);
Expand All @@ -123,6 +127,9 @@ void BRTransactionShuffleOutputs(BRTransaction *tx);
// size in bytes if signed, or estimated size assuming compact pubkey sigs
size_t BRTransactionSize(const BRTransaction *tx);

// Calculate size of HTLC transaction
size_t BRTransactionHTLCSize(const BRTransaction *tx, const size_t sigSize);

// virtual transaction size as defined by BIP141: https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
size_t BRTransactionVSize(const BRTransaction *tx);

Expand All @@ -135,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);
int BRTransactionSign(BRTransaction *tx, int forkId, BRKey keys[], size_t keysCount, HTLCScriptType htlcType = ScriptTypeNone, const uint8_t* seed = nullptr, const uint8_t* redeemScript = nullptr);

// true if tx meets IsStandard() rules: https://bitcoin.org/en/developer-guide#standard-transactions
int BRTransactionIsStandard(const BRTransaction *tx);
Expand Down
Loading