Commit 92c2d06f authored by Daniel Kraft's avatar Daniel Kraft
Browse files

Add support for "one-shot" naming.

This is, basically, the patch developed also for Isracoin with only
minor modifications.  Adaption and testing still needs to be done.
Showing with 1144 additions and 20 deletions
+1144 -20
......@@ -49,6 +49,7 @@ BITCOIN_CORE_H = \
main.h \
miner.h \
mruset.h \
names.h \
netbase.h \
net.h \
noui.h \
......@@ -98,11 +99,13 @@ libbitcoin_server_a_SOURCES = \
leveldbwrapper.cpp \
main.cpp \
miner.cpp \
names.cpp \
net.cpp \
noui.cpp \
rpcblockchain.cpp \
rpcmining.cpp \
rpcmisc.cpp \
rpcnames.cpp \
rpcnet.cpp \
rpcrawtransaction.cpp \
rpcserver.cpp \
......
......@@ -39,6 +39,8 @@ public:
bnProofOfWorkLimit = CBigNum(~uint256(0) >> 32);
nSubsidyHalvingInterval = 2100000;
nAuxpowStartHeight = 453273;
/* TODO: Decide about fork height. */
namesForkHeight = 1000000;
// Build the genesis block. Note that the output of the genesis coinbase cannot
// be spent as it did not originally exist in the database.
......@@ -123,6 +125,8 @@ public:
nRPCPort = 19341;
nAuxpowStartHeight = 453273;
strDataDir = "testnet3";
/* TODO: Decide about fork height. */
namesForkHeight = 1000000;
// Modify the testnet genesis block so the timestamp is valid for a later start.
genesis.nTime = 1405338325;
......@@ -165,11 +169,17 @@ public:
nDefaultPort = 19444;
nRPCPort = 19445;
nAuxpowStartHeight = 250;
namesForkHeight = 250;
strDataDir = "regtest";
hashGenesisBlock = genesis.GetHash();
assert(hashGenesisBlock == uint256("0x231de73ec08234a4adff3c71e57271a13fa73f5ae1ca6b0ded89275e557a6207"));
/* Fork height for names: The regtest framework constructs initial
chains of height 200. Use 250 here so that we can test both
before and after the fork. */
namesForkHeight = 250;
vSeeds.clear(); // Regtest mode doesn't have any DNS seeds.
}
......
......@@ -68,6 +68,14 @@ public:
int RPCPort() const { return nRPCPort; }
int AuxpowChainId() const { return 20; }
int AuxpowStartHeight() const { return nAuxpowStartHeight; }
/* Height at which names are enabled as a softfork. */
inline int
GetNamesForkHeight() const
{
return namesForkHeight;
}
protected:
CChainParams() {}
......@@ -83,6 +91,7 @@ protected:
string strDataDir;
vector<CDNSSeedData> vSeeds;
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
int namesForkHeight;
};
/**
......
// Copyright (c) 2012-2013 The Bitcoin developers
// Copyright (c) 2012-2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
......@@ -55,7 +55,10 @@ bool CCoinsView::SetCoins(const uint256 &txid, const CCoins &coins) { return fal
bool CCoinsView::HaveCoins(const uint256 &txid) { return false; }
uint256 CCoinsView::GetBestBlock() { return uint256(0); }
bool CCoinsView::SetBestBlock(const uint256 &hashBlock) { return false; }
bool CCoinsView::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock) { return false; }
bool CCoinsView::GetName (const CName& name, CNameData& data) const { return false; }
bool CCoinsView::SetName (const CName& name, const CNameData& data) { return false; }
bool CCoinsView::DeleteName (const CName& name) { return false; }
bool CCoinsView::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock, const CNameCache& names) { return false; }
bool CCoinsView::GetStats(CCoinsStats &stats) { return false; }
......@@ -65,8 +68,11 @@ bool CCoinsViewBacked::SetCoins(const uint256 &txid, const CCoins &coins) { retu
bool CCoinsViewBacked::HaveCoins(const uint256 &txid) { return base->HaveCoins(txid); }
uint256 CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); }
bool CCoinsViewBacked::SetBestBlock(const uint256 &hashBlock) { return base->SetBestBlock(hashBlock); }
bool CCoinsViewBacked::GetName (const CName& name, CNameData& data) const { return base->GetName (name, data); }
bool CCoinsViewBacked::SetName (const CName& name, const CNameData& data) { return base->SetName (name, data); }
bool CCoinsViewBacked::DeleteName (const CName& name) { return base->DeleteName (name); }
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
bool CCoinsViewBacked::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); }
bool CCoinsViewBacked::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock, const CNameCache& names) { return base->BatchWrite(mapCoins, hashBlock, names); }
bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); }
CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), hashBlock(0) { }
......@@ -121,21 +127,48 @@ bool CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
return true;
}
bool CCoinsViewCache::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlockIn) {
bool CCoinsViewCache::GetName (const CName& name, CNameData& data) const {
if (cacheNames.IsDeleted (name))
return false;
if (cacheNames.Get (name, data))
return true;
/* Note: This does not attempt to cache name queries. The cache
only keeps track of changes! */
return base->GetName (name, data);
}
bool CCoinsViewCache::SetName (const CName& name, const CNameData& data) {
cacheNames.Set (name, data);
return true;
}
bool CCoinsViewCache::DeleteName (const CName& name) {
cacheNames.Delete (name);
return true;
}
bool CCoinsViewCache::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlockIn, const CNameCache& names) {
for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++)
cacheCoins[it->first] = it->second;
hashBlock = hashBlockIn;
cacheNames.Apply (names);
return true;
}
bool CCoinsViewCache::Flush() {
bool fOk = base->BatchWrite(cacheCoins, hashBlock);
bool fOk = base->BatchWrite(cacheCoins, hashBlock, cacheNames);
if (fOk)
{
cacheCoins.clear();
cacheNames.Clear();
}
return fOk;
}
unsigned int CCoinsViewCache::GetCacheSize() {
// Do not take name operations into account here.
return cacheCoins.size();
}
......
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2013 The Bitcoin developers
// Copyright (c) 2009-2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_COINS_H
#define BITCOIN_COINS_H
#include "core.h"
#include "names.h"
#include "serialize.h"
#include "uint256.h"
......@@ -274,8 +275,16 @@ public:
// Modify the currently active block hash
virtual bool SetBestBlock(const uint256 &hashBlock);
// Do a bulk modification (multiple SetCoins + one SetBestBlock)
virtual bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock);
// Get a name (if it exists)
virtual bool GetName (const CName& name, CNameData& data) const;
// Set a name (or add it if not exists)
virtual bool SetName (const CName& name, const CNameData& data);
// Delete a name.
virtual bool DeleteName (const CName& name);
// Do a bulk modification (multiple SetCoins, one SetBestBlock
// and name updates)
virtual bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock, const CNameCache& names);
// Calculate statistics about the unspent transaction output set
virtual bool GetStats(CCoinsStats &stats);
......@@ -298,8 +307,11 @@ public:
bool HaveCoins(const uint256 &txid);
uint256 GetBestBlock();
bool SetBestBlock(const uint256 &hashBlock);
bool GetName (const CName& name, CNameData& data) const;
bool SetName (const CName& name, const CNameData& data);
bool DeleteName (const CName& name);
void SetBackend(CCoinsView &viewIn);
bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock);
bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock, const CNameCache& names);
bool GetStats(CCoinsStats &stats);
};
......@@ -310,6 +322,7 @@ class CCoinsViewCache : public CCoinsViewBacked
protected:
uint256 hashBlock;
std::map<uint256,CCoins> cacheCoins;
CNameCache cacheNames;
public:
CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false);
......@@ -320,7 +333,10 @@ public:
bool HaveCoins(const uint256 &txid);
uint256 GetBestBlock();
bool SetBestBlock(const uint256 &hashBlock);
bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock);
bool GetName (const CName& name, CNameData& data) const;
bool SetName (const CName& name, const CNameData& data);
bool DeleteName (const CName& name);
bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock, const CNameCache& names);
// Return a modifiable reference to a CCoins. Check HaveCoins first.
// Many methods explicitly require a CCoinsViewCache because of this method, to reduce
......@@ -332,6 +348,7 @@ public:
bool Flush();
// Calculate the size of the cache (in number of transactions)
// This doesn't take name operations into account.
unsigned int GetCacheSize();
/** Amount of bitcoins coming in to a transaction
......
......@@ -82,7 +82,7 @@ public:
CLevelDBWrapper(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory = false, bool fWipe = false);
~CLevelDBWrapper();
template<typename K, typename V> bool Read(const K& key, V& value) throw(leveldb_error) {
template<typename K, typename V> bool Read(const K& key, V& value) const throw(leveldb_error) {
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(ssKey.GetSerializeSize(key));
ssKey << key;
......@@ -111,7 +111,7 @@ public:
return WriteBatch(batch, fSync);
}
template<typename K> bool Exists(const K& key) throw(leveldb_error) {
template<typename K> bool Exists(const K& key) const throw(leveldb_error) {
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(ssKey.GetSerializeSize(key));
ssKey << key;
......
......@@ -403,6 +403,17 @@ CBlockIndex *CChain::FindFork(const CBlockLocator &locator) const {
CCoinsViewCache *pcoinsTip = NULL;
CBlockTreeDB *pblocktree = NULL;
//////////////////////////////////////////////////////////////////////////////
//
// CBlockUndo
//
CBlockUndo::CBlockUndo (const CBlockIndex* pindex)
: vtxundo(), names()
{
supportsNames = (pindex->nHeight >= Params ().GetNamesForkHeight ());
}
//////////////////////////////////////////////////////////////////////////////
//
// mapOrphanTransactions
......@@ -895,12 +906,19 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
hash.ToString(),
nFees, CTransaction::nMinRelayTxFee * 10000);
/* Check that there's not already a pending name operation. */
if (!pool.names.checkTransaction (tx))
return error ("AcceptToMemoryPool: already have an operation on"
" the same name");
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC))
{
const unsigned checkFlags = SCRIPT_VERIFY_P2SH
| SCRIPT_VERIFY_STRICTENC
| SCRIPT_VERIFY_NAMES;
if (!CheckInputs (tx, state, view, true, checkFlags))
return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString());
}
// Store transaction in memory
pool.addUnchecked(hash, entry);
}
......@@ -1562,6 +1580,15 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach
}
}
/* Check name operations if the flag is set. */
if (flags & SCRIPT_VERIFY_NAMES)
for (std::vector<CTxOut>::const_iterator out = tx.vout.begin ();
out != tx.vout.end (); ++out)
{
if (!CheckNameOperation (*out, inputs, state))
return error ("CheckInputs: name operation is invalid");
}
return true;
}
......@@ -1576,7 +1603,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
bool fClean = true;
CBlockUndo blockUndo;
CBlockUndo blockUndo(pindex);
CDiskBlockPos pos = pindex->GetUndoPos();
if (pos.IsNull())
return error("DisconnectBlock() : no undo data available");
......@@ -1644,6 +1671,10 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
}
}
/* Undo name operations. */
if (!blockUndo.names.applyUndo (view))
return error ("DisconnectBlock: failed to undo name operations");
// move best block pointer to prevout block
view.SetBestBlock(pindex->pprev->GetBlockHash());
......@@ -1694,6 +1725,15 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
if (!CheckBlock(block, state, !fJustCheck, !fJustCheck))
return false;
/* Check that no names appear multiple times in an operation
in the single block. This needs the current height (since we only
do this check after the softfork), thus it can't be part of
the initial CheckBlock routine. */
const int namesForkHeight = Params ().GetNamesForkHeight ();
const bool considerNames = (pindex->nHeight >= namesForkHeight);
if (considerNames && !CheckNamesInBlock (block, state))
return error ("CheckBlock: CheckNamesInBlock failed");
// verify that the view's current state corresponds to the previous block
uint256 hashPrevBlock = pindex->pprev == NULL ? uint256(0) : pindex->pprev->GetBlockHash();
assert(hashPrevBlock == view.GetBestBlock());
......@@ -1737,8 +1777,10 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
unsigned int flags = SCRIPT_VERIFY_NOCACHE |
(fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE);
if (considerNames)
flags |= SCRIPT_VERIFY_NAMES;
CBlockUndo blockundo;
CBlockUndo blockundo(pindex);
CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL);
......@@ -1789,6 +1831,14 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
if (!tx.IsCoinBase())
blockundo.vtxundo.push_back(txundo);
/* Update the name database for all name operations found. */
if (considerNames)
for (std::vector<CTxOut>::const_iterator out = tx.vout.begin ();
out != tx.vout.end (); ++out)
{
ApplyNameOperation (*out, view, blockundo.names, state);
}
vPos.push_back(std::make_pair(block.GetTxHash(i), pos));
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
}
......@@ -2901,7 +2951,7 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth)
return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
// check level 2: verify undo validity
if (nCheckLevel >= 2 && pindex) {
CBlockUndo undo;
CBlockUndo undo(pindex);
CDiskBlockPos pos = pindex->GetUndoPos();
if (!pos.IsNull()) {
if (!undo.ReadFromDisk(pos, pindex->pprev->GetBlockHash()))
......
......@@ -14,6 +14,7 @@
#include "chainparams.h"
#include "coins.h"
#include "core.h"
#include "names.h"
#include "net.h"
#include "script.h"
#include "sync.h"
......@@ -331,8 +332,27 @@ class CBlockUndo
public:
std::vector<CTxUndo> vtxundo; // for all but the coinbase
/**
* Determine whether or not to read/write the names information.
* This flag is set depending on whether the block this corresponds to
* is before or after the fork point.
*/
bool supportsNames;
/** Undo information for names. */
CNameUndo names;
/* Construct the undo object empty. It uses the CBlockIndex object
(and in particular the block height) to initialise the
"supportsNames" field appropriately. */
explicit CBlockUndo (const CBlockIndex* pindex);
IMPLEMENT_SERIALIZE(
READWRITE(vtxundo);
if (supportsNames)
READWRITE (names);
else
assert (names.IsNull ());
)
bool WriteToDisk(CDiskBlockPos &pos, const uint256 &hashBlock)
......
......@@ -280,7 +280,8 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
continue;
CValidationState state;
if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH))
if (!CheckInputs (tx, state, view, true,
SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_NAMES))
continue;
CTxUndo txundo;
......
// Copyright (c) 2014-2015 Daniel Kraft
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "names.h"
#include "leveldbwrapper.h"
#include "main.h"
#include "util.h"
#include <assert.h>
/* Construct a name from a string. */
CName
NameFromString (const std::string& str)
{
const unsigned char* strPtr;
strPtr = reinterpret_cast<const unsigned char*> (str.c_str ());
return CName(strPtr, strPtr + str.size ());
}
/* Convert a name to a string. */
std::string
NameToString (const CName& name)
{
return std::string (name.begin (), name.end ());
}
/* Return the required (minimum) cost of a name registration. */
int64_t
GetNameCost (const CName& name)
{
const vchType::size_type len = name.size ();
/* TODO: Decide about real cost schedule. */
if (len < 3)
return -1;
return COIN;
}
/* ************************************************************************** */
/* CNameCache. */
/* Try to get a name's associated data. This looks only
in entries, and doesn't care about deleted data. */
bool
CNameCache::Get (const CName& name, CNameData& data) const
{
const std::map<CName, CNameData>::const_iterator i = entries.find (name);
if (i == entries.end ())
return false;
data = i->second;
return true;
}
/* Insert (or update) a name. If it is marked as "deleted", this also
removes the "deleted" mark. */
void
CNameCache::Set (const CName& name, const CNameData& data)
{
const std::set<CName>::iterator di = deleted.find (name);
if (di != deleted.end ())
deleted.erase (di);
const std::map<CName, CNameData>::iterator ei = entries.find (name);
if (ei != entries.end ())
ei->second = data;
else
entries.insert (std::make_pair (name, data));
}
/* Delete a name. If it is in the "entries" set also, remove it there. */
void
CNameCache::Delete (const CName& name)
{
const std::map<CName, CNameData>::iterator ei = entries.find (name);
if (ei != entries.end ())
entries.erase (ei);
deleted.insert (name);
}
/* Apply all the changes in the passed-in record on top of this one. */
void
CNameCache::Apply (const CNameCache& cache)
{
for (std::map<CName, CNameData>::const_iterator i = cache.entries.begin ();
i != cache.entries.end (); ++i)
Set (i->first, i->second);
for (std::set<CName>::const_iterator i = cache.deleted.begin ();
i != cache.deleted.end (); ++i)
Delete (*i);
}
/* Write all cached changes to a database batch update object. */
void
CNameCache::WriteBatch (CLevelDBBatch& batch) const
{
for (std::map<CName, CNameData>::const_iterator i = entries.begin ();
i != entries.end (); ++i)
batch.Write (std::make_pair ('n', i->first), i->second);
for (std::set<CName>::const_iterator i = deleted.begin ();
i != deleted.end (); ++i)
batch.Erase (std::make_pair ('n', *i));
}
/* ************************************************************************** */
/* CNameUndo. */
/* Undo everything in here on the given coins view. */
bool
CNameUndo::applyUndo (CCoinsView& view) const
{
for (std::set<CName>::const_iterator i = registrations.begin ();
i != registrations.end (); ++i)
if (!view.DeleteName (*i))
return error ("CNameUndo::applyUndo: failed to delete name '%s'",
NameToString (*i).c_str ());
return true;
}
/* ************************************************************************** */
/* CNameMemPool. */
/* Check if a given new transaction conflicts with the names
already in here. */
bool
CNameMemPool::checkTransaction (const CTransaction& tx) const
{
for (std::vector<CTxOut>::const_iterator out = tx.vout.begin ();
out != tx.vout.end (); ++out)
{
CName name;
if (!IsNameOperation (*out, name))
continue;
if (hasName (name))
return error ("CNameMemPool: name '%s' has a pending operation",
NameToString (name).c_str ());
}
return true;
}
/* Add all names appearing in the given tx. This should only be
called after CheckTransaction has already been fine. */
void
CNameMemPool::addTransaction (const CTransaction& tx)
{
for (std::vector<CTxOut>::const_iterator out = tx.vout.begin ();
out != tx.vout.end (); ++out)
{
CName name;
if (!IsNameOperation (*out, name))
continue;
names.insert (name);
}
}
/* Remove all entries for the given tx. */
void
CNameMemPool::removeTransaction (const CTransaction& tx)
{
for (std::vector<CTxOut>::const_iterator out = tx.vout.begin ();
out != tx.vout.end (); ++out)
{
CName name;
if (!IsNameOperation (*out, name))
continue;
names.erase (name);
}
}
/* ************************************************************************** */
/* Decode a tx output script and see if it is a name operation. This also
checks that the operation is well-formed. If it looks like a name operation
(OP_RETURN OP_NAME_*) but isn't well-formed, it isn't accepted at all
(not just ignored). In that case, fError is set to true. */
bool
DecodeNameScript (const CScript& script, opcodetype& op, CName& name,
std::vector<vchType>& args, bool& fError)
{
CScript::const_iterator pc = script.begin ();
opcodetype cur;
if (!script.GetOp (pc, cur) || cur != OP_RETURN)
{
fError = false;
return false;
}
if (!script.GetOp (pc, op) || op != OP_NAME_REGISTER)
{
fError = false;
return false;
}
/* Get remaining data as arguments. The name itself is also taken care of
as the first argument. */
bool haveName = false;
args.clear ();
while (pc != script.end ())
{
vchType arg;
if (!script.GetOp (pc, cur, arg) || cur < 0 || cur > OP_PUSHDATA4)
{
fError = true;
return error ("fetching name script arguments failed");
}
if (haveName)
args.push_back (arg);
else
{
name = arg;
haveName = true;
}
}
if (!haveName)
{
fError = true;
return error ("no name found in name script");
}
/* For now, only OP_NAME_REGISTER is implemented. Thus verify that the
arguments match what they should be. */
if (args.size () != 1)
{
fError = true;
return error ("wrong argument count for OP_NAME_REGISTER");
}
fError = false;
return true;
}
/* See if a given tx output is a name operation. */
bool
IsNameOperation (const CTxOut& txo, CName& name)
{
opcodetype op;
std::vector<vchType> args;
bool fError;
return DecodeNameScript (txo.scriptPubKey, op, name, args, fError);
}
/* Construct a name registration script. The passed-in script is
overwritten with the constructed one. */
void
ConstructNameRegistration (CScript& out, const CName& name,
const CNameData& data)
{
out = CScript();
out << OP_RETURN << OP_NAME_REGISTER << name
<< static_cast<const vchType&> (data.address);
}
/* "Hook" for basic checking of a block. This looks through all transactions
in it, and verifies that each name is touched at most once by an operation
in the block. This is done as a preparatory step for block validation,
before checking the transactions in detail. */
bool
CheckNamesInBlock (const CBlock& block, CValidationState& state)
{
std::set<CName> names;
for (std::vector<CTransaction>::const_iterator tx = block.vtx.begin ();
tx != block.vtx.end (); ++tx)
for (std::vector<CTxOut>::const_iterator out = tx->vout.begin ();
out != tx->vout.end (); ++out)
{
CName name;
/* Note: Actual checking of the transaction is not done here. So
we don't care about fError, and we don't do anything except keeping
track of the names that appear. */
if (!IsNameOperation (*out, name))
continue;
if (names.count (name) != 0)
return state.Invalid (error ("CheckNamesInBlock: duplicate name '%s'",
NameToString (name).c_str ()));
names.insert (name);
}
return true;
}
/* Check a tx output from the name point-of-view. If it looks like
a name operation, verify that it is valid (taking also the
chain state in coins into account). */
bool
CheckNameOperation (const CTxOut& txo, const CCoinsView& coins,
CValidationState& state)
{
opcodetype op;
CName name;
std::vector<vchType> args;
bool fError;
if (!DecodeNameScript (txo.scriptPubKey, op, name, args, fError))
{
if (fError)
return state.Invalid (error ("CheckNameOperation: decoding of name"
" script returned an error flag"));
return true;
}
/* Currently, only OP_NAME_REGISTER is implemented. */
assert (op == OP_NAME_REGISTER && args.size () == 1);
CNameData data;
if (coins.GetName (name, data))
return state.Invalid (error ("CheckNameOperation: name '%s' exists already",
NameToString (name).c_str ()));
const int64_t cost = GetNameCost (name);
if (cost == -1)
return state.Invalid (error ("CheckNameOperation: name '%s' is invalid",
NameToString (name).c_str ()));
assert (cost >= 0);
if (txo.nValue < GetNameCost (name))
return state.Invalid (error ("CheckNameOperation: not enough coins paid"
" for '%s'",
NameToString (name).c_str ()));
return true;
}
/* If the tx output is a name operation, apply it to the coin view. */
bool
ApplyNameOperation (const CTxOut& txo, CCoinsView& coins, CNameUndo& undo,
CValidationState& state)
{
opcodetype op;
CName name;
std::vector<vchType> args;
bool fError;
if (!DecodeNameScript (txo.scriptPubKey, op, name, args, fError))
return true;
/* Currently, only OP_NAME_REGISTER is implemented. */
assert (op == OP_NAME_REGISTER && args.size () == 1);
CNameData data;
data.address = CScript(args[0].begin (), args[0].end ());
if (!coins.SetName (name, data))
return state.Abort ("ApplyNameOperation: failed to write name");
undo.registrations.insert (name);
return true;
}
// Copyright (c) 2014-2015 Daniel Kraft
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_NAMES_H
#define BITCOIN_NAMES_H
#include "core.h"
#include "serialize.h"
#include <stdint.h>
#include <map>
#include <set>
#include <string>
#include <vector>
class CCoinsView;
class CValidationState;
/* Format of name scripts:
OP_NAME_REGISTER:
OP_RETURN OP_NAME_REGISTER <name> <script>
<name> and <script> are vchType values, where <script> is the
script corresponding to the name's desired address.
*/
class CLevelDBBatch;
/** Type representing a name internally. */
typedef vchType CName;
/** Construct a name from a string. */
CName NameFromString (const std::string& str);
/** Convert a name to a string. */
std::string NameToString (const CName& name);
/**
* Return the required (minimum) cost of a name registration. It may return
* -1 to signify that the name is not allowed to be registered at all.
*/
int64_t GetNameCost (const CName& name);
/**
* Information stored internally for a name. For now, this is just
* the corresponding owner/recipient script.
*/
class CNameData
{
public:
/** The name's ownership / recipient script. */
CScript address;
IMPLEMENT_SERIALIZE
(
READWRITE (address);
)
/* Implement == and != operators. */
friend inline bool
operator== (const CNameData& a, const CNameData& b)
{
return (a.address == b.address);
}
friend inline bool
operator!= (const CNameData& a, const CNameData& b)
{
return !(a == b);
}
};
/**
* Cache / record of updates to the name database. In addition to
* new names (or updates to them), this also keeps track of deleted names
* (when rolling back changes).
*/
class CNameCache
{
public:
/** New or updated names. */
std::map<CName, CNameData> entries;
/** Deleted names. */
std::set<CName> deleted;
CNameCache ()
: entries(), deleted()
{}
inline void
Clear ()
{
entries.clear ();
deleted.clear ();
}
/* See if the given name is marked as deleted. */
inline bool
IsDeleted (const CName& name) const
{
return (deleted.count (name) > 0);
}
/* Try to get a name's associated data. This looks only
in entries, and doesn't care about deleted data. */
bool Get (const CName& name, CNameData& data) const;
/* Insert (or update) a name. If it is marked as "deleted", this also
removes the "deleted" mark. */
void Set (const CName& name, const CNameData& data);
/* Delete a name. If it is in the "entries" set also, remove it there. */
void Delete (const CName& name);
/* Apply all the changes in the passed-in record on top of this one. */
void Apply (const CNameCache& cache);
/* Write all cached changes to a database batch update object. */
void WriteBatch (CLevelDBBatch& batch) const;
};
/**
* Undo object for name operations in a block.
*/
class CNameUndo
{
public:
/**
* Name registrations that have to be undone. These names are simply
* removed from the database when performing the undo.
*/
std::set<CName> registrations;
IMPLEMENT_SERIALIZE
(
/* For future extensions (changing of names), we store also
a "format version". */
int nFormat = 1;
READWRITE (nFormat);
assert (nFormat == 1);
READWRITE (registrations);
)
/* Check if this object is "empty". This is enforced when writing
the undo information before the hardfork point (so that we ensure
that no information is lost by *not* actually writing the object
before that). */
inline bool
IsNull () const
{
return registrations.empty ();
}
/* Undo everything in here on the given coins view. */
bool applyUndo (CCoinsView& view) const;
};
/**
* "Memory pool" for name operations. This is used by CTxMemPool, and
* makes sure that for each name, only a single tx operating on it
* will ever be held in memory.
*/
class CNameMemPool
{
public:
/* The names that have pending operations in the mempool. */
std::set<CName> names;
inline CNameMemPool ()
: names()
{}
/* See if a name has already a pending operation. */
inline bool
hasName (const CName& name) const
{
return (names.count (name) != 0);
}
/* Check if a given new transaction conflicts with the names
already in here. */
bool checkTransaction (const CTransaction& tx) const;
/* Add all names appearing in the given tx. This should only be
called after CheckTransaction has already been fine. */
void addTransaction (const CTransaction& tx);
/* Remove all entries for the given tx. */
void removeTransaction (const CTransaction& tx);
/* Completely clear. */
inline void
clear ()
{
names.clear ();
}
/* Return number of names in here. This is used by the sanity checks
of CTxMemPool. */
inline unsigned
size () const
{
return names.size ();
}
};
/* Decode a tx output script and see if it is a name operation. This also
checks that the operation is well-formed. If it looks like a name operation
(OP_RETURN OP_NAME_*) but isn't well-formed, it isn't accepted at all
(not just ignored). In that case, fError is set to true. */
bool DecodeNameScript (const CScript& script, opcodetype& op, CName& name,
std::vector<vchType>& args, bool& fError);
/* See if a given tx output is a name operation. */
bool IsNameOperation (const CTxOut& txo, CName& name);
/* Construct a name registration script. The passed-in script is
overwritten with the constructed one. */
void ConstructNameRegistration (CScript& out, const CName& name,
const CNameData& data);
/* "Hook" for basic checking of a block. This looks through all transactions
in it, and verifies that each name is touched at most once by an operation
in the block. This is done as a preparatory step for block validation,
before checking the transactions in detail. */
bool CheckNamesInBlock (const CBlock& block, CValidationState& state);
/* Check a tx output from the name point-of-view. If it looks like
a name operation, verify that it is valid (taking also the
chain state in coins into account). */
bool CheckNameOperation (const CTxOut& txo, const CCoinsView& coins,
CValidationState& state);
/* If the tx output is a name operation, apply it to the coin view. This also
fills in the appropriate undo information. */
bool ApplyNameOperation (const CTxOut& txo, CCoinsView& coins, CNameUndo& undo,
CValidationState& state);
#endif
......@@ -132,6 +132,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
if (strMethod == "getnetworkhashps" && n > 0) ConvertTo<int64_t>(params[0]);
if (strMethod == "getnetworkhashps" && n > 1) ConvertTo<int64_t>(params[1]);
if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
if (strMethod == "sendtoname" && n > 1) ConvertTo<double>(params[1]);
if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<int64_t>(params[1]);
if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<int64_t>(params[1]);
......
// Copyright (c) 2014-2015 Daniel Kraft
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "base58.h"
#include "core.h"
#include "init.h"
#include "main.h"
#include "names.h"
#include "rpcserver.h"
#ifdef ENABLE_WALLET
# include "wallet.h"
#endif /* ENABLE_WALLET? */
#include "json/json_spirit_utils.h"
#include "json/json_spirit_value.h"
#include <sstream>
json_spirit::Value
name_getaddress (const json_spirit::Array& params, bool fHelp)
{
if (fHelp || params.size () != 1)
throw std::runtime_error (
"name_getaddress \"name\"\n"
"\nLook up the address corresponding to the given name."
" It fails if the name doesn't exist or if its associated"
" script cannot be parsed for an address.\n"
"\nResult:\n"
"\"xxxx\" (string) address of the name\n"
"\nExamples:\n"
+ HelpExampleCli ("name_getaddress", "\"myname\"")
+ HelpExampleRpc ("name_getaddress", "\"myname\"")
);
CName name;
CNameData data;
name = NameFromString (params[0].get_str ());
if (!pcoinsTip->GetName (name, data))
{
std::ostringstream msg;
msg << "name not found: '" << NameToString (name) << "'";
throw JSONRPCError(RPC_NAME_NOT_FOUND, msg.str ());
}
CTxDestination dest;
CBitcoinAddress addr;
if (!ExtractDestination (data.address, dest) || !addr.Set (dest))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"destination address cannot be extracted");
return addr.ToString ();
}
#ifdef ENABLE_WALLET
/* Check if there's a pending name operation and throw an appropriate
JSONRPCError if that is the case. */
static void
CheckPendingNameOperation (const CName& name)
{
if (mempool.names.hasName (name))
throw JSONRPCError(RPC_NAME_PENDING_OPERATION,
"there is a pending operation on this name");
}
json_spirit::Value
name_register (const json_spirit::Array& params, bool fHelp)
{
if (fHelp || params.size () != 2)
throw std::runtime_error (
"name_register \"name\" \"address\"\n"
"\nRegister the given name as an alias for the address.\n"
+ HelpRequiringPassphrase () +
"\nArguments:\n"
"1. \"name\" (string) The name to register.\n"
"2. \"amount\" (string) The address to which the name will resolve.\n"
"\nResult:\n"
"\"transactionid\" (string) The transaction id. (view at https://blockchain.info/tx/[transactionid])\n"
"\nExamples:\n"
+ HelpExampleCli ("name_register", "\"myname\" \"i5qPw9kNW6Ce9T2jwMn3vWaRrPWDY8C4G9\"")
+ HelpExampleRpc ("name_register", "\"myname\" \"i5qPw9kNW6Ce9T2jwMn3vWaRrPWDY8C4G9\"")
);
/* Check if we already had the softfork. */
if (chainActive.Height () < Params ().GetNamesForkHeight ())
throw JSONRPCError(RPC_MISC_ERROR,
"names are not yet supported by the chain");
/* Get the name and verify that it is open for registration. */
const CName name = NameFromString (params[0].get_str ());
CNameData data;
if (pcoinsTip->GetName (name, data))
throw JSONRPCError(RPC_NAME_NOT_AVAILABLE, "the name is already taken");
CheckPendingNameOperation (name);
/* Validate the target address and build up the name
registration data we want in the end. */
data = CNameData();
CBitcoinAddress addr(params[1].get_str ());
if (!addr.IsValid ())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "invalid Crowncoin address");
data.address.SetDestination (addr.Get ());
/* Build up the final transaction and send it. */
const int64_t nAmount = GetNameCost (name);
if (nAmount == -1)
throw JSONRPCError(RPC_NAME_INVALID, "this name is not allowed");
assert (nAmount >= 0);
CScript script;
ConstructNameRegistration (script, name, data);
CWalletTx wtx;
EnsureWalletIsUnlocked ();
std::string strError = pwalletMain->SendMoney (script, nAmount, wtx);
if (strError != "")
throw JSONRPCError(RPC_WALLET_ERROR, strError);
return wtx.GetHash ().GetHex ();
}
json_spirit::Value
sendtoname (const json_spirit::Array& params, bool fHelp)
{
if (fHelp || params.size () < 2 || params.size () > 4)
throw std::runtime_error (
"sendtoaddress \"name\" amount ( \"comment\" \"comment-to\" )\n"
"\nSent an amount to the address of a given name. The amount is a"
" real and is rounded to the nearest 0.00000001.\n"
+ HelpRequiringPassphrase () +
"\nArguments:\n"
"1. \"name\" (string, required) The name to send to.\n"
"2. \"amount\" (numeric, required) The amount in ISR to send. eg 100.01\n"
"3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n"
" This is not part of the transaction, just kept in your wallet.\n"
"4. \"comment-to\" (string, optional) A comment to store the name of the person or organization \n"
" to which you're sending the transaction. This is not part of the \n"
" transaction, just kept in your wallet.\n"
"\nResult:\n"
"\"transactionid\" (string) The transaction id. (view at https://blockchain.info/tx/[transactionid])\n"
"\nExamples:\n"
+ HelpExampleCli ("sendtoname", "\"myname\" 0.1")
+ HelpExampleCli ("sendtoname", "\"myname\" 0.1 \"donation\" \"seans outpost\"")
+ HelpExampleRpc ("sendtoname", "\"myname\", 0.1, \"donation\", \"seans outpost\"")
);
/* Extract destination script from name database. */
CName name;
CNameData data;
name = NameFromString (params[0].get_str ());
if (!pcoinsTip->GetName (name, data))
{
std::ostringstream msg;
msg << "name not found: '" << NameToString (name) << "'";
throw JSONRPCError (RPC_NAME_NOT_FOUND, msg.str ());
}
/* Amount and wallet comments, just as in "sendtoaddress". */
const int64_t nAmount = AmountFromValue (params[1]);
CWalletTx wtx;
if (params.size () > 2 && params[2].type () != json_spirit::null_type
&& !params[2].get_str ().empty ())
wtx.mapValue["comment"] = params[2].get_str ();
if (params.size () > 3 && params[3].type () != json_spirit::null_type
&& !params[3].get_str ().empty ())
wtx.mapValue["to"] = params[3].get_str ();
/* Perform the send. */
EnsureWalletIsUnlocked ();
std::string strError = pwalletMain->SendMoney (data.address, nAmount, wtx);
if (strError != "")
throw JSONRPCError(RPC_WALLET_ERROR, strError);
return wtx.GetHash ().GetHex ();
}
#endif /* ENABLE_WALLET? */
......@@ -69,6 +69,12 @@ enum RPCErrorCode
RPC_WALLET_WRONG_ENC_STATE = -15, // Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
RPC_WALLET_ENCRYPTION_FAILED = -16, // Failed to encrypt the wallet
RPC_WALLET_ALREADY_UNLOCKED = -17, // Wallet is already unlocked
// Name errors
RPC_NAME_NOT_FOUND = -100, // Name is not in the database
RPC_NAME_NOT_AVAILABLE = -101, // Name is already taken
RPC_NAME_PENDING_OPERATION = -102, // There is already a pending name operation
RPC_NAME_INVALID = -103, // The name is not allowed for registration
};
//
......
......@@ -268,6 +268,13 @@ static const CRPCCommand vRPCCommands[] =
{ "validateaddress", &validateaddress, true, false, false }, /* uses wallet if enabled */
{ "verifymessage", &verifymessage, false, false, false },
/* Names */
{ "name_getaddress", &name_getaddress, true, false, false },
#ifdef ENABLE_WALLET
{ "name_register", &name_register, false, false, true },
{ "sendtoname", &sendtoname, false, false, true },
#endif /* ENABLE_WALLET? */
#ifdef ENABLE_WALLET
/* Wallet */
{ "addmultisigaddress", &addmultisigaddress, false, false, true },
......
......@@ -189,4 +189,8 @@ extern json_spirit::Value gettxoutsetinfo(const json_spirit::Array& params, bool
extern json_spirit::Value gettxout(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value verifychain(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value name_getaddress (const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value name_register (const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value sendtoname (const json_spirit::Array& params, bool fHelp);
#endif
......@@ -82,6 +82,7 @@ const char* GetTxnOutputType(txnouttype t)
case TX_SCRIPTHASH: return "scripthash";
case TX_MULTISIG: return "multisig";
case TX_NULL_DATA: return "nulldata";
case TX_NAME_OPERATION: return "nameop";
}
return NULL;
}
......@@ -1212,6 +1213,9 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
// Empty, provably prunable, data-carrying output
mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA));
mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN));
// Name operations
mTemplates.insert(make_pair(TX_NAME_OPERATION, CScript() << OP_RETURN << OP_NAME_REGISTER << OP_SMALLDATA << OP_SMALLDATA));
}
// Shortcut for pay-to-script-hash, which are more constrained than the other types:
......@@ -1365,6 +1369,7 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash
{
case TX_NONSTANDARD:
case TX_NULL_DATA:
case TX_NAME_OPERATION:
return false;
case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
......@@ -1396,6 +1401,7 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned c
{
case TX_NONSTANDARD:
case TX_NULL_DATA:
case TX_NAME_OPERATION:
return -1;
case TX_PUBKEY:
return 1;
......@@ -1473,6 +1479,7 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
{
case TX_NONSTANDARD:
case TX_NULL_DATA:
case TX_NAME_OPERATION:
return false;
case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
......@@ -1534,7 +1541,7 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto
vector<valtype> vSolutions;
if (!Solver(scriptPubKey, typeRet, vSolutions))
return false;
if (typeRet == TX_NULL_DATA){
if (typeRet == TX_NULL_DATA || typeRet == TX_NAME_OPERATION){
// This is data, not addresses
return false;
}
......@@ -1754,6 +1761,7 @@ static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo,
{
case TX_NONSTANDARD:
case TX_NULL_DATA:
case TX_NAME_OPERATION:
// Don't know anything about this, assume bigger one is correct:
if (sigs1.size() >= sigs2.size())
return PushAll(sigs1);
......
......@@ -25,6 +25,9 @@ class CTransaction;
static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes
static const unsigned int MAX_OP_RETURN_RELAY = 40; // bytes
/** Type used for generic data on the stack. */
typedef std::vector<unsigned char> vchType;
/** Signature hash types/flags */
enum
{
......@@ -42,6 +45,8 @@ enum
SCRIPT_VERIFY_STRICTENC = (1U << 1), // enforce strict conformance to DER and SEC2 for signatures and pubkeys
SCRIPT_VERIFY_EVEN_S = (1U << 2), // enforce even S values in signatures (depends on STRICTENC)
SCRIPT_VERIFY_NOCACHE = (1U << 3), // do not store results in signature cache (but do query it)
SCRIPT_VERIFY_NAMES = (1U << 10), // check name operations?
};
enum txnouttype
......@@ -53,6 +58,7 @@ enum txnouttype
TX_SCRIPTHASH,
TX_MULTISIG,
TX_NULL_DATA,
TX_NAME_OPERATION,
};
class CNoDestination {
......@@ -84,6 +90,7 @@ enum opcodetype
OP_RESERVED = 0x50,
OP_1 = 0x51,
OP_TRUE=OP_1,
OP_NAME_REGISTER=OP_1,
OP_2 = 0x52,
OP_3 = 0x53,
OP_4 = 0x54,
......
......@@ -50,6 +50,7 @@ test_crowncoin_SOURCES = \
miner_tests.cpp \
mruset_tests.cpp \
multisig_tests.cpp \
name_tests.cpp \
netbase_tests.cpp \
pmt_tests.cpp \
rpc_tests.cpp \
......
// Copyright (c) 2014-2015 Daniel Kraft
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "base58.h"
#include "core.h"
#include "main.h"
#include "names.h"
#include <boost/test/unit_test.hpp>
static const char* addrString = "1HbJ5rPgNLJzFMfkKDZSEoRv2ePtAZqh8q";
BOOST_AUTO_TEST_SUITE (name_tests)
BOOST_AUTO_TEST_CASE (name_script_parsing)
{
CNameData data;
CBitcoinAddress addr(addrString);
BOOST_CHECK (addr.IsValid ());
data.address.SetDestination (addr.Get ());
const CName name = NameFromString ("my-cool-name");
CScript script;
ConstructNameRegistration (script, name, data);
opcodetype op;
CName name2;
std::vector<vchType> args;
bool error;
BOOST_CHECK (DecodeNameScript (script, op, name2, args, error));
BOOST_CHECK (!error);
BOOST_CHECK (op == OP_NAME_REGISTER);
BOOST_CHECK (name2 == name);
BOOST_CHECK (args.size () == 1);
BOOST_CHECK (args[0] == data.address);
BOOST_CHECK (!DecodeNameScript (data.address, op, name2, args, error));
BOOST_CHECK (!error);
script = CScript ();
script << OP_RETURN << OP_NAME_REGISTER;
BOOST_CHECK (!DecodeNameScript (script, op, name2, args, error));
BOOST_CHECK (error);
script << name;
BOOST_CHECK (!DecodeNameScript (script, op, name2, args, error));
BOOST_CHECK (error);
script << OP_NOP;
BOOST_CHECK (!DecodeNameScript (script, op, name2, args, error));
BOOST_CHECK (error);
ConstructNameRegistration (script, name, data);
script << OP_NOP;
BOOST_CHECK (!DecodeNameScript (script, op, name2, args, error));
BOOST_CHECK (error);
}
BOOST_AUTO_TEST_CASE (names_in_block)
{
CBlock block;
CValidationState state;
CNameData data;
CBitcoinAddress addr(addrString);
BOOST_CHECK (addr.IsValid ());
data.address.SetDestination (addr.Get ());
const CName name = NameFromString ("my-cool-name");
CTxOut txo;
txo.nValue = GetNameCost (name);
ConstructNameRegistration (txo.scriptPubKey, name, data);
CTransaction tx;
tx.vout.push_back (txo);
block.vtx.push_back (tx);
BOOST_CHECK (CheckNamesInBlock (block, state));
block.vtx.push_back (tx);
BOOST_CHECK (!CheckNamesInBlock (block, state));
}
BOOST_AUTO_TEST_CASE (names_database)
{
const CName name = NameFromString ("database-test-name");
CNameData data, data2;
CBitcoinAddress addr(addrString);
BOOST_CHECK (addr.IsValid ());
data.address.SetDestination (addr.Get ());
CCoinsViewCache& view = *pcoinsTip;
BOOST_CHECK (!view.GetName (name, data2));
BOOST_CHECK (view.SetName (name, data));
BOOST_CHECK (view.GetName (name, data2));
BOOST_CHECK (data == data2);
BOOST_CHECK (view.Flush ());
BOOST_CHECK (view.DeleteName (name));
BOOST_CHECK (!view.GetName (name, data2));
BOOST_CHECK (view.Flush ());
}
BOOST_AUTO_TEST_CASE (name_operations)
{
CCoinsView dummy;
CCoinsViewCache view(dummy);
CNameUndo undo;
const CName nameInvalid = NameFromString ("ab");
BOOST_CHECK (GetNameCost (nameInvalid) == -1);
const CName name = NameFromString ("database-test-name");
CNameData data, data2;
CBitcoinAddress addr(addrString);
BOOST_CHECK (addr.IsValid ());
data.address.SetDestination (addr.Get ());
CValidationState state;
CTxOut txo;
ConstructNameRegistration (txo.scriptPubKey, name, data);
txo.nValue = GetNameCost (name) - 1;
BOOST_CHECK (!CheckNameOperation (txo, view, state));
BOOST_CHECK (undo.IsNull ());
txo.nValue = GetNameCost (name);
BOOST_CHECK (CheckNameOperation (txo, view, state));
BOOST_CHECK (ApplyNameOperation (txo, view, undo, state));
BOOST_CHECK (!undo.IsNull ());
BOOST_CHECK (view.GetName (name, data2));
BOOST_CHECK (data == data2);
BOOST_CHECK (!CheckNameOperation (txo, view, state));
undo.applyUndo (view);
BOOST_CHECK (CheckNameOperation (txo, view, state));
}
BOOST_AUTO_TEST_SUITE_END ()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment