Fix boost::get usage with boost 1.58
[novacoin.git] / src / rpcrawtransaction.cpp
1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2012 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #include <boost/assign/list_of.hpp>
7
8 #include "base58.h"
9 #include "bitcoinrpc.h"
10 #include "txdb.h"
11 #include "init.h"
12 #include "main.h"
13 #include "net.h"
14 #include "wallet.h"
15
16 using namespace std;
17 using namespace boost;
18 using namespace boost::assign;
19 using namespace json_spirit;
20
21 void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex)
22 {
23     txnouttype type;
24     vector<CTxDestination> addresses;
25     int nRequired;
26
27     out.push_back(Pair("asm", scriptPubKey.ToString()));
28
29     if (fIncludeHex)
30         out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
31
32     if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired))
33     {
34         out.push_back(Pair("type", GetTxnOutputType(TX_NONSTANDARD)));
35         return;
36     }
37
38     out.push_back(Pair("reqSigs", nRequired));
39     out.push_back(Pair("type", GetTxnOutputType(type)));
40
41     Array a;
42     BOOST_FOREACH(const CTxDestination& addr, addresses)
43         a.push_back(CBitcoinAddress(addr).ToString());
44     out.push_back(Pair("addresses", a));
45 }
46
47 void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
48 {
49     entry.push_back(Pair("txid", tx.GetHash().GetHex()));
50     entry.push_back(Pair("version", tx.nVersion));
51     entry.push_back(Pair("time", (boost::int64_t)tx.nTime));
52     entry.push_back(Pair("locktime", (boost::int64_t)tx.nLockTime));
53     Array vin;
54     BOOST_FOREACH(const CTxIn& txin, tx.vin)
55     {
56         Object in;
57         if (tx.IsCoinBase())
58             in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
59         else
60         {
61             in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
62             in.push_back(Pair("vout", (boost::int64_t)txin.prevout.n));
63             Object o;
64             o.push_back(Pair("asm", txin.scriptSig.ToString()));
65             o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
66             in.push_back(Pair("scriptSig", o));
67         }
68         in.push_back(Pair("sequence", (boost::int64_t)txin.nSequence));
69         vin.push_back(in);
70     }
71     entry.push_back(Pair("vin", vin));
72     Array vout;
73     for (unsigned int i = 0; i < tx.vout.size(); i++)
74     {
75         const CTxOut& txout = tx.vout[i];
76         Object out;
77         out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
78         out.push_back(Pair("n", (boost::int64_t)i));
79         Object o;
80         ScriptPubKeyToJSON(txout.scriptPubKey, o, true);
81         out.push_back(Pair("scriptPubKey", o));
82         vout.push_back(out);
83     }
84     entry.push_back(Pair("vout", vout));
85
86     if (hashBlock != 0)
87     {
88         entry.push_back(Pair("blockhash", hashBlock.GetHex()));
89         map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
90         if (mi != mapBlockIndex.end() && (*mi).second)
91         {
92             CBlockIndex* pindex = (*mi).second;
93             if (pindex->IsInMainChain())
94             {
95                 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
96                 entry.push_back(Pair("time", (boost::int64_t)pindex->nTime));
97                 entry.push_back(Pair("blocktime", (boost::int64_t)pindex->nTime));
98             }
99             else
100                 entry.push_back(Pair("confirmations", 0));
101         }
102     }
103 }
104
105 Value getrawtransaction(const Array& params, bool fHelp)
106 {
107     if (fHelp || params.size() < 1 || params.size() > 2)
108         throw runtime_error(
109             "getrawtransaction <txid> [verbose=0]\n"
110             "If verbose=0, returns a string that is\n"
111             "serialized, hex-encoded data for <txid>.\n"
112             "If verbose is non-zero, returns an Object\n"
113             "with information about <txid>.");
114
115     uint256 hash;
116     hash.SetHex(params[0].get_str());
117
118     bool fVerbose = false;
119     if (params.size() > 1)
120         fVerbose = (params[1].get_int() != 0);
121
122     CTransaction tx;
123     uint256 hashBlock = 0;
124     if (!GetTransaction(hash, tx, hashBlock))
125         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
126
127     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
128     ssTx << tx;
129     string strHex = HexStr(ssTx.begin(), ssTx.end());
130
131     if (!fVerbose)
132         return strHex;
133
134     Object result;
135     result.push_back(Pair("hex", strHex));
136     TxToJSON(tx, hashBlock, result);
137     return result;
138 }
139
140 Value listunspent(const Array& params, bool fHelp)
141 {
142     if (fHelp || params.size() > 3)
143         throw runtime_error(
144             "listunspent [minconf=1] [maxconf=9999999]  [\"address\",...]\n"
145             "Returns array of unspent transaction outputs\n"
146             "with between minconf and maxconf (inclusive) confirmations.\n"
147             "Optionally filtered to only include txouts paid to specified addresses.\n"
148             "Results are an array of Objects, each of which has:\n"
149             "{txid, vout, scriptPubKey, amount, confirmations}");
150
151     RPCTypeCheck(params, list_of(int_type)(int_type)(array_type));
152
153     int nMinDepth = 1;
154     if (params.size() > 0)
155         nMinDepth = params[0].get_int();
156
157     int nMaxDepth = 9999999;
158     if (params.size() > 1)
159         nMaxDepth = params[1].get_int();
160
161     set<CBitcoinAddress> setAddress;
162     if (params.size() > 2)
163     {
164         Array inputs = params[2].get_array();
165         BOOST_FOREACH(Value& input, inputs)
166         {
167             CBitcoinAddress address(input.get_str());
168             if (!address.IsValid())
169                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+input.get_str());
170             if (setAddress.count(address))
171                 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str());
172            setAddress.insert(address);
173         }
174     }
175
176     Array results;
177     vector<COutput> vecOutputs;
178     pwalletMain->AvailableCoins(vecOutputs, false);
179     BOOST_FOREACH(const COutput& out, vecOutputs)
180     {
181         if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
182             continue;
183
184         if(setAddress.size())
185         {
186             CTxDestination address;
187             if(!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
188                 continue;
189
190             if (!setAddress.count(address))
191                 continue;
192         }
193
194         int64_t nValue = out.tx->vout[out.i].nValue;
195         const CScript& pk = out.tx->vout[out.i].scriptPubKey;
196         Object entry;
197         entry.push_back(Pair("txid", out.tx->GetHash().GetHex()));
198         entry.push_back(Pair("vout", out.i));
199         CTxDestination address;
200         if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
201         {
202             entry.push_back(Pair("address", CBitcoinAddress(address).ToString()));
203             if (pwalletMain->mapAddressBook.count(address))
204                 entry.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
205         }
206         entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end())));
207         if (pk.IsPayToScriptHash())
208         {
209             CTxDestination address;
210             if (ExtractDestination(pk, address))
211             {
212                 const CScriptID& hash = boost::get<CScriptID>(address);
213                 CScript redeemScript;
214                 if (pwalletMain->GetCScript(hash, redeemScript))
215                     entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())));
216             }
217         }
218         entry.push_back(Pair("amount",ValueFromAmount(nValue)));
219         entry.push_back(Pair("confirmations",out.nDepth));
220         entry.push_back(Pair("spendable", out.fSpendable));
221         results.push_back(entry);
222     }
223
224     return results;
225 }
226
227 Value createrawtransaction(const Array& params, bool fHelp)
228 {
229     if (fHelp || params.size() != 2)
230         throw runtime_error(
231             "createrawtransaction [{\"txid\":txid,\"vout\":n},...] {address:amount,...}\n"
232             "Create a transaction spending given inputs\n"
233             "(array of objects containing transaction id and output number),\n"
234             "sending to given address(es).\n"
235             "Returns hex-encoded raw transaction.\n"
236             "Note that the transaction's inputs are not signed, and\n"
237             "it is not stored in the wallet or transmitted to the network.");
238
239     RPCTypeCheck(params, list_of(array_type)(obj_type));
240
241     Array inputs = params[0].get_array();
242     Object sendTo = params[1].get_obj();
243
244     CTransaction rawTx;
245
246     BOOST_FOREACH(Value& input, inputs)
247     {
248         const Object& o = input.get_obj();
249
250         const Value& txid_v = find_value(o, "txid");
251         if (txid_v.type() != str_type)
252             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing txid key");
253         string txid = txid_v.get_str();
254         if (!IsHex(txid))
255             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid");
256
257         const Value& vout_v = find_value(o, "vout");
258         if (vout_v.type() != int_type)
259             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
260         int nOutput = vout_v.get_int();
261         if (nOutput < 0)
262             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
263
264         CTxIn in(COutPoint(uint256(txid), nOutput));
265         rawTx.vin.push_back(in);
266     }
267
268     set<CBitcoinAddress> setAddress;
269     BOOST_FOREACH(const Pair& s, sendTo)
270     {
271         CBitcoinAddress address(s.name_);
272         if (!address.IsValid())
273             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
274
275         if (setAddress.count(address))
276             throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
277         setAddress.insert(address);
278
279         CScript scriptPubKey;
280         scriptPubKey.SetDestination(address.Get());
281         int64_t nAmount = AmountFromValue(s.value_);
282
283         CTxOut out(nAmount, scriptPubKey);
284         rawTx.vout.push_back(out);
285     }
286
287     CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
288     ss << rawTx;
289     return HexStr(ss.begin(), ss.end());
290 }
291
292 Value decoderawtransaction(const Array& params, bool fHelp)
293 {
294     if (fHelp || params.size() != 1)
295         throw runtime_error(
296             "decoderawtransaction <hex string>\n"
297             "Return a JSON object representing the serialized, hex-encoded transaction.");
298
299     RPCTypeCheck(params, list_of(str_type));
300
301     vector<unsigned char> txData(ParseHex(params[0].get_str()));
302     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
303     CTransaction tx;
304     try {
305         ssData >> tx;
306     }
307     catch (std::exception &e) {
308         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
309     }
310
311     Object result;
312     TxToJSON(tx, 0, result);
313
314     return result;
315 }
316
317 Value decodescript(const Array& params, bool fHelp)
318 {
319     if (fHelp || params.size() != 1)
320         throw runtime_error(
321             "decodescript <hex string>\n"
322             "Decode a hex-encoded script.");
323
324     RPCTypeCheck(params, list_of(str_type));
325
326     Object r;
327     CScript script;
328     if (params[0].get_str().size() > 0){
329         vector<unsigned char> scriptData(ParseHexV(params[0], "argument"));
330         script = CScript(scriptData.begin(), scriptData.end());
331     } else {
332         // Empty scripts are valid
333     }
334     ScriptPubKeyToJSON(script, r, false);
335
336     r.push_back(Pair("p2sh", CBitcoinAddress(script.GetID()).ToString()));
337     return r;
338 }
339
340 Value signrawtransaction(const Array& params, bool fHelp)
341 {
342     if (fHelp || params.size() < 1 || params.size() > 4)
343         throw runtime_error(
344             "signrawtransaction <hex string> [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex,\"redeemScript\":hex},...] [<privatekey1>,...] [sighashtype=\"ALL\"]\n"
345             "Sign inputs for raw transaction (serialized, hex-encoded).\n"
346             "Second optional argument (may be null) is an array of previous transaction outputs that\n"
347             "this transaction depends on but may not yet be in the blockchain.\n"
348             "Third optional argument (may be null) is an array of base58-encoded private\n"
349             "keys that, if given, will be the only keys used to sign the transaction.\n"
350             "Fourth optional argument is a string that is one of six values; ALL, NONE, SINGLE or\n"
351             "ALL|ANYONECANPAY, NONE|ANYONECANPAY, SINGLE|ANYONECANPAY.\n"
352             "Returns json object with keys:\n"
353             "  hex : raw transaction with signature(s) (hex-encoded string)\n"
354             "  complete : 1 if transaction has a complete set of signature (0 if not)"
355             + HelpRequiringPassphrase());
356
357     RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true);
358
359     vector<unsigned char> txData(ParseHex(params[0].get_str()));
360     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
361     vector<CTransaction> txVariants;
362     while (!ssData.empty())
363     {
364         try {
365             CTransaction tx;
366             ssData >> tx;
367             txVariants.push_back(tx);
368         }
369         catch (std::exception &e) {
370             throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
371         }
372     }
373
374     if (txVariants.empty())
375         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction");
376
377     // mergedTx will end up with all the signatures; it
378     // starts as a clone of the rawtx:
379     CTransaction mergedTx(txVariants[0]);
380     bool fComplete = true;
381
382     // Fetch previous transactions (inputs):
383     map<COutPoint, CScript> mapPrevOut;
384     for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
385     {
386         CTransaction tempTx;
387         MapPrevTx mapPrevTx;
388         CTxDB txdb("r");
389         map<uint256, CTxIndex> unused;
390         bool fInvalid;
391
392         // FetchInputs aborts on failure, so we go one at a time.
393         tempTx.vin.push_back(mergedTx.vin[i]);
394         tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
395
396         // Copy results into mapPrevOut:
397         BOOST_FOREACH(const CTxIn& txin, tempTx.vin)
398         {
399             const uint256& prevHash = txin.prevout.hash;
400             if (mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size()>txin.prevout.n)
401                 mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey;
402         }
403     }
404
405     bool fGivenKeys = false;
406     CBasicKeyStore tempKeystore;
407     if (params.size() > 2 && params[2].type() != null_type)
408     {
409         fGivenKeys = true;
410         Array keys = params[2].get_array();
411         BOOST_FOREACH(Value k, keys)
412         {
413             CBitcoinSecret vchSecret;
414             bool fGood = vchSecret.SetString(k.get_str());
415             if (!fGood)
416                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
417             CKey key;
418             bool fCompressed;
419             CSecret secret = vchSecret.GetSecret(fCompressed);
420             key.SetSecret(secret, fCompressed);
421             tempKeystore.AddKey(key);
422         }
423     }
424     else
425         EnsureWalletIsUnlocked();
426
427     // Add previous txouts given in the RPC call:
428     if (params.size() > 1 && params[1].type() != null_type)
429     {
430         Array prevTxs = params[1].get_array();
431         BOOST_FOREACH(Value& p, prevTxs)
432         {
433             if (p.type() != obj_type)
434                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
435
436             Object prevOut = p.get_obj();
437
438             RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type));
439
440             string txidHex = find_value(prevOut, "txid").get_str();
441             if (!IsHex(txidHex))
442                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "txid must be hexadecimal");
443             uint256 txid;
444             txid.SetHex(txidHex);
445
446             int nOut = find_value(prevOut, "vout").get_int();
447             if (nOut < 0)
448                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
449
450             string pkHex = find_value(prevOut, "scriptPubKey").get_str();
451             if (!IsHex(pkHex))
452                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "scriptPubKey must be hexadecimal");
453             vector<unsigned char> pkData(ParseHex(pkHex));
454             CScript scriptPubKey(pkData.begin(), pkData.end());
455
456             COutPoint outpoint(txid, nOut);
457             if (mapPrevOut.count(outpoint))
458             {
459                 // Complain if scriptPubKey doesn't match
460                 if (mapPrevOut[outpoint] != scriptPubKey)
461                 {
462                     string err("Previous output scriptPubKey mismatch:\n");
463                     err = err + mapPrevOut[outpoint].ToString() + "\nvs:\n"+
464                         scriptPubKey.ToString();
465                     throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
466                 }
467             }
468             else
469                 mapPrevOut[outpoint] = scriptPubKey;
470
471             // if redeemScript given and not using the local wallet (private keys
472             // given), add redeemScript to the tempKeystore so it can be signed:
473             Value v = find_value(prevOut, "redeemScript");
474             if (fGivenKeys && scriptPubKey.IsPayToScriptHash())
475             {
476                 RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type));
477                 Value v = find_value(prevOut, "redeemScript");
478                 if (!(v == Value::null))
479                 {
480                     vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
481                     CScript redeemScript(rsData.begin(), rsData.end());
482                     tempKeystore.AddCScript(redeemScript);
483                 }
484             }
485         }
486     }
487
488     const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain);
489
490     int nHashType = SIGHASH_ALL;
491     if (params.size() > 3 && params[3].type() != null_type)
492     {
493         static map<string, int> mapSigHashValues =
494             boost::assign::map_list_of
495             (string("ALL"), int(SIGHASH_ALL))
496             (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))
497             (string("NONE"), int(SIGHASH_NONE))
498             (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY))
499             (string("SINGLE"), int(SIGHASH_SINGLE))
500             (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY))
501             ;
502         string strHashType = params[3].get_str();
503         if (mapSigHashValues.count(strHashType))
504             nHashType = mapSigHashValues[strHashType];
505         else
506             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
507     }
508
509     bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
510
511     // Sign what we can:
512     for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
513     {
514         CTxIn& txin = mergedTx.vin[i];
515         if (mapPrevOut.count(txin.prevout) == 0)
516         {
517             fComplete = false;
518             continue;
519         }
520         const CScript& prevPubKey = mapPrevOut[txin.prevout];
521
522         txin.scriptSig.clear();
523         // Only sign SIGHASH_SINGLE if there's a corresponding output:
524         if (!fHashSingle || (i < mergedTx.vout.size()))
525             SignSignature(keystore, prevPubKey, mergedTx, i, nHashType);
526
527         // ... and merge in other signatures:
528         BOOST_FOREACH(const CTransaction& txv, txVariants)
529         {
530             txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
531         }
532         if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, STRICT_FLAGS, 0))
533             fComplete = false;
534     }
535
536     Object result;
537     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
538     ssTx << mergedTx;
539     result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end())));
540     result.push_back(Pair("complete", fComplete));
541
542     return result;
543 }
544
545 Value sendrawtransaction(const Array& params, bool fHelp)
546 {
547     if (fHelp || params.size() < 1 || params.size() > 1)
548         throw runtime_error(
549             "sendrawtransaction <hex string>\n"
550             "Submits raw transaction (serialized, hex-encoded) to local node and network.");
551
552     RPCTypeCheck(params, list_of(str_type));
553
554     // parse hex string from parameter
555     vector<unsigned char> txData(ParseHex(params[0].get_str()));
556     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
557     CTransaction tx;
558
559     // deserialize binary data stream
560     try {
561         ssData >> tx;
562     }
563     catch (std::exception &e) {
564         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
565     }
566     uint256 hashTx = tx.GetHash();
567
568     // See if the transaction is already in a block
569     // or in the memory pool:
570     CTransaction existingTx;
571     uint256 hashBlock = 0;
572     if (GetTransaction(hashTx, existingTx, hashBlock))
573     {
574         if (hashBlock != 0)
575             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("transaction already in block ")+hashBlock.GetHex());
576         // Not in block, but already in the memory pool; will drop
577         // through to re-relay it.
578     }
579     else
580     {
581         // push to local node
582         CTxDB txdb("r");
583         if (!tx.AcceptToMemoryPool(txdb))
584             throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected");
585
586         SyncWithWallets(tx, NULL, true);
587     }
588     RelayTransaction(tx, hashTx);
589
590     return hashTx.GetHex();
591 }
592
593 Value createmultisig(const Array& params, bool fHelp)
594 {
595     if (fHelp || params.size() < 2 || params.size() > 3)
596     {
597         string msg = "createmultisig <nrequired> <'[\"key\",\"key\"]'>\n"
598             "\nCreates a multi-signature address with n signature of m keys required.\n"
599             "It returns a json object with the address and redeemScript.";
600         throw runtime_error(msg);
601     }
602
603     int nRequired = params[0].get_int();
604     const Array& keys = params[1].get_array();
605     string strAccount;
606
607     // Gather public keys
608     if (nRequired < 1)
609         throw runtime_error("a multisignature address must require at least one key to redeem");
610     if ((int)keys.size() < nRequired)
611         throw runtime_error(
612             strprintf("not enough keys supplied "
613                       "(got %" PRIszu " keys, but need at least %d to redeem)", keys.size(), nRequired));
614     if (keys.size() > 16)
615         throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number");
616     std::vector<CKey> pubkeys;
617     pubkeys.resize(keys.size());
618     for (unsigned int i = 0; i < keys.size(); i++)
619     {
620         const std::string& ks = keys[i].get_str();
621
622         // Case 1: Bitcoin address and we have full public key:
623         CBitcoinAddress address(ks);
624         if (address.IsValid())
625         {
626             CKeyID keyID;
627             if (!address.GetKeyID(keyID))
628                 throw runtime_error(
629                     strprintf("%s does not refer to a key",ks.c_str()));
630             CPubKey vchPubKey;
631             if (!pwalletMain->GetPubKey(keyID, vchPubKey))
632                 throw runtime_error(
633                     strprintf("no full public key for address %s",ks.c_str()));
634             if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
635                 throw runtime_error(" Invalid public key: "+ks);
636         }
637
638         // Case 2: hex public key
639         else if (IsHex(ks))
640         {
641             CPubKey vchPubKey(ParseHex(ks));
642             if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
643                 throw runtime_error(" Invalid public key: "+ks);
644         }
645         else
646         {
647             throw runtime_error(" Invalid public key: "+ks);
648         }
649     }
650
651     // Construct using pay-to-script-hash:
652     CScript inner;
653     inner.SetMultisig(nRequired, pubkeys);
654
655     if (inner.size() > MAX_SCRIPT_ELEMENT_SIZE)
656         throw runtime_error(
657             strprintf("redeemScript exceeds size limit: %" PRIszu " > %d", inner.size(), MAX_SCRIPT_ELEMENT_SIZE));
658
659     CScriptID innerID = inner.GetID();
660     CBitcoinAddress address(innerID);
661
662     Object result;
663     result.push_back(Pair("address", address.ToString()));
664     result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end())));
665
666     return result;
667 }