Skip to content

Commit

Permalink
Merge pull request ElementsProject#10 from kallewoof/makeoffer
Browse files Browse the repository at this point in the history
Makeoffer
  • Loading branch information
kallewoof authored Jul 26, 2017
2 parents 34586b4 + fc557e9 commit dcf8b4b
Show file tree
Hide file tree
Showing 7 changed files with 698 additions and 30 deletions.
6 changes: 6 additions & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,15 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createrawtransaction", 3, "output_assetids" },
{ "signrawtransaction", 1, "prevtxs" },
{ "signrawtransaction", 2, "privkeys" },
{ "signrawtransaction", 4, "selectedinputs" },
{ "signrawtransaction", 5, "selectedoutputs" },
{ "sendrawtransaction", 1, "allowhighfees" },
{ "sendrawtransaction", 2, "allowunblindfails" },
{ "fundrawtransaction", 1, "options" },
{ "makeoffer", 1, "sellamount" },
{ "makeoffer", 3, "buyamount" },
{ "matchoffer", 1, "sellamount" },
{ "matchoffer", 3, "buyamount" },
{ "gettxout", 1, "n" },
{ "gettxout", 2, "include_mempool" },
{ "gettxoutproof", 0, "txids" },
Expand Down
98 changes: 96 additions & 2 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1148,7 +1148,7 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::

UniValue signrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 6)
throw runtime_error(
"signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n"
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
Expand Down Expand Up @@ -1185,6 +1185,12 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
" \"ALL|ANYONECANPAY\"\n"
" \"NONE|ANYONECANPAY\"\n"
" \"SINGLE|ANYONECANPAY\"\n"
" \"ALL|SELECTINPUTS\"\n"
" \"ALL|SELECTOUTPUTS\"\n"
" \"ALL|SELECTINPUTS|SELECTOUTPUTS\"\n"
"5. \"selectedinputs\" (array, required for sighashtype & SELECTINPUTS) array of indices for to-sign inputs\n"
"6. \"selectedoutputs\" (array, required for sighashtype & SELECTOUTPUTS) array of indices for to-cover outputs\n"
" The selections may be omitted if all inputs and all outputs should be selected (an appendable transaction)\n"

"\nResult:\n"
"{\n"
Expand Down Expand Up @@ -1354,6 +1360,9 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
(string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY))
(string("SINGLE"), int(SIGHASH_SINGLE))
(string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY))
(string("ALL|SELECTINPUTS"), int(SIGHASH_ALL|SIGHASH_SELECTINPUTS))
(string("ALL|SELECTOUTPUTS"), int(SIGHASH_ALL|SIGHASH_SELECTOUTPUTS))
(string("ALL|SELECTINPUTS|SELECTOUTPUTS"), int(SIGHASH_ALL|SIGHASH_SELECTINPUTS|SIGHASH_SELECTOUTPUTS))
;
string strHashType = request.params[3].get_str();
if (mapSigHashValues.count(strHashType))
Expand All @@ -1367,11 +1376,83 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
// Script verification errors
UniValue vErrors(UniValue::VARR);

// Create selected transaction
SignSelect ss;
// CMutableTransaction selectedtx = CMutableTransaction(CTransaction(mergedTx));
std::vector<uint32_t> sigMap; // maps indices in selectedtx to indices in mergedTx for migrating signatures later
size_t selectionIndex = 4;
bool selectAll = false;
if (nHashType & SIGHASH_SELECTINPUTS) {
printf("selecting inputs\n");
while (selectionIndex < 6 && request.params[selectionIndex].isNull()) {
selectionIndex++;
}
if (selectionIndex == 6) {
selectAll = true;
for (uint32_t i = 0; i < mergedTx.vin.size(); i++)
sigMap.push_back(i);
} else {
UniValue selectedInputs = request.params[selectionIndex++];
size_t six = selectedInputs.size();
size_t ii = 0;
for (uint32_t i = 0, realI = 0; realI < mergedTx.vin.size(); i++, realI++) {
if (ii >= six || selectedInputs[ii].get_int() > realI) {
// exclude
// selectedtx.vin.erase(selectedtx.vin.begin() + i);
// if (selectedtx.wit.vtxinwit.size() > i) {
// selectedtx.wit.vtxinwit.erase(selectedtx.wit.vtxinwit.begin() + i);
// }
i--;
} else {
printf("including %u (%u)\n", ii, realI);
sigMap.push_back(realI);
ii++;
}
}
}
ss.SelectInput(sigMap);
}
printf("sigmaps\n");
for (uint32_t i : sigMap) printf("%u\n", i);
if (nHashType & SIGHASH_SELECTOUTPUTS) {
std::vector<uint32_t> sel;
if (selectAll) {
for (uint32_t i = 0; i < mergedTx.vout.size(); i++)
sel.push_back(i);
} else {
while (selectionIndex < 6 && request.params[selectionIndex].isNull()) {
selectionIndex++;
}
if (selectionIndex == 6) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing selection for SIGHASH_SELECTOUTPUTS");
}
UniValue selectedOutputs = request.params[selectionIndex];
size_t sox = selectedOutputs.size();
size_t io = 0;
for (uint32_t i = 0, realI = 0; realI < mergedTx.vout.size(); i++, realI++) {
if (io >= sox || selectedOutputs[io].get_int() > realI) {
// exclude
// selectedtx.vout.erase(selectedtx.vout.begin() + i);
// if (selectedtx.wit.vtxoutwit.size() > i) {
// selectedtx.wit.vtxoutwit.erase(selectedtx.wit.vtxoutwit.begin() + i);
// }
i--;
} else {
sel.push_back(realI);
io++;
}
}
}
ss.SelectOutput(sel);
}

// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
const CTransaction txConst(mergedTx);
// Sign what we can:
bool selecting = sigMap.size() > 0;
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
if (selecting && !ss.HasInput(i)) continue;
CTxIn& txin = mergedTx.vin[i];
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) {
Expand All @@ -1384,7 +1465,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
SignatureData sigdata;
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mergedTx.vout.size()))
ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata);
ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType, selecting ? &ss : NULL), prevPubKey, sigdata);

// ... and merge in other signatures:
BOOST_FOREACH(const CMutableTransaction& txv, txVariants) {
Expand All @@ -1399,11 +1480,24 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
if (!VerifyScript(txin.scriptSig, prevPubKey, (mergedTx.wit.vtxinwit.size() > i) ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionNoWithdrawsSignatureChecker(&txConst, i, amount), &serror)) {
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
}

// if (sigMap.size() > 0) { // <= 0 --> no selecting of inputs
// // migrate signature into mergedTx
// uint32_t realIdx = sigMap[i];
// mergedTx.vin[realIdx].scriptSig = mergedTx.vin[i].scriptSig;
// if (mergedTx.wit.vtxinwit.size() > i) {
// // TODO: define if ok to assume mergedTx also has witties
// mergedTx.wit.vtxinwit[realIdx] = mergedTx.wit.vtxinwit[i];
// }
// }
}
bool fComplete = vErrors.empty();

UniValue result(UniValue::VOBJ);
result.push_back(Pair("hex", EncodeHexTx(mergedTx)));
if (selecting) {
result.push_back(Pair("partial", true));
}
result.push_back(Pair("complete", fComplete));
if (!vErrors.empty()) {
result.push_back(Pair("errors", vErrors));
Expand Down
53 changes: 50 additions & 3 deletions src/script/sign.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,46 @@ using namespace std;

typedef std::vector<unsigned char> valtype;

TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}
CTransaction* SignSelect::ExtractSelection(const CTransaction* txTo, int& adjustedNIn)
{
CMutableTransaction tx = CMutableTransaction(*txTo);
std::vector<uint32_t>& si = selectedInputs;
std::vector<uint32_t>& so = selectedOutputs;
size_t six = si.size();
size_t sox = so.size();
size_t ii = 0;
size_t io = 0;
for (uint32_t i = 0, realI = 0; i < tx.vin.size(); i++, realI++) {
if (ii >= six || si[ii] > realI) {
// exclude
tx.vin.erase(tx.vin.begin() + i);
if (tx.wit.vtxinwit.size() > i) {
tx.wit.vtxinwit.erase(tx.wit.vtxinwit.begin() + i);
}
adjustedNIn -= adjustedNIn > i; // nIn = 1; remove 0 -> 1 -= 1 > 0; nIn = 1; remove 1 -> 1 -= 1 > 1
i--;
} else ii++; // include
}
for (uint32_t i = 0, realI = 0; i < tx.vout.size(); i++, realI++) {
if (io >= sox || so[io] > realI) {
// exclude
tx.vout.erase(tx.vout.begin() + i);
if (tx.wit.vtxoutwit.size() > i) {
tx.wit.vtxoutwit.erase(tx.wit.vtxoutwit.begin() + i);
}
i--;
} else io++; // include
}
return new CTransaction(tx);
}

TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn,
const CConfidentialValue& amountIn, int nHashTypeIn, SignSelect* ss)
: BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn), signSelect(ss) {}

bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
{
printf("address keyID = %s\n", address.ToString().c_str());
CKey key;
if (!keystore->GetKey(address, key))
return false;
Expand All @@ -30,10 +66,21 @@ bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig,
if (sigversion == SIGVERSION_WITNESS_V0 && !key.IsCompressed())
return false;

uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion);
int adjustedNIn = nIn;
CTransaction* sigtx = NULL;
if (nHashType & (SIGHASH_SELECTOUTPUTS | SIGHASH_SELECTINPUTS))
sigtx = signSelect->ExtractSelection(txTo, adjustedNIn);

uint256 hash = SignatureHash(scriptCode, *(sigtx ?: txTo), adjustedNIn, nHashType & ~(SIGHASH_SELECTINPUTS | SIGHASH_SELECTOUTPUTS), amount, sigversion);
if (sigtx) delete sigtx;
if (!key.Sign(hash, vchSig))
return false;
vchSig.push_back((unsigned char)nHashType);
if (signSelect) {
signSelect->nHashType = nHashType;
signSelect->Encode(vchSig);
} else {
vchSig.push_back((unsigned char)nHashType);
}
return true;
}

