Skip to content

Commit

Permalink
completerawexchange
Browse files Browse the repository at this point in the history
  • Loading branch information
mike31 committed Feb 14, 2017
1 parent d6382ce commit 0590087
Show file tree
Hide file tree
Showing 5 changed files with 276 additions and 13 deletions.
4 changes: 4 additions & 0 deletions src/rpc/rpcclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createrawexchange", 2 },
{ "appendrawexchange", 2 },
{ "appendrawexchange", 3 },
{ "completerawexchange", 2 },
{ "completerawexchange", 3 },
{ "completerawexchange", 4 },
{ "decoderawexchange", 1 },
{ "appendrawmetadata", 1 },
{ "appendrawdata", 1 },
Expand Down Expand Up @@ -305,6 +308,7 @@ static const CRPCConvertParamMayBeString vRPCConvertParamsMayBeString[] =
{ "sendwithdata", 2 },
{ "sendwithmetadatafrom", 3 },
{ "sendwithdatafrom", 3 },
{ "completerawexchange", 4 },
{ "importaddress", 0 },
{ "importprivkey", 0 },
{ "subscribe", 0 },
Expand Down
246 changes: 237 additions & 9 deletions src/rpc/rpcexchange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ bool AcceptExchange(const CTransaction& tx, vector <CTxOut>& inputs, vector <str
return GetTxInputsAsTxOuts(tx, inputs, errors, reason);
}

