forked from bitcoin/bitcoin
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5cbb014
commit 0e4e834
Showing
3 changed files
with
286 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
// Copyright (c) 2011-2015 The Bitcoin Core developers | ||
// Distributed under the MIT software license, see the accompanying | ||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
|
||
#include <chainparams.h> | ||
#include <coins.h> | ||
#include <consensus/consensus.h> | ||
#include <consensus/merkle.h> | ||
#include <consensus/validation.h> | ||
#include <validation.h> | ||
#include <miner.h> | ||
#include <pubkey.h> | ||
#include <script/standard.h> | ||
#include <txmempool.h> | ||
#include <uint256.h> | ||
#include <util.h> | ||
#include <utilstrencodings.h> | ||
|
||
#include <txdb.h> | ||
|
||
#include <test/test_bitcoin.h> | ||
|
||
#include <boost/test/unit_test.hpp> | ||
|
||
BOOST_FIXTURE_TEST_SUITE(pegin_spent_tests, TestingSetup) | ||
|
||
class CCoinsViewTester : public CCoinsView { | ||
public: | ||
bool IsPeginSpentCalled; | ||
bool IsPeginSpent(const std::pair<uint256, COutPoint> &outpoint) const { | ||
const_cast<bool&>(IsPeginSpentCalled) = true; | ||
return CCoinsView::IsPeginSpent(outpoint); | ||
} | ||
|
||
CCoinsMap mapCoinsWritten; | ||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { | ||
mapCoinsWritten.clear(); | ||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); it = mapCoins.erase(it)) { | ||
mapCoinsWritten[it->first] = it->second; | ||
} | ||
//mapCoinsWritten = mapCoins; | ||
return CCoinsView::BatchWrite(mapCoins, hashBlock); | ||
} | ||
|
||
CCoinsViewTester() : IsPeginSpentCalled(false) {} | ||
}; | ||
|
||
BOOST_AUTO_TEST_CASE(PeginSpent_validity) | ||
{ | ||
CCoinsViewTester coins; | ||
CCoinsViewCache coinsCache(&coins); | ||
Coin ret; | ||
|
||
//Basic insert of blank outpoint pair, blank COutPoint allows for checking coinsCache | ||
|
||
std::pair<uint256, COutPoint> outpoint = std::make_pair(GetRandHash(), COutPoint(GetRandHash(), 42)); | ||
BOOST_CHECK(!coinsCache.GetCoin(outpoint.second, ret)); | ||
|
||
//Checking for pegin spentness should not create an entry | ||
BOOST_CHECK(!coinsCache.IsPeginSpent(outpoint)); | ||
BOOST_CHECK(coins.IsPeginSpentCalled); | ||
coins.IsPeginSpentCalled = false; | ||
BOOST_CHECK(!coinsCache.IsPeginSpent(outpoint)); | ||
BOOST_CHECK(!coins.IsPeginSpentCalled); | ||
|
||
coinsCache.SetPeginSpent(outpoint, true); | ||
BOOST_CHECK(coinsCache.IsPeginSpent(outpoint)); | ||
coinsCache.SetPeginSpent(outpoint, false); | ||
BOOST_CHECK(!coinsCache.IsPeginSpent(outpoint)); | ||
coinsCache.SetPeginSpent(outpoint, true); | ||
BOOST_CHECK(coinsCache.IsPeginSpent(outpoint)); | ||
|
||
//Check for slightly similar non-existent entries | ||
std::pair<uint256, COutPoint> outpoint2(outpoint); | ||
outpoint2.second.n = 0; | ||
BOOST_CHECK(!coinsCache.IsPeginSpent(outpoint2)); | ||
|
||
CCoinsMap mapCoins; | ||
CCoinsCacheEntry entry; | ||
std::pair<uint256, COutPoint> outpoint3(std::make_pair(GetRandHash(), COutPoint(GetRandHash(), 42))); | ||
|
||
//Attempt batch write of non-dirty pegin, no effect | ||
entry.flags = CCoinsCacheEntry::PEGIN; | ||
entry.peginSpent = true; | ||
mapCoins.insert(std::make_pair(outpoint3, entry)); | ||
coinsCache.BatchWrite(mapCoins, uint256()); | ||
//Check for effect | ||
coins.IsPeginSpentCalled = false; | ||
BOOST_CHECK(!coinsCache.IsPeginSpent(outpoint3)); | ||
BOOST_CHECK(coins.IsPeginSpentCalled); | ||
BOOST_CHECK(mapCoins.size() == 0); | ||
|
||
//Write again with pegin, dirty && fresh flags, but unspent. No effect. | ||
entry.peginSpent = false; | ||
entry.flags |= CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH; | ||
mapCoins.insert(std::make_pair(outpoint3, entry)); | ||
coinsCache.BatchWrite(mapCoins, uint256()); | ||
//Check for effect | ||
coins.IsPeginSpentCalled = false; | ||
BOOST_CHECK(!coinsCache.IsPeginSpent(outpoint3)); | ||
BOOST_CHECK(coins.IsPeginSpentCalled); | ||
BOOST_CHECK(mapCoins.size() == 0); | ||
|
||
//Re-mark as spent. It's super effective. | ||
entry.peginSpent = true; | ||
mapCoins.insert(std::make_pair(outpoint3, entry)); | ||
coinsCache.BatchWrite(mapCoins, uint256()); | ||
//Check for effect | ||
coins.IsPeginSpentCalled = false; | ||
BOOST_CHECK(coinsCache.IsPeginSpent(outpoint3)); | ||
BOOST_CHECK(!coins.IsPeginSpentCalled); | ||
BOOST_CHECK(mapCoins.size() == 0); | ||
|
||
//Add an entry we never IsPeginSpent'd first (ie added to cache via SetPeginSpent) | ||
std::pair<uint256, COutPoint> outpoint4(std::make_pair(GetRandHash(), COutPoint(GetRandHash(), 42))); | ||
coinsCache.SetPeginSpent(outpoint4, true); | ||
|
||
// Check the final state of coinsCache.mapCoins is sane. | ||
BOOST_CHECK_EQUAL(coins.mapCoinsWritten.size(), 0); | ||
coinsCache.Flush(); | ||
BOOST_CHECK_EQUAL(coins.mapCoinsWritten.size(), 4); | ||
BOOST_CHECK_EQUAL(coins.mapCoinsWritten[outpoint].flags, CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH | CCoinsCacheEntry::PEGIN); | ||
BOOST_CHECK_EQUAL(coins.mapCoinsWritten[outpoint].peginSpent, true); | ||
BOOST_CHECK_EQUAL(coins.mapCoinsWritten[outpoint2].flags, CCoinsCacheEntry::FRESH | CCoinsCacheEntry::PEGIN); | ||
BOOST_CHECK_EQUAL(coins.mapCoinsWritten[outpoint2].peginSpent, false); | ||
BOOST_CHECK_EQUAL(coins.mapCoinsWritten[outpoint3].flags, CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH | CCoinsCacheEntry::PEGIN); | ||
BOOST_CHECK_EQUAL(coins.mapCoinsWritten[outpoint3].peginSpent, true); | ||
BOOST_CHECK_EQUAL(coins.mapCoinsWritten[outpoint4].flags, CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH | CCoinsCacheEntry::PEGIN); | ||
BOOST_CHECK_EQUAL(coins.mapCoinsWritten[outpoint3].peginSpent, true); | ||
|
||
// CCoinsViewCache should lose outpoint2 in BatchWrite logic | ||
CCoinsViewTester coins2; | ||
CCoinsViewCache coinsCache2(&coins2); | ||
BOOST_CHECK(coinsCache2.BatchWrite(coins.mapCoinsWritten, uint256())); | ||
coinsCache2.Flush(); | ||
BOOST_CHECK_EQUAL(coins2.mapCoinsWritten.size(), 3); | ||
BOOST_CHECK_EQUAL(coins2.mapCoinsWritten[outpoint].flags, CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH | CCoinsCacheEntry::PEGIN); | ||
BOOST_CHECK_EQUAL(coins2.mapCoinsWritten[outpoint].peginSpent, true); | ||
BOOST_CHECK_EQUAL(coins2.mapCoinsWritten[outpoint3].flags, CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH | CCoinsCacheEntry::PEGIN); | ||
BOOST_CHECK_EQUAL(coins2.mapCoinsWritten[outpoint3].peginSpent, true); | ||
BOOST_CHECK_EQUAL(coins2.mapCoinsWritten[outpoint4].flags, CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH | CCoinsCacheEntry::PEGIN); | ||
BOOST_CHECK_EQUAL(coins2.mapCoinsWritten[outpoint4].peginSpent, true); | ||
} | ||
|
||
BOOST_AUTO_TEST_SUITE_END() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
// Copyright (c) 2017-2017 Blockstream | ||
// Distributed under the MIT software license, see the accompanying | ||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
|
||
#include <clientversion.h> | ||
#include <chainparams.h> | ||
#include <checkqueue.h> | ||
#include <consensus/tx_verify.h> | ||
#include <consensus/validation.h> | ||
#include <core_io.h> | ||
#include <validation.h> // For CheckTransaction | ||
#include <pegins.h> | ||
#include <policy/policy.h> | ||
#include <script/script.h> | ||
#include <script/script_error.h> | ||
#include <utilstrencodings.h> | ||
#include <validation.h> | ||
#include <streams.h> | ||
#include <test/test_bitcoin.h> | ||
#include <util.h> | ||
|
||
#include <boost/algorithm/string/classification.hpp> | ||
#include <boost/algorithm/string/split.hpp> | ||
#include <boost/test/unit_test.hpp> | ||
|
||
std::vector<std::vector<unsigned char> > witness_stack = { | ||
ParseHex("00ca9a3b00000000"), | ||
ParseHex("e48a1a02a8f799892fda58347c2d794144311d4307dbfd10f77ffe28088c60be"), | ||
ParseHex("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f"), | ||
ParseHex("00141eef6361cd1507a303834285d1521d6baf1b19ae"), | ||
ParseHex("0200000001b399292c8100b8a1b66eb23896f799c1712390d560af0f70e81acd2d17a3b06e0000000049483045022100c3c749623486ea57ea93dfaf78d85590d78c7590a25768fe80f0ea4d6047419002202a0a00a90392b86c53c0fdda908c4591ba28040c16c25734c23b7df3c8b70acd01feffffff0228196bee000000001976a914470dd41542ee1a1bd75f1a838878648c8d65622488ac00ca9a3b0000000017a914cb60b1d7f76ba12b45a116c482c165a74c5d7e388765000000"), | ||
ParseHex("000000205e3913a320cd2e3a2efa141e47419f54cb9e82320cf8dbc812fc19b9a1b2413a57f5e9fb4fa22de191454a241387f5d10cc794ee0fbf72ae2841baf3129a4eab8133025affff7f20000000000200000002f9d0be670007d38fceece999cb6144658a99c307ccc37f6d8f69129ed0f4545ff321df9790633bc33c67239c4174df8142ee616ee6a2e2788fe4820fe70e9bce0105") | ||
}; | ||
|
||
//std::vector<unsigned char> pegin_transaction = ParseHex("020000000101f321df9790633bc33c67239c4174df8142ee616ee6a2e2788fe4820fe70e9bce0100004000ffffffff0201e48a1a02a8f799892fda58347c2d794144311d4307dbfd10f77ffe28088c60be01000000003b9ab2e0001976a914809326f7628dc976fbe63806479a1b8dfcc8c4b988ac01e48a1a02a8f799892fda58347c2d794144311d4307dbfd10f77ffe28088c60be010000000000001720000000000000000002483045022100ae17064745d80650a6a5cbcbe15c8c45ba498d1c6f45a7c0f5f32d871b463fc60220799f2836471702c21f7cfe124651727b530ad41f7af4dc213c65f5030a2f6fc4012103a9d3c6c7c161a565a76113632fe13330cf2c0207ba79a76d1154cdc3cb94d940060800ca9a3b0000000020e48a1a02a8f799892fda58347c2d794144311d4307dbfd10f77ffe28088c60be2006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f1600141eef6361cd1507a303834285d1521d6baf1b19aebe0200000001b399292c8100b8a1b66eb23896f799c1712390d560af0f70e81acd2d17a3b06e0000000049483045022100c3c749623486ea57ea93dfaf78d85590d78c7590a25768fe80f0ea4d6047419002202a0a00a90392b86c53c0fdda908c4591ba28040c16c25734c23b7df3c8b70acd01feffffff0228196bee000000001976a914470dd41542ee1a1bd75f1a838878648c8d65622488ac00ca9a3b0000000017a914cb60b1d7f76ba12b45a116c482c165a74c5d7e38876500000097000000205e3913a320cd2e3a2efa141e47419f54cb9e82320cf8dbc812fc19b9a1b2413a57f5e9fb4fa22de191454a241387f5d10cc794ee0fbf72ae2841baf3129a4eab8133025affff7f20000000000200000002f9d0be670007d38fceece999cb6144658a99c307ccc37f6d8f69129ed0f4545ff321df9790633bc33c67239c4174df8142ee616ee6a2e2788fe4820fe70e9bce010500000000"); | ||
std::vector<unsigned char> pegin_transaction = ParseHex("02000000000101f321df9790633bc33c67239c4174df8142ee616ee6a2e2788fe4820fe70e9bce0100004000ffffffff02e0b29a3b000000001976a914809326f7628dc976fbe63806479a1b8dfcc8c4b988ac20170000000000000002483045022100ae17064745d80650a6a5cbcbe15c8c45ba498d1c6f45a7c0f5f32d871b463fc60220799f2836471702c21f7cfe124651727b530ad41f7af4dc213c65f5030a2f6fc4012103a9d3c6c7c161a565a76113632fe13330cf2c0207ba79a76d1154cdc3cb94d940060800ca9a3b0000000020e48a1a02a8f799892fda58347c2d794144311d4307dbfd10f77ffe28088c60be2006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f1600141eef6361cd1507a303834285d1521d6baf1b19aebe0200000001b399292c8100b8a1b66eb23896f799c1712390d560af0f70e81acd2d17a3b06e0000000049483045022100c3c749623486ea57ea93dfaf78d85590d78c7590a25768fe80f0ea4d6047419002202a0a00a90392b86c53c0fdda908c4591ba28040c16c25734c23b7df3c8b70acd01feffffff0228196bee000000001976a914470dd41542ee1a1bd75f1a838878648c8d65622488ac00ca9a3b0000000017a914cb60b1d7f76ba12b45a116c482c165a74c5d7e38876500000097000000205e3913a320cd2e3a2efa141e47419f54cb9e82320cf8dbc812fc19b9a1b2413a57f5e9fb4fa22de191454a241387f5d10cc794ee0fbf72ae2841baf3129a4eab8133025affff7f20000000000200000002f9d0be670007d38fceece999cb6144658a99c307ccc37f6d8f69129ed0f4545ff321df9790633bc33c67239c4174df8142ee616ee6a2e2788fe4820fe70e9bce010500000000"); | ||
|
||
COutPoint prevout(uint256S("ce9b0ee70f82e48f78e2a2e66e61ee4281df74419c23673cc33b639097df21f3"), 1); | ||
|
||
// Needed for easier parent PoW check, and setting fedpegscript | ||
struct RegtestingSetup : public TestingSetup { | ||
RegtestingSetup() : TestingSetup("custom", "512103dff4923d778550cc13ce0d887d737553b4b58f4e8e886507fc39f5e447b2186451ae") {} | ||
}; | ||
|
||
BOOST_FIXTURE_TEST_SUITE(pegin_witness_tests, RegtestingSetup) | ||
|
||
BOOST_AUTO_TEST_CASE(witness_valid) | ||
{ | ||
CScriptWitness witness; | ||
witness.stack = witness_stack; | ||
|
||
BOOST_CHECK(IsValidPeginWitness(witness, prevout)); | ||
|
||
// Missing byte on each field to make claim ill-formatted | ||
// This will break deserialization and other data-matching checks | ||
for (unsigned int i = 0; i < witness.stack.size(); i++) { | ||
//TODO(rebase) CA remove this exception | ||
if (i == 1) { | ||
continue; | ||
} | ||
witness.stack[i].pop_back(); | ||
BOOST_CHECK(!IsValidPeginWitness(witness, prevout)); | ||
witness.stack = witness_stack; | ||
BOOST_CHECK(IsValidPeginWitness(witness, prevout)); | ||
} | ||
|
||
// Test mismatched but valid nOut to proof | ||
COutPoint fake_prevout = prevout; | ||
fake_prevout.n = 0; | ||
BOOST_CHECK(!IsValidPeginWitness(witness, fake_prevout)); | ||
|
||
// Test mistmatched but valid txid | ||
fake_prevout = prevout; | ||
fake_prevout.hash = uint256S("2f103ee04a5649eecb932b4da4ca9977f53a12bbe04d9d1eb5ccc0f4a06334"); | ||
BOOST_CHECK(!IsValidPeginWitness(witness, fake_prevout)); | ||
|
||
// Ensure that all witness stack sizes are handled | ||
BOOST_CHECK(IsValidPeginWitness(witness, prevout)); | ||
for (unsigned int i = 0; i < witness.stack.size(); i++) { | ||
witness.stack.pop_back(); | ||
BOOST_CHECK(!IsValidPeginWitness(witness, prevout)); | ||
} | ||
witness.stack = witness_stack; | ||
|
||
// Extra element causes failure | ||
witness.stack.push_back(witness.stack.back()); | ||
BOOST_CHECK(!IsValidPeginWitness(witness, prevout)); | ||
witness.stack = witness_stack; | ||
|
||
// Check validation of peg-in transaction's inputs and balance | ||
CDataStream ssTx(pegin_transaction, SER_NETWORK, PROTOCOL_VERSION); | ||
CTransactionRef txRef; | ||
ssTx >> txRef; | ||
CTransaction tx(*txRef); | ||
|
||
// Only one(valid) input witness should exist, and should match | ||
BOOST_CHECK(tx.vin[0].m_pegin_witness.stack == witness_stack); | ||
BOOST_CHECK(tx.vin[0].m_is_pegin); | ||
// Check that serialization doesn't cause issuance to become non-null | ||
//TODO(rebase) CA | ||
//BOOST_CHECK(tx.vin[0].assetIssuance.IsNull()); | ||
BOOST_CHECK(IsValidPeginWitness(tx.vin[0].m_pegin_witness, prevout)); | ||
|
||
std::set<std::pair<uint256, COutPoint> > setPeginsSpent; | ||
CValidationState state; | ||
CCoinsView coinsDummy; | ||
CCoinsViewCache coins(&coinsDummy); | ||
CAmount txfee; | ||
BOOST_CHECK(Consensus::CheckTxInputs(tx, state, coins, 0, txfee, setPeginsSpent)); | ||
BOOST_CHECK(setPeginsSpent.size() == 1); | ||
setPeginsSpent.clear(); | ||
|
||
// Strip pegin_witness | ||
CMutableTransaction mtxn(tx); | ||
mtxn.vin[0].m_pegin_witness.SetNull(); | ||
CTransaction tx2(mtxn); | ||
BOOST_CHECK(!Consensus::CheckTxInputs(tx2, state, coins, 0, txfee, setPeginsSpent)); | ||
BOOST_CHECK(setPeginsSpent.empty()); | ||
|
||
// Invalidate peg-in (and spending) authorization by pegin marker. | ||
// This only checks for peg-in authorization, with the only input marked | ||
// as m_is_pegin | ||
CMutableTransaction mtxn2(tx); | ||
mtxn2.vin[0].m_is_pegin = false; | ||
CTransaction tx3(mtxn2); | ||
BOOST_CHECK(!Consensus::CheckTxInputs(tx3, state, coins, 0, txfee, setPeginsSpent)); | ||
BOOST_CHECK(setPeginsSpent.empty()); | ||
|
||
|
||
// TODO Test mixed pegin/non-pegin input case | ||
// TODO Test spending authorization in conjunction with valid witness program in pegin auth | ||
|
||
} | ||
|
||
BOOST_AUTO_TEST_SUITE_END() | ||
|