diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index dbeb77d00..e5d66037f 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -155,6 +155,7 @@ testScripts = [ 'rpcnamedargs.py', 'listsinceblock.py', 'p2p-leaktests.py', + 'test_script_address2.py' ] if ENABLE_ZMQ: testScripts.append('zmq_test.py') diff --git a/qa/rpc-tests/test_script_address2.py b/qa/rpc-tests/test_script_address2.py new file mode 100755 index 000000000..adefb092b --- /dev/null +++ b/qa/rpc-tests/test_script_address2.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# Copyright (c) 2015-2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test new Litecoin multisig prefix functionality. +# + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +import decimal + +class ScriptAddress2Test(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 3 + self.setup_clean_chain = False + + def setup_network(self): + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, [])) + self.nodes.append(start_node(1, self.options.tmpdir, [])) + self.nodes.append(start_node(2, self.options.tmpdir, [])) + connect_nodes(self.nodes[1], 0) + connect_nodes(self.nodes[2], 0) + self.is_network_split = False + self.sync_all() + + def run_test(self): + cnt = self.nodes[0].getblockcount() + + # Mine some blocks + self.nodes[1].generate(100) + self.sync_all() + if (self.nodes[0].getblockcount() != cnt + 100): + raise AssertionError("Failed to mine 100 blocks") + + addr = self.nodes[0].getnewaddress() + addr2 = self.nodes[0].getnewaddress() + multisig_addr = self.nodes[0].addmultisigaddress(2, [addr, addr2], "multisigaccount") + assert_equal(multisig_addr[0], 'Q') + + # Send to a new multisig address + txid = self.nodes[1].sendtoaddress(multisig_addr, 1) + block = self.nodes[1].generate(3) + self.sync_all() + tx = self.nodes[2].getrawtransaction(txid, 1) + dest_addrs = [tx["vout"][0]['scriptPubKey']['addresses'][0], + tx["vout"][1]['scriptPubKey']['addresses'][0]] + assert(multisig_addr in dest_addrs) + + # Spend from the new multisig address + addr3 = self.nodes[1].getnewaddress() + txid = self.nodes[0].sendfrom("multisigaccount", addr3, 0.8) + block = self.nodes[0].generate(2) + self.sync_all() + assert(self.nodes[0].getbalance("multisigaccount", 1) < 0.2) + assert(self.nodes[1].listtransactions()[-1]['address'] == addr3) + + # Send to an old multisig address. The api addmultisigaddress + # can only generate a new address so we manually compute + # multisig_addr_old beforehand using an old client. + priv_keys = ["cU7eeLPKzXeKMeZvnEJhvZZ3tLqVF3XGeo1BbM8dnbmV7pP3Qg89", + "cTw7mRhSvTfzqCt6MFgBoTBqwBpYu2rWugisXcwjv4cAASh3iqPt"] + + addrs = ["mj6gNGRXPXrD69R5ApjcsDerZGrYKSfb6v", + "mqET4JA3L7P7FoUjUP3F6m6YsLpCkyzzou"] + + self.nodes[0].importprivkey(priv_keys[0]) + self.nodes[0].importprivkey(priv_keys[1]) + + multisig_addr_new = self.nodes[0].addmultisigaddress(2, addrs, "multisigaccount2") + assert_equal(multisig_addr_new, "QZ974ZrPrmqMmm1PSVp4m8YEgo3bCQZBbe") + multisig_addr_old = "2N5nLwYz9qfnGdaFLpPn3gS6oYQbmLTWPjq" + + ## Let's send to the old address. We can then find it in the + ## new address with the new client. So basically the old + ## address and the new one are the same thing. + txid = self.nodes[1].sendtoaddress(multisig_addr_old, 1) + block = self.nodes[1].generate(1) + self.sync_all() + tx = self.nodes[2].getrawtransaction(txid, 1) + dest_addrs = [tx["vout"][0]['scriptPubKey']['addresses'][0], + tx["vout"][1]['scriptPubKey']['addresses'][0]] + assert(multisig_addr_new in dest_addrs) + assert(multisig_addr_old not in dest_addrs) + + # Spend from the new multisig address + addr4 = self.nodes[1].getnewaddress() + txid = self.nodes[0].sendfrom("multisigaccount2", addr4, 0.8) + block = self.nodes[0].generate(2) + self.sync_all() + assert(self.nodes[0].getbalance("multisigaccount2", 1) < 0.2) + assert(self.nodes[1].listtransactions()[-1]['address'] == addr4) + + + +if __name__ == '__main__': + ScriptAddress2Test().main() diff --git a/src/base58.cpp b/src/base58.cpp index e2f475bb7..e421556c9 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -216,12 +216,18 @@ class CBitcoinAddressVisitor : public boost::static_visitor { private: CBitcoinAddress* addr; + CChainParams::Base58Type script_type_; public: - CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {} + CBitcoinAddressVisitor(CBitcoinAddress* addrIn, CChainParams::Base58Type script_type) + : addr(addrIn), script_type_(script_type) + { + assert(script_type == CChainParams::SCRIPT_ADDRESS || + script_type == CChainParams::SCRIPT_ADDRESS2); + } bool operator()(const CKeyID& id) const { return addr->Set(id); } - bool operator()(const CScriptID& id) const { return addr->Set(id); } + bool operator()(const CScriptID& id) const { return addr->Set(id, script_type_); } bool operator()(const CNoDestination& no) const { return false; } }; @@ -233,15 +239,17 @@ bool CBitcoinAddress::Set(const CKeyID& id) return true; } -bool CBitcoinAddress::Set(const CScriptID& id) +bool CBitcoinAddress::Set(const CScriptID& id, CChainParams::Base58Type type) { - SetData(Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS), &id, 20); + assert(type == CChainParams::SCRIPT_ADDRESS || type == CChainParams::SCRIPT_ADDRESS2); + SetData(Params().Base58Prefix(type), &id, 20); return true; } -bool CBitcoinAddress::Set(const CTxDestination& dest) +bool CBitcoinAddress::Set(const CTxDestination& dest, CChainParams::Base58Type type) { - return boost::apply_visitor(CBitcoinAddressVisitor(this), dest); + assert(type == CChainParams::SCRIPT_ADDRESS || type == CChainParams::SCRIPT_ADDRESS2); + return boost::apply_visitor(CBitcoinAddressVisitor(this, type), dest); } bool CBitcoinAddress::IsValid() const @@ -253,7 +261,8 @@ bool CBitcoinAddress::IsValid(const CChainParams& params) const { bool fCorrectSize = vchData.size() == 20; bool fKnownVersion = vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) || - vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); + vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS) || + vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS2); return fCorrectSize && fKnownVersion; } @@ -265,7 +274,8 @@ CTxDestination CBitcoinAddress::Get() const memcpy(&id, &vchData[0], 20); if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) return CKeyID(id); - else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) + else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS) || + vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS2)) return CScriptID(id); else return CNoDestination(); @@ -283,7 +293,8 @@ bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const bool CBitcoinAddress::IsScript() const { - return IsValid() && vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS); + return IsValid() && (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS) || + vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS2)); } void CBitcoinSecret::SetKey(const CKey& vchSecret) diff --git a/src/base58.h b/src/base58.h index 3998283bb..6ef98ec69 100644 --- a/src/base58.h +++ b/src/base58.h @@ -104,8 +104,8 @@ public: class CBitcoinAddress : public CBase58Data { public: bool Set(const CKeyID &id); - bool Set(const CScriptID &id); - bool Set(const CTxDestination &dest); + bool Set(const CScriptID &id, CChainParams::Base58Type type=CChainParams::SCRIPT_ADDRESS2); + bool Set(const CTxDestination &dest, CChainParams::Base58Type type=CChainParams::SCRIPT_ADDRESS2); bool IsValid() const; bool IsValid(const CChainParams ¶ms) const; diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index 6cd998990..ef3537329 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -228,7 +228,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) continue; } CBitcoinAddress addrOut; - BOOST_CHECK_MESSAGE(addrOut.Set(dest), "encode dest: " + strTest); + BOOST_CHECK_MESSAGE(addrOut.Set(dest, CChainParams::SCRIPT_ADDRESS), "encode dest: " + strTest); BOOST_CHECK_MESSAGE(addrOut.ToString() == exp_base58string, "mismatch: " + strTest); } }