diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index 97aecc5e..02ce19bc 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -27,6 +27,7 @@ testScripts=( 'merkle_blocks.py' 'signrawtransactions.py' 'walletbackup.py' + 'zcpour.py' ); testScriptsExt=( 'bipdersig-p2p.py' diff --git a/qa/rpc-tests/test_framework/authproxy.py b/qa/rpc-tests/test_framework/authproxy.py index bc7d655f..1188f099 100644 --- a/qa/rpc-tests/test_framework/authproxy.py +++ b/qa/rpc-tests/test_framework/authproxy.py @@ -49,7 +49,7 @@ except ImportError: USER_AGENT = "AuthServiceProxy/0.1" -HTTP_TIMEOUT = 30 +HTTP_TIMEOUT = 600 log = logging.getLogger("BitcoinRPC") diff --git a/qa/rpc-tests/zcpour.py b/qa/rpc-tests/zcpour.py new file mode 100755 index 00000000..39b4cf97 --- /dev/null +++ b/qa/rpc-tests/zcpour.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python2 + +# +# Test Pour semantics +# + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +from decimal import Decimal +import os +import shutil +import sys + +class PourTxTest(BitcoinTestFramework): + def setup_network(self): + # Start with split network: + return super(PourTxTest, self).setup_network(True) + + def run_test(self): + # All nodes should start with 1,250 BTC: + starting_balance = 1250 + for i in range(4): + assert_equal(self.nodes[i].getbalance(), starting_balance) + self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress! + + # Generate zcaddress keypairs + zckeypair1 = self.nodes[0].zcrawkeygen() + zcsecretkey1 = zckeypair1["zcsecretkey"] + zcaddress1 = zckeypair1["zcaddress"] + + zckeypair2 = self.nodes[0].zcrawkeygen() + zcsecretkey2 = zckeypair2["zcsecretkey"] + zcaddress2 = zckeypair2["zcaddress"] + + self.nodes[0].move("", "foo", 1220) + self.nodes[0].move("", "bar", 30) + assert_equal(self.nodes[0].getbalance(""), 0) + + change_address = self.nodes[0].getnewaddress("foo") + + # Pour some of our money into this address + (total_in, inputs) = gather_inputs(self.nodes[0], 1210) + outputs = {} + outputs[change_address] = 78 + rawtx = self.nodes[0].createrawtransaction(inputs, outputs) + + pour_inputs = {} + pour_outputs = {} + pour_outputs[zcaddress1] = 100 + pour_outputs[zcaddress2] = 800 + exception_triggered = False + try: + pour_result = self.nodes[0].zcrawpour(rawtx, pour_inputs, pour_outputs, 0, 0) + except JSONRPCException: + exception_triggered = True + + # We expect it to fail; the pour's balance equation isn't adding up. + assert_equal(exception_triggered, True) + + pour_outputs[zcaddress1] = 370 + pour_result = self.nodes[0].zcrawpour(rawtx, pour_inputs, pour_outputs, 1200, 30) + # This should succeed to construct a pour: the math adds up! + + signed_tx_pour = self.nodes[0].signrawtransaction(pour_result["rawtxn"]) + + print signed_tx_pour + + self.nodes[0].sendrawtransaction(signed_tx_pour["hex"]) + +if __name__ == '__main__': + PourTxTest().main() diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index 4b576b37..52d877c2 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -91,6 +91,10 @@ static const CRPCConvertParam vRPCConvertParams[] = { "estimatepriority", 0 }, { "prioritisetransaction", 1 }, { "prioritisetransaction", 2 }, + { "zcrawpour", 1 }, + { "zcrawpour", 2 }, + { "zcrawpour", 3 }, + { "zcrawpour", 4 } }; class CRPCConvertTable diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 82464846..c81839da 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -376,6 +376,8 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "walletlock", &walletlock, true }, { "wallet", "walletpassphrasechange", &walletpassphrasechange, true }, { "wallet", "walletpassphrase", &walletpassphrase, true }, + { "wallet", "zcrawkeygen", &zc_raw_keygen, true }, + { "wallet", "zcrawpour", &zc_raw_pour, true }, #endif // ENABLE_WALLET }; diff --git a/src/rpcserver.h b/src/rpcserver.h index 30a5b28d..890d12f0 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -208,6 +208,8 @@ extern json_spirit::Value getblockchaininfo(const json_spirit::Array& params, bo extern json_spirit::Value getnetworkinfo(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value setmocktime(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value resendwallettransactions(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value zc_raw_keygen(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value zc_raw_pour(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 62699716..b777e254 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2343,3 +2343,122 @@ Value listunspent(const Array& params, bool fHelp) return results; } + +Value zc_raw_pour(const json_spirit::Array& params, bool fHelp) +{ + /* + zcrawpour {: , ...} {: , ...} vpub_old vpub_new + */ + + //RPCTypeCheck(params, boost::assign::list_of(str_type)(obj_type)(obj_type)(int_type)(int_type)); + + CTransaction tx; + if (!DecodeHexTx(tx, params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + + Object inputs = params[1].get_obj(); + Object outputs = params[2].get_obj(); + + CAmount vpub_old(0); + CAmount vpub_new(0); + + if (params[3].get_real() != 0.0) + vpub_old = AmountFromValue(params[3]); + + if (params[4].get_real() != 0.0) + vpub_new = AmountFromValue(params[4]); + + std::vector vpourin; + std::vector vpourout; + + /* + BOOST_FOREACH(const Pair& s, inputs) + { + // TODO + } + */ + + // TODO + vpourin.push_back(PourInput(INCREMENTAL_MERKLE_TREE_DEPTH)); + vpourin.push_back(PourInput(INCREMENTAL_MERKLE_TREE_DEPTH)); + + BOOST_FOREACH(const Pair& s, outputs) + { + libzerocash::PublicAddress addrTo; + + { + vector decoded(ParseHex(s.name_)); + CDataStream ssData(decoded, SER_NETWORK, PROTOCOL_VERSION); + + std::vector pubAddressSecret; + std::string encryptionPublicKey; + + ssData >> pubAddressSecret; + ssData >> encryptionPublicKey; + + addrTo = libzerocash::PublicAddress(pubAddressSecret, encryptionPublicKey); + } + CAmount nAmount = AmountFromValue(s.value_); + + libzerocash::Coin coin(addrTo, nAmount); + libzerocash::PourOutput output(coin, addrTo); + + vpourout.push_back(output); + } + + while (vpourout.size() < 2) { + vpourout.push_back(PourOutput(0)); + } + + // TODO + if (vpourout.size() > 2 || vpourin.size() > 2) { + throw runtime_error("unsupported"); + } + + uint256 anchor; // TODO + CScript scriptPubKey; + CPourTx pourtx(*pzerocashParams, + scriptPubKey, + anchor, + {vpourin[0], vpourin[1]}, + {vpourout[0], vpourout[1]}, + vpub_old, + vpub_new); + + CMutableTransaction mtx(tx); + mtx.nVersion = 2; + mtx.vpour.push_back(pourtx); + + CTransaction rawTx(mtx); + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << rawTx; + + Object result; + result.push_back(Pair("encryptedbucket1", HexStr(pourtx.ciphertexts[0].begin(), pourtx.ciphertexts[0].end()))); + result.push_back(Pair("encryptedbucket2", HexStr(pourtx.ciphertexts[1].begin(), pourtx.ciphertexts[1].end()))); + result.push_back(Pair("rawtxn", HexStr(ss.begin(), ss.end()))); + return result; +} + +Value zc_raw_keygen(const json_spirit::Array& params, bool fHelp) +{ + auto zckeypair = libzerocash::Address::CreateNewRandomAddress(); + + CDataStream pub(SER_NETWORK, PROTOCOL_VERSION); + CDataStream priv(SER_NETWORK, PROTOCOL_VERSION); + + pub << zckeypair.getPublicAddress().getPublicAddressSecret(); // a_pk + pub << zckeypair.getPublicAddress().getEncryptionPublicKey(); // pk_enc + + priv << zckeypair.getPrivateAddress().getAddressSecret(); // a_sk + priv << zckeypair.getPrivateAddress().getEncryptionSecretKey(); // sk_enc + + std::string pub_hex = HexStr(pub.begin(), pub.end()); + std::string priv_hex = HexStr(priv.begin(), priv.end()); + + Object result; + result.push_back(Pair("zcaddress", pub_hex)); + result.push_back(Pair("zcsecretkey", priv_hex)); + return result; +} \ No newline at end of file