mirror of
https://github.com/twisterarmy/twister-core.git
synced 2025-01-22 12:34:24 +00:00
sendmany RPC command, to send to multiple recipients in one transaction.
This commit is contained in:
parent
f4f2987273
commit
b931ed8563
48
main.cpp
48
main.cpp
@ -678,7 +678,11 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
|
|||||||
|
|
||||||
// Safety limits
|
// Safety limits
|
||||||
unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
|
unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
|
||||||
if (GetSigOpCount() > 2 || nSize < 100)
|
// Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
|
||||||
|
// attacks disallow transactions with more than one SigOp per 34 bytes.
|
||||||
|
// 34 bytes because a TxOut is:
|
||||||
|
// 20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length
|
||||||
|
if (GetSigOpCount() > nSize / 34 || nSize < 100)
|
||||||
return error("AcceptToMemoryPool() : nonstandard transaction");
|
return error("AcceptToMemoryPool() : nonstandard transaction");
|
||||||
|
|
||||||
// Rather not work on nonstandard transactions
|
// Rather not work on nonstandard transactions
|
||||||
@ -3846,8 +3850,18 @@ bool SelectCoins(int64 nTargetValue, set<CWalletTx*>& setCoinsRet)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet)
|
bool CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet)
|
||||||
{
|
{
|
||||||
|
int64 nValue = 0;
|
||||||
|
foreach (const PAIRTYPE(CScript, int64)& s, vecSend)
|
||||||
|
{
|
||||||
|
if (nValue < 0)
|
||||||
|
return false;
|
||||||
|
nValue += s.second;
|
||||||
|
}
|
||||||
|
if (vecSend.empty() || nValue < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
CRITICAL_BLOCK(cs_main)
|
CRITICAL_BLOCK(cs_main)
|
||||||
{
|
{
|
||||||
// txdb must be opened before the mapWallet lock
|
// txdb must be opened before the mapWallet lock
|
||||||
@ -3860,11 +3874,12 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CR
|
|||||||
wtxNew.vin.clear();
|
wtxNew.vin.clear();
|
||||||
wtxNew.vout.clear();
|
wtxNew.vout.clear();
|
||||||
wtxNew.fFromMe = true;
|
wtxNew.fFromMe = true;
|
||||||
if (nValue < 0)
|
|
||||||
return false;
|
|
||||||
int64 nValueOut = nValue;
|
|
||||||
int64 nTotalValue = nValue + nFeeRet;
|
int64 nTotalValue = nValue + nFeeRet;
|
||||||
double dPriority = 0;
|
double dPriority = 0;
|
||||||
|
// vouts to the payees
|
||||||
|
foreach (const PAIRTYPE(CScript, int64)& s, vecSend)
|
||||||
|
wtxNew.vout.push_back(CTxOut(s.second, s.first));
|
||||||
|
|
||||||
// Choose coins to use
|
// Choose coins to use
|
||||||
set<CWalletTx*> setCoins;
|
set<CWalletTx*> setCoins;
|
||||||
@ -3878,11 +3893,6 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CR
|
|||||||
dPriority += (double)nCredit * pcoin->GetDepthInMainChain();
|
dPriority += (double)nCredit * pcoin->GetDepthInMainChain();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill a vout to the payee
|
|
||||||
bool fChangeFirst = GetRand(2);
|
|
||||||
if (!fChangeFirst)
|
|
||||||
wtxNew.vout.push_back(CTxOut(nValueOut, scriptPubKey));
|
|
||||||
|
|
||||||
// Fill a vout back to self with any change
|
// Fill a vout back to self with any change
|
||||||
int64 nChange = nValueIn - nTotalValue;
|
int64 nChange = nValueIn - nTotalValue;
|
||||||
if (nChange >= CENT)
|
if (nChange >= CENT)
|
||||||
@ -3900,19 +3910,18 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CR
|
|||||||
|
|
||||||
// Fill a vout to ourself, using same address type as the payment
|
// Fill a vout to ourself, using same address type as the payment
|
||||||
CScript scriptChange;
|
CScript scriptChange;
|
||||||
if (scriptPubKey.GetBitcoinAddressHash160() != 0)
|
if (vecSend[0].first.GetBitcoinAddressHash160() != 0)
|
||||||
scriptChange.SetBitcoinAddress(vchPubKey);
|
scriptChange.SetBitcoinAddress(vchPubKey);
|
||||||
else
|
else
|
||||||
scriptChange << vchPubKey << OP_CHECKSIG;
|
scriptChange << vchPubKey << OP_CHECKSIG;
|
||||||
wtxNew.vout.push_back(CTxOut(nChange, scriptChange));
|
|
||||||
|
// Insert change txn at random position:
|
||||||
|
vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size());
|
||||||
|
wtxNew.vout.insert(position, CTxOut(nChange, scriptChange));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
reservekey.ReturnKey();
|
reservekey.ReturnKey();
|
||||||
|
|
||||||
// Fill a vout to the payee
|
|
||||||
if (fChangeFirst)
|
|
||||||
wtxNew.vout.push_back(CTxOut(nValueOut, scriptPubKey));
|
|
||||||
|
|
||||||
// Fill vin
|
// Fill vin
|
||||||
foreach(CWalletTx* pcoin, setCoins)
|
foreach(CWalletTx* pcoin, setCoins)
|
||||||
for (int nOut = 0; nOut < pcoin->vout.size(); nOut++)
|
for (int nOut = 0; nOut < pcoin->vout.size(); nOut++)
|
||||||
@ -3954,6 +3963,13 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CR
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet)
|
||||||
|
{
|
||||||
|
vector< pair<CScript, int64> > vecSend;
|
||||||
|
vecSend.push_back(make_pair(scriptPubKey, nValue));
|
||||||
|
return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet);
|
||||||
|
}
|
||||||
|
|
||||||
// Call after CreateTransaction unless you want to abort
|
// Call after CreateTransaction unless you want to abort
|
||||||
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
|
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
|
||||||
{
|
{
|
||||||
|
1
main.h
1
main.h
@ -77,6 +77,7 @@ bool ProcessMessages(CNode* pfrom);
|
|||||||
bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv);
|
bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv);
|
||||||
bool SendMessages(CNode* pto, bool fSendTrickle);
|
bool SendMessages(CNode* pto, bool fSendTrickle);
|
||||||
int64 GetBalance();
|
int64 GetBalance();
|
||||||
|
bool CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet);
|
||||||
bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet);
|
bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet);
|
||||||
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
|
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
|
||||||
bool BroadcastTransaction(CWalletTx& wtxNew);
|
bool BroadcastTransaction(CWalletTx& wtxNew);
|
||||||
|
73
rpc.cpp
73
rpc.cpp
@ -768,6 +768,69 @@ Value sendfrom(const Array& params, bool fHelp)
|
|||||||
return wtx.GetHash().GetHex();
|
return wtx.GetHash().GetHex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value sendmany(const Array& params, bool fHelp)
|
||||||
|
{
|
||||||
|
if (fHelp || params.size() < 2 || params.size() > 4)
|
||||||
|
throw runtime_error(
|
||||||
|
"sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
|
||||||
|
"amounts are double-precision floating point numbers");
|
||||||
|
|
||||||
|
string strAccount = AccountFromValue(params[0]);
|
||||||
|
Object sendTo = params[1].get_obj();
|
||||||
|
int nMinDepth = 1;
|
||||||
|
if (params.size() > 2)
|
||||||
|
nMinDepth = params[2].get_int();
|
||||||
|
|
||||||
|
CWalletTx wtx;
|
||||||
|
wtx.strFromAccount = strAccount;
|
||||||
|
if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
|
||||||
|
wtx.mapValue["comment"] = params[3].get_str();
|
||||||
|
|
||||||
|
set<string> setAddress;
|
||||||
|
vector<pair<CScript, int64> > vecSend;
|
||||||
|
|
||||||
|
int64 totalAmount = 0;
|
||||||
|
foreach(const Pair& s, sendTo)
|
||||||
|
{
|
||||||
|
uint160 hash160;
|
||||||
|
string strAddress = s.name_;
|
||||||
|
|
||||||
|
if (setAddress.count(strAddress))
|
||||||
|
throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+strAddress);
|
||||||
|
setAddress.insert(strAddress);
|
||||||
|
|
||||||
|
CScript scriptPubKey;
|
||||||
|
if (!scriptPubKey.SetBitcoinAddress(strAddress))
|
||||||
|
throw JSONRPCError(-5, string("Invalid bitcoin address:")+strAddress);
|
||||||
|
int64 nAmount = AmountFromValue(s.value_);
|
||||||
|
totalAmount += nAmount;
|
||||||
|
|
||||||
|
vecSend.push_back(make_pair(scriptPubKey, nAmount));
|
||||||
|
}
|
||||||
|
|
||||||
|
CRITICAL_BLOCK(cs_mapWallet)
|
||||||
|
{
|
||||||
|
// Check funds
|
||||||
|
int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
|
||||||
|
if (totalAmount > nBalance)
|
||||||
|
throw JSONRPCError(-6, "Account has insufficient funds");
|
||||||
|
|
||||||
|
// Send
|
||||||
|
CReserveKey keyChange;
|
||||||
|
int64 nFeeRequired = 0;
|
||||||
|
bool fCreated = CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
|
||||||
|
if (!fCreated)
|
||||||
|
{
|
||||||
|
if (totalAmount + nFeeRequired > GetBalance())
|
||||||
|
throw JSONRPCError(-6, "Insufficient funds");
|
||||||
|
throw JSONRPCError(-4, "Transaction creation failed");
|
||||||
|
}
|
||||||
|
if (!CommitTransaction(wtx, keyChange))
|
||||||
|
throw JSONRPCError(-4, "Transaction commit failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return wtx.GetHash().GetHex();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct tallyitem
|
struct tallyitem
|
||||||
@ -1344,6 +1407,7 @@ pair<string, rpcfn_type> pCallTable[] =
|
|||||||
make_pair("getbalance", &getbalance),
|
make_pair("getbalance", &getbalance),
|
||||||
make_pair("move", &movecmd),
|
make_pair("move", &movecmd),
|
||||||
make_pair("sendfrom", &sendfrom),
|
make_pair("sendfrom", &sendfrom),
|
||||||
|
make_pair("sendmany", &sendmany),
|
||||||
make_pair("gettransaction", &gettransaction),
|
make_pair("gettransaction", &gettransaction),
|
||||||
make_pair("listtransactions", &listtransactions),
|
make_pair("listtransactions", &listtransactions),
|
||||||
make_pair("getwork", &getwork),
|
make_pair("getwork", &getwork),
|
||||||
@ -1995,6 +2059,15 @@ int CommandLineRPC(int argc, char *argv[])
|
|||||||
if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
|
if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
|
||||||
if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||||
if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||||
|
if (strMethod == "sendmany" && n > 1)
|
||||||
|
{
|
||||||
|
string s = params[1].get_str();
|
||||||
|
Value v;
|
||||||
|
if (!read_string(s, v) || v.type() != obj_type)
|
||||||
|
throw runtime_error("type mismatch");
|
||||||
|
params[1] = v.get_obj();
|
||||||
|
}
|
||||||
|
if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
|
||||||
|
|
||||||
// Execute
|
// Execute
|
||||||
Object reply = CallRPC(strMethod, params);
|
Object reply = CallRPC(strMethod, params);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user