diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index e57a6db5d..576312487 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -144,6 +144,7 @@ testScripts = [ 'signmessages.py', 'p2p-compactblocks.py', 'nulldummy.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 d1d60a6f1..c1b08a264 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -212,12 +212,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; } }; @@ -229,15 +235,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 @@ -249,7 +257,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; } @@ -261,7 +270,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(); @@ -279,7 +289,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 cccebc9e0..42ce6902a 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/chainparams.cpp b/src/chainparams.cpp index c979fc724..78c36d575 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -126,6 +126,7 @@ public: base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,48); base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,5); + base58Prefixes[SCRIPT_ADDRESS2] = std::vector(1,50); base58Prefixes[SECRET_KEY] = std::vector(1,176); base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container >(); base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container >(); @@ -223,6 +224,7 @@ public: base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,111); base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,196); + base58Prefixes[SCRIPT_ADDRESS2] = std::vector(1,58); base58Prefixes[SECRET_KEY] = std::vector(1,239); base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container >(); base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container >(); @@ -312,6 +314,7 @@ public: base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,111); base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,196); + base58Prefixes[SCRIPT_ADDRESS2] = std::vector(1,58); base58Prefixes[SECRET_KEY] = std::vector(1,239); base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container >(); base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container >(); diff --git a/src/chainparams.h b/src/chainparams.h index 0c3820b7c..c4afdf4c7 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -46,6 +46,7 @@ public: enum Base58Type { PUBKEY_ADDRESS, SCRIPT_ADDRESS, + SCRIPT_ADDRESS2, SECRET_KEY, EXT_PUBLIC_KEY, EXT_SECRET_KEY, diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index 01eb2aee9..0a8377bf3 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -230,7 +230,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); } }