From bf798734db4539a39edd6badf54a1c3aecf193e5 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Wed, 28 Sep 2011 12:30:06 -0400 Subject: Support 3 new multisignature IsStandard transactions Initial support for (a and b), (a or b), and 2-of-3 escrow transactions (where a, b, and c are keys). --- src/bitcoinrpc.cpp | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 1 deletion(-) (limited to 'src/bitcoinrpc.cpp') diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 1f05fa862..122bf61b8 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -936,6 +936,101 @@ Value sendmany(const Array& params, bool fHelp) return wtx.GetHash().GetHex(); } +Value sendmultisig(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 4 || params.size() > 7) + { + string msg = "sendmultisig <[\"key\",\"key\"]> [minconf=1] [comment] [comment-to]\n" + " is one of: \"and\", \"or\", \"escrow\"\n" + " is an array of strings (in JSON array format); each key is a bitcoin address, hex or base58 public key\n" + " is a real and is rounded to the nearest 0.00000001"; + if (pwalletMain->IsCrypted()) + msg += "\nrequires wallet passphrase to be set with walletpassphrase first"; + throw runtime_error(msg); + } + + string strAccount = AccountFromValue(params[0]); + string strType = params[1].get_str(); + const Array& keys = params[2].get_array(); + int64 nAmount = AmountFromValue(params[3]); + int nMinDepth = 1; + if (params.size() > 4) + nMinDepth = params[4].get_int(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) + wtx.mapValue["comment"] = params[5].get_str(); + if (params.size() > 6 && params[6].type() != null_type && !params[6].get_str().empty()) + wtx.mapValue["to"] = params[6].get_str(); + + if (pwalletMain->IsLocked()) + throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first."); + + // Check funds + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + if (nAmount > nBalance) + throw JSONRPCError(-6, "Account has insufficient funds"); + + // Gather public keys + int nKeysNeeded = 0; + if (strType == "and" || strType == "or") + nKeysNeeded = 2; + else if (strType == "escrow") + nKeysNeeded = 3; + else + throw runtime_error("sendmultisig: must be one of: and or and_or"); + if (keys.size() != nKeysNeeded) + throw runtime_error( + strprintf("sendmultisig: wrong number of keys (got %d, need %d)", keys.size(), nKeysNeeded)); + std::vector pubkeys; + pubkeys.resize(nKeysNeeded); + for (int i = 0; i < nKeysNeeded; i++) + { + const std::string& ks = keys[i].get_str(); + if (ks.size() == 130) // hex public key + pubkeys[i].SetPubKey(ParseHex(ks)); + else if (ks.size() > 34) // base58-encoded + { + std::vector vchPubKey; + if (DecodeBase58(ks, vchPubKey)) + pubkeys[i].SetPubKey(vchPubKey); + else + throw runtime_error("Error base58 decoding key: "+ks); + } + else // bitcoin address for key in this wallet + { + CBitcoinAddress address(ks); + if (!pwalletMain->GetKey(address, pubkeys[i])) + throw runtime_error( + strprintf("sendmultisig: unknown address: %s",ks.c_str())); + } + } + + // Send + CScript scriptPubKey; + if (strType == "and") + scriptPubKey.SetMultisigAnd(pubkeys); + else if (strType == "or") + scriptPubKey.SetMultisigOr(pubkeys); + else + scriptPubKey.SetMultisigEscrow(pubkeys); + + CReserveKey keyChange(pwalletMain); + int64 nFeeRequired = 0; + bool fCreated = pwalletMain->CreateTransaction(scriptPubKey, nAmount, wtx, keyChange, nFeeRequired); + if (!fCreated) + { + if (nAmount + nFeeRequired > pwalletMain->GetBalance()) + throw JSONRPCError(-6, "Insufficient funds"); + throw JSONRPCError(-4, "Transaction creation failed"); + } + if (!pwalletMain->CommitTransaction(wtx, keyChange)) + throw JSONRPCError(-4, "Transaction commit failed"); + + return wtx.GetHash().GetHex(); +} + struct tallyitem { @@ -1596,7 +1691,17 @@ Value validateaddress(const Array& params, bool fHelp) // version of the address: string currentAddress = address.ToString(); ret.push_back(Pair("address", currentAddress)); - ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0))); + if (pwalletMain->HaveKey(address)) + { + ret.push_back(Pair("ismine", true)); + std::vector vchPubKey; + pwalletMain->GetPubKey(address, vchPubKey); + ret.push_back(Pair("pubkey", HexStr(vchPubKey))); + std::string strPubKey(vchPubKey.begin(), vchPubKey.end()); + ret.push_back(Pair("pubkey58", EncodeBase58(vchPubKey))); + } + else + ret.push_back(Pair("ismine", false)); if (pwalletMain->mapAddressBook.count(address)) ret.push_back(Pair("account", pwalletMain->mapAddressBook[address])); } @@ -1841,6 +1946,7 @@ pair pCallTable[] = make_pair("move", &movecmd), make_pair("sendfrom", &sendfrom), make_pair("sendmany", &sendmany), + make_pair("sendmultisig", &sendmultisig), make_pair("gettransaction", &gettransaction), make_pair("listtransactions", &listtransactions), make_pair("signmessage", &signmessage), @@ -2484,6 +2590,16 @@ int CommandLineRPC(int argc, char *argv[]) params[1] = v.get_obj(); } if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); + if (strMethod == "sendmultisig" && n > 2) + { + string s = params[2].get_str(); + Value v; + if (!read_string(s, v) || v.type() != array_type) + throw runtime_error("sendmultisig: type mismatch "+s); + params[2] = v.get_array(); + } + if (strMethod == "sendmultisig" && n > 3) ConvertTo(params[3]); + if (strMethod == "sendmultisig" && n > 4) ConvertTo(params[4]); // Execute Object reply = CallRPC(strMethod, params); -- cgit v1.2.3 From e679ec969c8b22c676ebb10bea1038f6c8f13b33 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Mon, 3 Oct 2011 13:05:43 -0400 Subject: OP_EVAL implementation OP_EVAL is a new opcode that evaluates an item on the stack as a script. It enables a new type of bitcoin address that needs an arbitrarily complex script to redeem. --- src/bitcoinrpc.cpp | 122 +++++++++++++++++++++++------------------------------ 1 file changed, 52 insertions(+), 70 deletions(-) (limited to 'src/bitcoinrpc.cpp') diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 122bf61b8..3e42e7038 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -667,7 +667,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) if (params.size() > 1) nMinDepth = params[1].get_int(); - // Get the set of pub keys that have the label + // Get the set of pub keys assigned to account string strAccount = AccountFromValue(params[0]); set setAddress; GetAccountAddresses(strAccount, setAddress); @@ -936,56 +936,30 @@ Value sendmany(const Array& params, bool fHelp) return wtx.GetHash().GetHex(); } -Value sendmultisig(const Array& params, bool fHelp) +Value addmultisigaddress(const Array& params, bool fHelp) { - if (fHelp || params.size() < 4 || params.size() > 7) + if (fHelp || params.size() < 2 || params.size() > 3) { - string msg = "sendmultisig <[\"key\",\"key\"]> [minconf=1] [comment] [comment-to]\n" - " is one of: \"and\", \"or\", \"escrow\"\n" - " is an array of strings (in JSON array format); each key is a bitcoin address, hex or base58 public key\n" - " is a real and is rounded to the nearest 0.00000001"; - if (pwalletMain->IsCrypted()) - msg += "\nrequires wallet passphrase to be set with walletpassphrase first"; + string msg = "addmultisigaddress <'[\"key\",\"key\"]'> [account]\n" + "Add a nrequired-to-sign multisignature address to the wallet\"\n" + "each key is a bitcoin address, hex or base58 public key\n" + "If [account] is specified, assign address to [account]."; throw runtime_error(msg); } - string strAccount = AccountFromValue(params[0]); - string strType = params[1].get_str(); - const Array& keys = params[2].get_array(); - int64 nAmount = AmountFromValue(params[3]); - int nMinDepth = 1; - if (params.size() > 4) - nMinDepth = params[4].get_int(); - - CWalletTx wtx; - wtx.strFromAccount = strAccount; - if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) - wtx.mapValue["comment"] = params[5].get_str(); - if (params.size() > 6 && params[6].type() != null_type && !params[6].get_str().empty()) - wtx.mapValue["to"] = params[6].get_str(); - - if (pwalletMain->IsLocked()) - throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first."); - - // Check funds - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); - if (nAmount > nBalance) - throw JSONRPCError(-6, "Account has insufficient funds"); + int nRequired = params[0].get_int(); + const Array& keys = params[1].get_array(); + string strAccount; + if (params.size() > 2) + strAccount = AccountFromValue(params[2]); // Gather public keys - int nKeysNeeded = 0; - if (strType == "and" || strType == "or") - nKeysNeeded = 2; - else if (strType == "escrow") - nKeysNeeded = 3; - else - throw runtime_error("sendmultisig: must be one of: and or and_or"); - if (keys.size() != nKeysNeeded) + if (keys.size() < nRequired) throw runtime_error( - strprintf("sendmultisig: wrong number of keys (got %d, need %d)", keys.size(), nKeysNeeded)); + strprintf("addmultisigaddress: wrong number of keys (got %d, need at least %d)", keys.size(), nRequired)); std::vector pubkeys; - pubkeys.resize(nKeysNeeded); - for (int i = 0; i < nKeysNeeded; i++) + pubkeys.resize(keys.size()); + for (int i = 0; i < keys.size(); i++) { const std::string& ks = keys[i].get_str(); if (ks.size() == 130) // hex public key @@ -1003,32 +977,23 @@ Value sendmultisig(const Array& params, bool fHelp) CBitcoinAddress address(ks); if (!pwalletMain->GetKey(address, pubkeys[i])) throw runtime_error( - strprintf("sendmultisig: unknown address: %s",ks.c_str())); + strprintf("addmultisigaddress: unknown address: %s",ks.c_str())); } } - // Send - CScript scriptPubKey; - if (strType == "and") - scriptPubKey.SetMultisigAnd(pubkeys); - else if (strType == "or") - scriptPubKey.SetMultisigOr(pubkeys); - else - scriptPubKey.SetMultisigEscrow(pubkeys); + // Construct using OP_EVAL + CScript inner; + inner.SetMultisig(nRequired, pubkeys); - CReserveKey keyChange(pwalletMain); - int64 nFeeRequired = 0; - bool fCreated = pwalletMain->CreateTransaction(scriptPubKey, nAmount, wtx, keyChange, nFeeRequired); - if (!fCreated) - { - if (nAmount + nFeeRequired > pwalletMain->GetBalance()) - throw JSONRPCError(-6, "Insufficient funds"); - throw JSONRPCError(-4, "Transaction creation failed"); - } - if (!pwalletMain->CommitTransaction(wtx, keyChange)) - throw JSONRPCError(-4, "Transaction commit failed"); + uint160 scriptHash = Hash160(inner); + CScript scriptPubKey; + scriptPubKey.SetEval(inner); + pwalletMain->AddCScript(scriptHash, inner); + CBitcoinAddress address; + address.SetScriptHash160(scriptHash); - return wtx.GetHash().GetHex(); + pwalletMain->SetAddressBookName(address, strAccount); + return address.ToString(); } @@ -1700,6 +1665,24 @@ Value validateaddress(const Array& params, bool fHelp) std::string strPubKey(vchPubKey.begin(), vchPubKey.end()); ret.push_back(Pair("pubkey58", EncodeBase58(vchPubKey))); } + else if (pwalletMain->HaveCScript(address.GetHash160())) + { + ret.push_back(Pair("isscript", true)); + CScript subscript; + pwalletMain->GetCScript(address.GetHash160(), subscript); + ret.push_back(Pair("ismine", ::IsMine(*pwalletMain, subscript))); + std::vector addresses; + txntype whichType; + int nRequired; + ExtractAddresses(subscript, pwalletMain, whichType, addresses, nRequired); + ret.push_back(Pair("script", GetTxnTypeName(whichType))); + Array a; + BOOST_FOREACH(const CBitcoinAddress& addr, addresses) + a.push_back(addr.ToString()); + ret.push_back(Pair("addresses", a)); + if (whichType == TX_MULTISIG) + ret.push_back(Pair("sigsrequired", nRequired)); + } else ret.push_back(Pair("ismine", false)); if (pwalletMain->mapAddressBook.count(address)) @@ -1946,7 +1929,7 @@ pair pCallTable[] = make_pair("move", &movecmd), make_pair("sendfrom", &sendfrom), make_pair("sendmany", &sendmany), - make_pair("sendmultisig", &sendmultisig), + make_pair("addmultisigaddress", &addmultisigaddress), make_pair("gettransaction", &gettransaction), make_pair("listtransactions", &listtransactions), make_pair("signmessage", &signmessage), @@ -2590,16 +2573,15 @@ int CommandLineRPC(int argc, char *argv[]) params[1] = v.get_obj(); } if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); - if (strMethod == "sendmultisig" && n > 2) + if (strMethod == "addmultisigaddress" && n > 0) ConvertTo(params[0]); + if (strMethod == "addmultisigaddress" && n > 1) { - string s = params[2].get_str(); + string s = params[1].get_str(); Value v; if (!read_string(s, v) || v.type() != array_type) - throw runtime_error("sendmultisig: type mismatch "+s); - params[2] = v.get_array(); + throw runtime_error("addmultisigaddress: type mismatch "+s); + params[1] = v.get_array(); } - if (strMethod == "sendmultisig" && n > 3) ConvertTo(params[3]); - if (strMethod == "sendmultisig" && n > 4) ConvertTo(params[4]); // Execute Object reply = CallRPC(strMethod, params); -- cgit v1.2.3 From fae3e2aab646a4813c6b9ad28b4c9cf4330c1a59 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Wed, 19 Oct 2011 09:50:15 -0400 Subject: Disable addmultisigaddress if not testnet --- src/bitcoinrpc.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/bitcoinrpc.cpp') diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 3e42e7038..47fd9830a 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -946,6 +946,8 @@ Value addmultisigaddress(const Array& params, bool fHelp) "If [account] is specified, assign address to [account]."; throw runtime_error(msg); } + if (!fTestNet) + throw runtime_error("addmultisigaddress available only when running -testnet\n"); int nRequired = params[0].get_int(); const Array& keys = params[1].get_array(); -- cgit v1.2.3 From 2a45a494b0bec6a0f1fc6ab7f26c260b85e7ff3e Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Tue, 8 Nov 2011 13:20:29 -0500 Subject: Use block times for 'hard' OP_EVAL switchover, and refactored EvalScript so it takes a flag for how to interpret OP_EVAL. Also increased IsStandard size of scriptSigs to 500 bytes, so a 3-of-3 multisig transaction IsStandard. --- src/bitcoinrpc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/bitcoinrpc.cpp') diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 47fd9830a..1a1c99157 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1674,10 +1674,10 @@ Value validateaddress(const Array& params, bool fHelp) pwalletMain->GetCScript(address.GetHash160(), subscript); ret.push_back(Pair("ismine", ::IsMine(*pwalletMain, subscript))); std::vector addresses; - txntype whichType; + txnouttype whichType; int nRequired; ExtractAddresses(subscript, pwalletMain, whichType, addresses, nRequired); - ret.push_back(Pair("script", GetTxnTypeName(whichType))); + ret.push_back(Pair("script", GetTxnOutputType(whichType))); Array a; BOOST_FOREACH(const CBitcoinAddress& addr, addresses) a.push_back(addr.ToString()); -- cgit v1.2.3