Expand Down
115 changes: 112 additions & 3 deletions src/script/sign.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,115 @@ class CScript;
class CTransaction;

struct CMutableTransaction;
class TransactionSignatureCreator;

class SignSelect {
public:
int nHashType;
std::vector<uint32_t> selectedInputs;
std::vector<uint32_t> selectedOutputs;

void SelectInput(std::vector<uint32_t> inputIndices)
{
selectedInputs.insert(selectedInputs.end(), inputIndices.begin(), inputIndices.end());
}

void SelectOutput(std::vector<uint32_t> outputIndices)
{
selectedOutputs.insert(selectedOutputs.end(), outputIndices.begin(), outputIndices.end());
}

bool HasInput(uint32_t inputIndex)
{
return std::find(selectedInputs.begin(), selectedInputs.end(), inputIndex) != selectedInputs.end();
}

bool HasOutput(uint32_t outputIndex)
{
return std::find(selectedOutputs.begin(), selectedOutputs.end(), outputIndex) != selectedOutputs.end();
}

void Write(std::vector<uint32_t> idx, std::vector<uint8_t>& buf) const
{
// serialize using utf-style bytes where the lower 7 bits are on/off toggles and the high 8th bit indicates whether
// there is more data or whether the selection is finished
if (idx.size() == 0) {
// 0 selected
buf.push_back(0);
return;
}
uint32_t max = idx[0];
for (uint32_t i = 1; i < idx.size(); i++) {
if (max < idx[i]) max = idx[i];
}
uint32_t bytes = 1 + max / 7;
uint8_t data[bytes];
memset(data, 0, bytes);
for (uint32_t i = 1; i < bytes; i++) {
data[i] |= 0x80;
}
for (uint32_t i = 0; i < idx.size(); i++) {
data[bytes - 1 - idx[i] / 7] |= 1 << (idx[i] % 7);
}
for (uint32_t i = 0; i < bytes; i++) {
buf.push_back(data[i]);
}
}

void Encode(std::vector<uint8_t>& s) const
{
if (nHashType & SIGHASH_SELECTOUTPUTS) {
Write(selectedOutputs, s);
}
if (nHashType & SIGHASH_SELECTINPUTS) {
Write(selectedInputs, s);
}
uint8_t ht = nHashType;
s.push_back(ht);
}

void Decode(std::vector<uint8_t>& v)
{
// clear up
selectedInputs.clear();
selectedOutputs.clear();

size_t csr = v.size() - 1;

uint8_t ht = nHashType = v[csr--];
std::vector<uint32_t> selections;
if (ht & SIGHASH_SELECTINPUTS) {
// read input selections
uint32_t index = 0;
for (;;) {
uint8_t sel = v[csr--];
for (uint8_t i = 0; i < 7; i++) {
if (sel & (1 << i)) selections.push_back(index);
index++;
}
if (!(sel & 0x80)) break;
}
// register
SelectInput(selections);
}
if (ht & SIGHASH_SELECTOUTPUTS) {
// read output selections
uint32_t index = 0;
selections.clear();
for (;;) {
uint8_t sel = v[csr--];
for (uint8_t i = 0; i < 7; i++) {
if (sel & (1 << i)) selections.push_back(index);
index++;
}
if (!(sel & 0x80)) break;
}
// register
SelectOutput(selections);
}
}
CTransaction* ExtractSelection(const CTransaction* txTo, int& adjustedNIn);
};

/** Virtual base class for signature creators. */
class BaseSignatureCreator {
Expand All @@ -37,9 +146,9 @@ class TransactionSignatureCreator : public BaseSignatureCreator {
int nHashType;
CConfidentialValue amount;
const TransactionNoWithdrawsSignatureChecker checker;

SignSelect* signSelect;
public:
TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, int nHashTypeIn=SIGHASH_ALL);
TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, int nHashTypeIn=SIGHASH_ALL, SignSelect* ss=NULL);
const BaseSignatureChecker& Checker() const { return checker; }
bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const;
};
Expand All @@ -48,7 +157,7 @@ class MutableTransactionSignatureCreator : public TransactionSignatureCreator {
CTransaction tx;

public:
MutableTransactionSignatureCreator(const CKeyStore* keystoreIn, const CMutableTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amount, int nHashTypeIn) : TransactionSignatureCreator(keystoreIn, &tx, nInIn, amount, nHashTypeIn), tx(*txToIn) {}
MutableTransactionSignatureCreator(const CKeyStore* keystoreIn, const CMutableTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amount, int nHashTypeIn, SignSelect* ss=NULL) : TransactionSignatureCreator(keystoreIn, &tx, nInIn, amount, nHashTypeIn, ss), tx(*txToIn) {}
};

/** A signature creator that just produces 72-byte empty signatures. */
Expand Down
Loading

0 comments on commit dcf8b4b

Please sign in to comment.