Object ExchangeAssetEntry(uint256 hash,const CTxOut txout,mc_Script *lpScript,mc_Buffer *asset_amounts,mc_Buffer *total_amounts,bool& can_disable,string& strError)
Object ExchangeAssetEntry(uint256 hash,const CTxOut txout,mc_Script *lpScript,mc_Buffer *asset_amounts,mc_Buffer *total_amounts,bool& can_disable,bool allow_sighashall,string& strError)
{
Object result;
CBitcoinAddress address;
Expand Down Expand Up @@ -48,7 +48,17 @@ Object ExchangeAssetEntry(uint256 hash,const CTxOut txout,mc_Script *lpScript,mc

if(hash_type != (SIGHASH_SINGLE | SIGHASH_ANYONECANPAY))
{
strError="Signature hash type should be SINGLE | SIGHASH_ANYONECANPAY";
if(allow_sighashall)
{
if(hash_type != SIGHASH_ALL)
{
strError="Signature hash type should be SINGLE | SIGHASH_ANYONECANPAY or SIGHASH_ALL";
}
}
else
{
strError="Signature hash type should be SINGLE | SIGHASH_ANYONECANPAY";
}
}

elem = lpScript->GetData(1,&elem_size);
Expand Down Expand Up @@ -193,7 +203,7 @@ Object ExchangeAssetEntry(uint256 hash,const CTxOut txout,mc_Script *lpScript,mc
return result;
}

Object DecodeExchangeTransaction(const CTransaction tx,int verbose,int64_t& native_balance,mc_Buffer *lpAssets,bool& is_complete,string& strError)
Object DecodeExchangeTransaction(const CTransaction tx,int verbose,int64_t& native_balance,mc_Buffer *lpAssets,bool& is_complete,bool allow_last_sighashall,string& strError)
{
Object result;
strError="";
Expand Down Expand Up @@ -242,20 +252,31 @@ Object DecodeExchangeTransaction(const CTransaction tx,int verbose,int64_t& nati

entry_error=input_errors[i];

Object offer=ExchangeAssetEntry(tx.vin[i].prevout.hash,input_txouts[i],lpScript,asset_amounts,lpAssets,can_disable,entry_error);
Object offer=ExchangeAssetEntry(tx.vin[i].prevout.hash,input_txouts[i],lpScript,asset_amounts,lpAssets,can_disable,
(i==(int)input_txouts.size()-1) ? allow_last_sighashall : false,entry_error);



offer.push_back(Pair("txid", tx.vin[i].prevout.hash.ToString()));
offer.push_back(Pair("vout", (uint64_t)(tx.vin[i].prevout.n)));

exchange.push_back(Pair("offer", offer));
if(entry_error.size() == 0)
{
if (!VerifyScript(script2, input_txouts[i].scriptPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&tx, i)))
{
entry_error="Wrong signature";
}
}

if(entry_error.size())
{
throw JSONRPCError(RPC_INVALID_PARAMETER, entry_error + strprintf("; Input: %d, txid: %s, vout: %d",i,tx.vin[i].prevout.hash.GetHex().c_str(),tx.vin[i].prevout.n));
}

entry_error="";
lpScript->Clear();
exchange.push_back(Pair("ask", ExchangeAssetEntry(0,tx.vout[i],lpScript,asset_amounts,lpAssets,can_disable,entry_error)));
exchange.push_back(Pair("ask", ExchangeAssetEntry(0,tx.vout[i],lpScript,asset_amounts,lpAssets,can_disable,true,entry_error)));
if(entry_error.size())
{
throw JSONRPCError(RPC_INVALID_PARAMETER, entry_error+ strprintf("; Output: %d,",i));
Expand All @@ -265,6 +286,55 @@ Object DecodeExchangeTransaction(const CTransaction tx,int verbose,int64_t& nati
exchanges.push_back(exchange);
}

Array aMetaData;
set<uint256> streams_already_seen;

if(tx.vout.size() > tx.vin.size())
{
for(int i=(int)input_txouts.size();i<(int)tx.vout.size();i++)
{
const CTxOut& txout = tx.vout[i];
if(!txout.scriptPubKey.IsUnspendable())
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Too many outputs");
}
else
{
const CScript& script1 = tx.vout[i].scriptPubKey;
CScript::const_iterator pc1 = script1.begin();
size_t elem_size;
const unsigned char *elem;

lpScript->Clear();
lpScript->SetScript((unsigned char*)(&pc1[0]),(size_t)(script1.end()-pc1),MC_SCR_TYPE_SCRIPTPUBKEY);

if(lpScript->GetNumElements()<=1)
{
if(lpScript->GetNumElements()==1)
{
elem = lpScript->GetData(lpScript->GetNumElements()-1,&elem_size);
aMetaData.push_back(OpReturnEntry(elem,elem_size,tx.GetHash(),i));
}
}
else
{
Value data_item_entry=DataItemEntry(tx,i,streams_already_seen, 0x03);
if(!data_item_entry.is_null())
{
aMetaData.push_back(data_item_entry);
}
else
{
elem = lpScript->GetData(lpScript->GetNumElements()-1,&elem_size);
if(elem_size)
{
aMetaData.push_back(OpReturnEntry(elem,elem_size,tx.GetHash(),i));
}
}
}
}
}
}


delete lpScript;
Expand Down Expand Up @@ -471,10 +541,13 @@ Object DecodeExchangeTransaction(const CTransaction tx,int verbose,int64_t& nati

result.push_back(Pair("complete", is_complete));
}



if(verbose)
{
result.push_back(Pair("exchanges", exchanges));
result.push_back(Pair("data", aMetaData));
}

// result.push_back(Pair("error", strError));
Expand Down Expand Up @@ -762,7 +835,7 @@ Value appendrawexchange(const json_spirit::Array& params, bool fHelp)
LOCK(cs_main);
Object decode_result;

decode_result=DecodeExchangeTransaction(tx,0,native_balance,asset_amounts,is_complete,strError);
decode_result=DecodeExchangeTransaction(tx,0,native_balance,asset_amounts,is_complete,false,strError);
}

delete asset_amounts;
Expand All @@ -774,9 +847,164 @@ Value appendrawexchange(const json_spirit::Array& params, bool fHelp)
return result;
}

Value completeexchange(const json_spirit::Array& params, bool fHelp)
Value completerawexchange(const json_spirit::Array& params, bool fHelp)
{
return "";
if (fHelp || params.size() < 4 || params.size() > 5)
throw runtime_error("Help message not found\n");

COutPoint offer_input;
mc_Script *lpScript;
lpScript=new mc_Script;
CAmount nAmount=0;


string strError=FindExchangeOutPoint(params,1,offer_input,nAmount,lpScript);
if(strError.size())
{
throw JSONRPCError(RPC_INVALID_PARAMETER, strError);
}

CTxOut preparedTxOut;
if(!FindPreparedTxOut(preparedTxOut,offer_input,strError))
{
throw JSONRPCError(RPC_INVALID_PARAMETER, strError);
}

const CScript& script1 = preparedTxOut.scriptPubKey;
CTxDestination addressRet;
if(!ExtractDestination(script1, addressRet))
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot extract address from prepared output");
}

CKeyID *lpKeyID=boost::get<CKeyID> (&addressRet);
if(lpKeyID == NULL)
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Prepared output should be pay-to-pubkeyhash");
}

