cli method to create CC transaction

This commit is contained in:
Scott Sadler
2018-01-23 14:05:43 -03:00
parent 2356884c5f
commit 6585a02268
2 changed files with 129 additions and 164 deletions

View File

@@ -0,0 +1,47 @@
import os
import subprocess
import json
import base64
inputs = [
{
'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71',
'vout': 0,
'fulfillment': {
'type': 'preimage-sha-256',
'preimage': '',
},
}
]
outputs = [
{
"condition": {
'type': 'preimage-sha-256',
'preimage': ''
},
"amount": 1,
}
]
addresses = ['RQygCQA7ovCrVCHM3sSkDQjjQUg5X4SCJw']
privkeys = ["UszeEw39HcoeRegZb74xuvngbcQ5jkwQZZVpAvBp1bvBhXD36ghX"]
proc = subprocess.Popen(['src/komodo-cli', 'createrawtransactioncc', json.dumps(inputs), json.dumps(outputs)],
stdout=subprocess.PIPE)
assert not proc.wait()
tx_hex = proc.stdout.read().strip()
tx = base64.b16decode(tx_hex, casefold=True)
print repr(tx)
proc = subprocess.Popen(['src/komodo-cli', 'signrawtransactioncc', tx_hex, json.dumps(privkeys)],
stdout=subprocess.PIPE)
proc.wait()
result = json.loads(proc.stdout.read())

View File