if(mc_gState->m_Permissions->CanSend(NULL,(unsigned char*)(lpKeyID)) == 0)
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Address of prepared output doesn't have send permission");
}

if(mc_gState->m_Permissions->CanReceive(NULL,(unsigned char*)(lpKeyID)) == 0)
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Address of prepared output doesn't have receive permission");
}

vector<CTxDestination> addresses;
addresses.push_back(addressRet);

CScript scriptPubKey = GetScriptForDestination(addresses[0]);

for(int element=0;element < lpScript->GetNumElements();element++)
{
size_t elem_size;
const unsigned char *elem;
elem = lpScript->GetData(element,&elem_size);
if(elem)
{
scriptPubKey << vector<unsigned char>(elem, elem + elem_size) << OP_DROP;
}
else
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid script");
}

CMutableTransaction tx;

vector<unsigned char> txData(ParseHex(params[0].get_str()));

CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
ssData >> tx;

CTxOut txout(nAmount, scriptPubKey);

tx.vout.push_back(txout);
tx.vin.push_back(CTxIn(offer_input));

if(params.size() > 4)
{
mc_EntityDetails found_entity;
CScript scriptOpReturn=ParseRawMetadata(params[4],0x0002,NULL,&found_entity);

if(found_entity.GetEntityType() == MC_ENT_TYPE_STREAM)
{
const unsigned char *aptr;

aptr=GetAddressIDPtr(addresses[0]);
if(aptr)
{
if((found_entity.AnyoneCanWrite() == 0) && (mc_gState->m_Permissions->CanWrite(found_entity.GetTxID(),aptr) == 0))
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Publishing in this stream is not allowed from this address");
}
if(mc_gState->m_Permissions->CanSend(NULL,aptr) == 0)
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Address doesn't have send permission");
}
}
}

CTxOut txoutdata(0, scriptOpReturn);
tx.vout.push_back(txoutdata);
}

if(mc_gState->m_WalletMode & MC_WMD_ADDRESS_TXS)
{
int err;
const CWalletTx& wtx=pwalletTxsMain->GetWalletTx(offer_input.hash,NULL,&err);
if(err)
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Output not found in wallet");
}
if(!SignSignature(*pwalletMain, wtx.vout[offer_input.n].scriptPubKey, tx, tx.vin.size()-1, SIGHASH_ALL ))
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Signing transaction failed");
}
}
else
{
std::map<uint256, CWalletTx>::const_iterator it = pwalletMain->mapWallet.find(offer_input.hash);

if (it == pwalletMain->mapWallet.end())
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Output not found in wallet");
}

const CWalletTx* pcoin = &(*it).second;


if(!SignSignature(*pwalletMain, pcoin->vout[offer_input.n].scriptPubKey, tx, tx.vin.size()-1, SIGHASH_ALL ))
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Signing transaction failed");
}
}
delete lpScript;

bool is_complete=true;
int64_t native_balance;

mc_Buffer *asset_amounts;
asset_amounts=new mc_Buffer;
mc_InitABufferMap(asset_amounts);
asset_amounts->Clear();

{
LOCK(cs_main);
Object decode_result;

decode_result=DecodeExchangeTransaction(tx,0,native_balance,asset_amounts,is_complete,true,strError);
}

delete asset_amounts;

if(!is_complete)
{
throw JSONRPCError(RPC_INVALID_REQUEST, "Incomplete exchange");
}

return EncodeHexTx(tx);
}

Value decoderawexchange(const json_spirit::Array& params, bool fHelp)
Expand Down Expand Up @@ -819,7 +1047,7 @@ Value decoderawexchange(const json_spirit::Array& params, bool fHelp)
{
LOCK(cs_main);

result=DecodeExchangeTransaction(tx,verbose,native_balance,asset_amounts,is_complete,strError);
result=DecodeExchangeTransaction(tx,verbose,native_balance,asset_amounts,is_complete,true,strError);
}

delete asset_amounts;
Expand Down
35 changes: 33 additions & 2 deletions src/rpc/rpchelp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3463,15 +3463,45 @@ void mc_InitRPCHelpMap15()
+ HelpExampleRpc("setruntimeparam", "\"miningturnover\", 0.3")
));

}

void mc_InitRPCHelpMap16()
{
mapHelpStrings.insert(std::make_pair("completerawexchange",
"completerawexchange hex txid vout ask-assets ( data-hex|object ) \n"
"\nCompletes existing exchange transaction, adds fee if needed\n"
"Returns hex-encoded raw transaction.\n"
+ HelpRequiringPassphraseWrapper() +
"\nArguments:\n"
"1. \"hex\" (string, required) The transaction hex string\n"
"2. \"txid\" (string, required) Transaction ID of the output prepared by preparelockunspent.\n"
"3. \"vout\" (numeric, required) Output index\n"
"4. \"ask-assets\" (object, required) A json object of assets to ask\n"
" {\n"
" \"asset-identifier\" : asset-quantity\n"
" ,...\n"
" }\n"
"5. data-hex (string, optional) Data hex string\n"
"or\n"
"5. publish-new-stream-item (object, optional) A json object with stream item\n"
" {\n"
" \"for\" : stream-identifier (string,required) Stream identifier - one of the following: stream txid, stream reference, stream name.\n"
" \"key\" : key (string,optional, default: \"\") Item key\n"
" \"data\" : data-hex (string,optional, default: \"\") Data hex string\n"
" }\n"
"\nResult:\n"
"\"transaction\" (string) hex string of the transaction\n"
"\nExamples:\n"
+ HelpExampleCli("completerawexchange", "\"hexstring\" f4c3dd510dd55761015c9d96bff7793b0d501dd6f01a959fd7dd02478fb47dfb 1 \"{\\\"1234-5678-1234\\\":200}\"" )
+ HelpExampleRpc("completerawexchange", "\"hexstring\",\"f4c3dd510dd55761015c9d96bff7793b0d501dd6f01a959fd7dd02478fb47dfb\",1,\"{\\\"1234-5678-1234\\\":200}\\\"")
));

mapHelpStrings.insert(std::make_pair("AAAAAAA",
""
));


}


void mc_InitRPCLogParamCountMap()
{
mapLogParamCounts.insert(std::make_pair("encryptwallet",0));
Expand All @@ -3498,6 +3528,7 @@ void mc_InitRPCHelpMap()
mc_InitRPCHelpMap13();
mc_InitRPCHelpMap14();
mc_InitRPCHelpMap15();
mc_InitRPCHelpMap16();

mc_InitRPCLogParamCountMap();
}
Expand Down
Loading

0 comments on commit 0590087

Please sign in to comment.