@@ -375,45 +375,73 @@ UniValue verifytxoutproofcc(const UniValue& params, bool fHelp)
return res; return res;
} }
CC *ConditionFromValue(const UniValue& condition) {
std::string encoded = condition.write(); CC *ConditionFromString(std::string encoded) {
char *err = new char[1000]; char *err = new char[1000];
CC *cond = cc_conditionFromJSONString(encoded.c_str(), err); CC *cond = cc_conditionFromJSONString(encoded.c_str(), err);
if (NULL == cond) if (NULL == cond)
throw JSONRPCError(RPC_INVALID_PARAMETER, err); throw JSONRPCError(RPC_INVALID_PARAMETER, err);
} }
CC *ConditionFromCharVector(vector<unsigned char>& vch) {
return ConditionFromString(std::string(vch.begin(), vch.end()));
}
CC *ConditionFromUnsignedInputScript(const CScript& script)
{
opcodetype op;
CScript::const_iterator it = script.begin();
vector<unsigned char> vch;
if (script.GetOp2(it, op, &vch)) {
if (vch.at(0) == '{') {
return ConditionFromCharVector(vch);
}
}
return NULL;
}
CC *ConditionFromValue(UniValue value) {
std::string conditionEncoded = value.write();
// TODO: Is null?
return ConditionFromString(conditionEncoded);
}
UniValue createrawtransactioncc(const UniValue& params, bool fHelp) UniValue createrawtransactioncc(const UniValue& params, bool fHelp)
{ {
if (fHelp || params.size() != 2) if (fHelp || params.size() != 2)
throw runtime_error( throw runtime_error(
"createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,...}\n" "createrawtransactioncc [{\"txid\":\"id\",\"vout\":n,\"fulfillment\":{...}},...] {\"address\":amount,...}\n"
"\nCreate a transaction spending the given inputs and sending to the given addresses.\n" "\nCreate a transaction spending the given inputs and sending to the given addresses.\n"
"Returns hex-encoded raw transaction.\n" "Returns hex-encoded raw transaction.\n"
"Note that the transaction's inputs are not signed, and\n" "Note that the transaction's inputs are not signed, and\n"
"it is not stored in the wallet or transmitted to the network.\n" "it is not stored in the wallet or transmitted to the network.\n"
"\nArguments:\n" "\nArguments:\n"
"1. \"transactions\" (string, required) A json array of json objects\n" "1. \"inputs\" (string, required) A json array of json objects\n"
" [\n" " [\n"
" {\n" " {\n"
" \"txid\":\"id\", (string, required) The transaction id\n" " \"txid\":\"id\", (string, required) The transaction id\n"
" \"vout\":n (numeric, required) The output number\n" " \"vout\":n, (numeric, required) The output number\n"
" \"fulfillment\":{...} (object, required) The JSON encoded cryptocondition fulfillment for the previous transaction's output\n"
" }\n" " }\n"
" ,...\n" " ,...\n"
" ]\n" " ]\n"
"2. \"addresses\" (string, required) a json object with addresses as keys and amounts as values\n" "2. \"outputs\" (string, required) a json array with addresses as keys and amounts as values\n"
" {\n" " {\n"
" \"address\": x.xxx (numeric, required) The key is the bitcoin address, the value is the btc amount\n" " \"condition\":\"{...}\" (object, required) The JSON encoded condition\n"
" ,...\n" " \"amount\":n, (numeric, required) The coin amount in satoshis\n"
" }\n" " }\n"
"\nResult:\n" "\nResult:\n"
"\"transaction\" (string) hex string of the transaction\n" "\"transaction\" (string) hex string of the transaction\n"
"\nExamples\n" // "\nExamples\n"
+ HelpExampleCli("createrawtransactioncc", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"") // + HelpExampleCli("createrawtransactioncc", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"")
+ HelpExampleRpc("createrawtransactioncc", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"") // + HelpExampleRpc("createrawtransactioncc", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"")
); );
LOCK(cs_main); LOCK(cs_main);
@@ -437,49 +465,46 @@ UniValue createrawtransactioncc(const UniValue& params, bool fHelp)
if (nOutput < 0) if (nOutput < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
CC *cond = ConditionFromValue(find_value(output, "condition")); const UniValue& ffill_v = find_value(input, "fulfillment");
char *ffillBin = new char[1024*1024]; if (!ffill_v.isObject()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "\"fulfillment\" missing or not an object");
}
const std::string ffillEncoded = ffill_v.write();
CTxIn in(COutPoint(txid, nOutput)); // Validate the fulfillment
CC *cond = ConditionFromString(ffillEncoded);
if (cond == NULL) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "\"fulfillment\" not a valid JSON encoded crypto condition");
}
cc_free(cond);
CScript inputScript;
const std::vector<unsigned char> ffill(ffillEncoded.c_str(), ffillEncoded.c_str() + ffillEncoded.size());
inputScript << ffill;
CTxIn in(COutPoint(txid, nOutput), inputScript);
rawTx.vin.push_back(in); rawTx.vin.push_back(in);
} }
for (size_t idx = 0; idx < outputs.size(); idx++) { for (size_t idx = 0; idx < outputs.size(); idx++) {
const UniValue& output = outputs[idx].get_obj(); const UniValue& output = outputs[idx].get_obj();
// This does not need to be fulfilled, we just want the condition binary
CC *cond = ConditionFromValue(find_value(output, "condition")); CC *cond = ConditionFromValue(find_value(output, "condition"));
char *condBin = new char[1000]; char *condBin = new char[1000];
size_t binLength = cc_conditionBinary(cond, condBin); size_t binLength = cc_conditionBinary(cond, condBin);
std::vector<char> condVec(condBin, condBin + binLength); const std::vector<unsigned char> condVec(condBin, condBin + binLength);
CScript redeemScript; CScript redeemScript;
redeemScript << ToByteVector(condVec) << OP_CHECKCRYPTOCONDITIONVERIFY; redeemScript << condVec << OP_CHECKCRYPTOCONDITIONVERIFY;
CAmount nAmount = AmountFromValue(find_value(output, "amount")); CAmount nAmount = AmountFromValue(find_value(output, "amount"));
CTxOut out(nAmount, redeemScript); CTxOut out(nAmount, redeemScript);
rawTx.vout.push_back(out); rawTx.vout.push_back(out);
} }
/*
set<CBitcoinAddress> setAddress;
vector<string> addrList = sendTo.getKeys();
BOOST_FOREACH(const string& name_, addrList) {
CBitcoinAddress address(name_);
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+name_);
if (setAddress.count(address))
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_);
setAddress.insert(address);
CScript scriptPubKey = GetScriptForDestination(address.Get());
CAmount nAmount = AmountFromValue(sendTo[name_]);
CTxOut out(nAmount, scriptPubKey);
rawTx.vout.push_back(out);
}
*/
return EncodeHexTx(rawTx); return EncodeHexTx(rawTx);
} }
@@ -575,57 +600,15 @@ UniValue decoderawtransactioncc(const UniValue& params, bool fHelp)
return result; return result;
} }
UniValue decodescriptcc(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"decodescript \"hex\"\n"
"\nDecode a hex-encoded script.\n"
"\nArguments:\n"
"1. \"hex\" (string) the hex encoded script\n"
"\nResult:\n"
"{\n"
" \"asm\":\"asm\", (string) Script public key\n"
" \"hex\":\"hex\", (string) hex encoded public key\n"
" \"type\":\"type\", (string) The output type\n"
" \"reqSigs\": n, (numeric) The required signatures\n"
" \"addresses\": [ (json array of string)\n"
" \"address\" (string) bitcoin address\n"
" ,...\n"
" ],\n"
" \"p2sh\",\"address\" (string) script address\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("decodescript", "\"hexstring\"")
+ HelpExampleRpc("decodescript", "\"hexstring\"")
);
LOCK(cs_main);
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR));
UniValue r(UniValue::VOBJ);
CScript script;
if (params[0].get_str().size() > 0){
vector<unsigned char> scriptData(ParseHexV(params[0], "argument"));
script = CScript(scriptData.begin(), scriptData.end());
} else {
// Empty scripts are valid
}
ScriptPubKeyToJSON(script, r, false);
r.push_back(Pair("p2sh", CBitcoinAddress(CScriptID(script)).ToString()));
return r;
}
/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */ /** Pushes a JSON object for script verification or signing errors to vErrorsRet. */
static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage) {} static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage) {}
UniValue signrawtransactioncc(const UniValue& params, bool fHelp) UniValue signrawtransactioncc(const UniValue& params, bool fHelp)
{ {
if (fHelp || params.size() < 1 || params.size() > 4) if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error( throw runtime_error(
"signrawtransactioncc \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n" "signrawtransactioncc \"hexstring\" ( [\"privatekey1\",...] sighashtype )\n"
"\nSign cryptocondition inputs for raw transaction (serialized, hex-encoded).\n" "\nSign cryptocondition inputs for raw transaction (serialized, hex-encoded).\n"
"The second optional argument (may be null) is an array of previous transaction outputs that\n" "The second optional argument (may be null) is an array of previous transaction outputs that\n"
"this transaction depends on but may not yet be in the block chain.\n" "this transaction depends on but may not yet be in the block chain.\n"
@@ -637,17 +620,7 @@ UniValue signrawtransactioncc(const UniValue& params, bool fHelp)
"\nArguments:\n" "\nArguments:\n"
"1. \"hexstring\" (string, required) The transaction hex string\n" "1. \"hexstring\" (string, required) The transaction hex string\n"
/* "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n" "2. \"privatekeys\" (string, optional) A json array of base58-encoded private keys for signing\n"
" [ (json array of json objects, or 'null' if none provided)\n"
" {\n"
" \"txid\":\"id\", (string, required) The transaction id\n"
" \"vout\":n, (numeric, required) The output number\n"
" \"scriptPubKey\": \"hex\", (string, required) script key\n"
" \"redeemScript\": \"hex\" (string, required for P2SH) redeem script\n"
" }\n"
" ,...\n"
" ]\n" */
"3. \"privatekeys\" (string, optional) A json array of base58-encoded private keys for signing\n"
" [ (json array of strings, or 'null' if none provided)\n" " [ (json array of strings, or 'null' if none provided)\n"
" \"privatekey\" (string) private key in base58-encoding\n" " \"privatekey\" (string) private key in base58-encoding\n"
" ,...\n" " ,...\n"
@@ -709,24 +682,6 @@ UniValue signrawtransactioncc(const UniValue& params, bool fHelp)
// starts as a clone of the rawtx: // starts as a clone of the rawtx:
CMutableTransaction mergedTx(txVariants[0]); CMutableTransaction mergedTx(txVariants[0]);
// Fetch previous transactions (inputs):
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
{
LOCK(mempool.cs);
CCoinsViewCache &viewChain = *pcoinsTip;
CCoinsViewMemPool viewMempool(&viewChain, mempool);
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) {
const uint256& prevHash = txin.prevout.hash;
CCoins coins;
view.AccessCoins(prevHash); // this is certainly allowed to fail
}
view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
}
bool fGivenKeys = false; bool fGivenKeys = false;
CBasicKeyStore tempKeystore; CBasicKeyStore tempKeystore;
if (params.size() > 2 && !params[2].isNull()) { if (params.size() > 2 && !params[2].isNull()) {
@@ -749,57 +704,6 @@ UniValue signrawtransactioncc(const UniValue& params, bool fHelp)
EnsureWalletIsUnlocked(); EnsureWalletIsUnlocked();
#endif #endif
// Add previous txouts given in the RPC call:
/*
if (params.size() > 1 && !params[1].isNull()) {
UniValue prevTxs = params[1].get_array();
for (size_t idx = 0; idx < prevTxs.size(); idx++) {
const UniValue& p = prevTxs[idx];
if (!p.isObject())
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
UniValue prevOut = p.get_obj();
RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR));
uint256 txid = ParseHashO(prevOut, "txid");
int nOut = find_value(prevOut, "vout").get_int();
if (nOut < 0)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
CScript scriptPubKey(pkData.begin(), pkData.end());
{
CCoinsModifier coins = view.ModifyCoins(txid);
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
string err("Previous output scriptPubKey mismatch:\n");
err = err + coins->vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+
scriptPubKey.ToString();
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
}
if ((unsigned int)nOut >= coins->vout.size())
coins->vout.resize(nOut+1);
coins->vout[nOut].scriptPubKey = scriptPubKey;
coins->vout[nOut].nValue = 0; // we don't know the actual output value
}
// if redeemScript given and not using the local wallet (private keys
// given), add redeemScript to the tempKeystore so it can be signed:
if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) {
RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)("redeemScript",UniValue::VSTR));
UniValue v = find_value(prevOut, "redeemScript");
if (!v.isNull()) {
vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
tempKeystore.AddCScript(redeemScript);
}
}
}
}
*/
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain); const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain);
#else #else
@@ -834,6 +738,18 @@ UniValue signrawtransactioncc(const UniValue& params, bool fHelp)
// Sign what we can: // Sign what we can:
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
CTxIn& txin = mergedTx.vin[i]; CTxIn& txin = mergedTx.vin[i];
CC *cond;
printf("Go\n");
if (cond = ConditionFromUnsignedInputScript(txin.scriptSig)) {
printf("Found unsigned\n");
continue;
}
printf("No luck\n");
/*
* Replace all this with detection of cryptoconditions spec and signing with each privkey
const CCoins* coins = view.AccessCoins(txin.prevout.hash); const CCoins* coins = view.AccessCoins(txin.prevout.hash);
if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) { if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) {
TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
@@ -854,6 +770,7 @@ UniValue signrawtransactioncc(const UniValue& params, bool fHelp)
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i), &serror)) { if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i), &serror)) {
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
} }
*/
} }
bool fComplete = vErrors.empty(); bool fComplete = vErrors.empty();
@@ -867,13 +784,14 @@ UniValue signrawtransactioncc(const UniValue& params, bool fHelp)
return result; return result;
} }
UniValue sendrawtransactioncc(const UniValue& params, bool fHelp) UniValue sendrawtransactioncc(const UniValue& params, bool fHelp)
{ {
if (fHelp || params.size() < 1 || params.size() > 2) if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error( throw runtime_error(
"sendrawtransaction \"hexstring\" ( allowhighfees )\n" "sendrawtransaction \"hexstring\" ( allowhighfees )\n"
"\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n" "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n"
"\nAlso see createrawtransaction and signrawtransaction calls.\n" "\nAlso see createrawtransactioncc and signrawtransactioncc calls.\n"
"\nArguments:\n" "\nArguments:\n"
"1. \"hexstring\" (string, required) The hex string of the raw transaction)\n" "1. \"hexstring\" (string, required) The hex string of the raw transaction)\n"
"2. allowhighfees (boolean, optional, default=false) Allow high fees\n" "2. allowhighfees (boolean, optional, default=false) Allow high fees\n"
@@ -881,7 +799,7 @@ UniValue sendrawtransactioncc(const UniValue& params, bool fHelp)
"\"hex\" (string) The transaction hash in hex\n" "\"hex\" (string) The transaction hash in hex\n"
"\nExamples:\n" "\nExamples:\n"
"\nCreate a transaction\n" "\nCreate a transaction\n"
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") + + HelpExampleCli("createrawtransactioncc", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
"Sign the transaction, and get back the hex\n" "Sign the transaction, and get back the hex\n"
+ HelpExampleCli("signrawtransaction", "\"myhex\"") + + HelpExampleCli("signrawtransaction", "\"myhex\"") +
"\nSend the transaction (signed hex)\n" "\nSend the transaction (signed hex)\n"