diff --git a/src/Makefile.am b/src/Makefile.am index 58b605e7..1797f237 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -85,7 +85,8 @@ BITCOIN_CORE_H = \ hashgroestl.h \ hashskein.h \ hashqubit.h \ - stealthaddress.h + stealthaddress.h \ + auxpow.h JSON_H = \ json/json_spirit.h \ @@ -125,6 +126,7 @@ libbitcoin_server_a_SOURCES = \ rpcserver.cpp \ txdb.cpp \ txmempool.cpp \ + auxpow.cpp \ $(JSON_H) \ $(BITCOIN_CORE_H) diff --git a/src/core.cpp b/src/core.cpp index 78833e4c..851007da 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -6,6 +6,7 @@ #include "core.h" #include "util.h" +#include "auxpow.h" std::string COutPoint::ToString() const { diff --git a/src/core.h b/src/core.h index cbb35b09..8804f032 100644 --- a/src/core.h +++ b/src/core.h @@ -29,6 +29,8 @@ enum // primary version BLOCK_VERSION_DEFAULT = 2, + BLOCK_VERSION_AUXPOW = (1 << 8), + // algo BLOCK_VERSION_ALGO = (7 << 9), BLOCK_VERSION_SCRYPT = (1 << 9), @@ -37,6 +39,13 @@ enum BLOCK_VERSION_QUBIT = (4 << 9), }; +static const int BLOCK_VERSION_CHAIN_START = (1 << 16); +static const int BLOCK_VERSION_CHAIN_END = (1 << 30); + +static const int AUXPOW_CHAIN_ID = 0x005A; +static const int AUXPOW_START_MAINNET = 9999999; //TODO change me +static const int AUXPOW_START_TESTNET = 1; + inline int GetAlgo(int nVersion) { switch (nVersion & BLOCK_VERSION_ALGO) @@ -402,6 +411,16 @@ public: ) }; +class CAuxPow; + +template +int ReadWriteAuxPow(Stream& s, const boost::shared_ptr& auxpow, int nType, int nVersion, CSerActionSerialize ser_action); + +template +int ReadWriteAuxPow(Stream& s, boost::shared_ptr& auxpow, int nType, int nVersion, CSerActionUnserialize ser_action); + +template +int ReadWriteAuxPow(Stream& s, const boost::shared_ptr& auxpow, int nType, int nVersion, CSerActionGetSerializeSize ser_action); /** Nodes collect new transactions into a block, hash them into a hash tree, * and scan through nonce values to make the block's hash satisfy proof-of-work @@ -410,6 +429,7 @@ public: * in the block is a special one that creates a new coin owned by the creator * of the block. */ + class CBlockHeader { public: @@ -421,6 +441,7 @@ public: unsigned int nTime; unsigned int nBits; unsigned int nNonce; + boost::shared_ptr auxpow; CBlockHeader() { @@ -438,11 +459,12 @@ public: READWRITE(nTime); READWRITE(nBits); READWRITE(nNonce); + nSerSize += ReadWriteAuxPow(s, auxpow, nType, nVersion, ser_action); ) void SetNull() { - nVersion = CBlockHeader::CURRENT_VERSION; + nVersion = CBlockHeader::CURRENT_VERSION | (AUXPOW_CHAIN_ID * BLOCK_VERSION_CHAIN_START); hashPrevBlock = 0; hashMerkleRoot = 0; nTime = 0; @@ -455,6 +477,13 @@ public: return (nBits == 0); } + int GetChainID() const + { + return nVersion / BLOCK_VERSION_CHAIN_START; + } + + void SetAuxPow(CAuxPow* pow); + uint256 GetHash() const; // Note: we use explicitly provided algo instead of the one returned by GetAlgo(), because this can be a block @@ -486,6 +515,8 @@ public: { return (int64_t)nTime; } + + bool CheckProofOfWork(int nHeight) const; }; @@ -531,6 +562,7 @@ public: block.nTime = nTime; block.nBits = nBits; block.nNonce = nNonce; + block.auxpow = auxpow; return block; } diff --git a/src/main.cpp b/src/main.cpp index 4df6ee33..828e9992 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,7 @@ #include "txmempool.h" #include "ui_interface.h" #include "util.h" +#include "auxpow.h" #include @@ -407,6 +408,33 @@ CBlockIndex *CChain::FindFork(const CBlockLocator &locator) const { CCoinsViewCache *pcoinsTip = NULL; CBlockTreeDB *pblocktree = NULL; +// to enable merged mining: +// - set a block from which it will be enabled +// - set a unique chain ID +// each merged minable scrypt_1024_1_1_256 coin should have a different one +// (if two have the same ID, they can't be merge mined together) +int GetAuxPowStartBlock() +{ + if (TestNet()) + return AUXPOW_START_TESTNET; + else + return AUXPOW_START_MAINNET; +} + +void CBlockHeader::SetAuxPow(CAuxPow* pow) +{ + if (pow != NULL) + nVersion |= BLOCK_VERSION_AUXPOW; + else + nVersion &= ~BLOCK_VERSION_AUXPOW; + auxpow.reset(pow); +} + +bool IsAuxPowVersion(int nVersion) +{ + return (nVersion == BLOCK_VERSION_AUXPOW_WITH_AUX || nVersion == BLOCK_VERSION_AUXPOW_WITHOUT_AUX); +} + ////////////////////////////////////////////////////////////////////////////// // // mapOrphanTransactions @@ -1469,6 +1497,47 @@ bool CheckProofOfWork(uint256 hash, unsigned int nBits, int algo) return true; } +bool CBlockHeader::CheckProofOfWork(int nHeight) const +{ + int algo = GetAlgo(); + if ((nHeight >= GetAuxPowStartBlock()) && (algo == ALGO_SHA256D)) + { + // Prevent same work from being submitted twice: + // - this block must have our chain ID + // - parent block must not have the same chain ID (see CAuxPow::Check) + // - index of this chain in chain merkle tree must be pre-determined (see CAuxPow::Check) + if (!TestNet() && nHeight != INT_MAX && GetChainID() != AUXPOW_CHAIN_ID) + return error("CheckProofOfWork() : block (chainID=%d) does not have our chain ID (chainID=%d)", GetChainID(),AUXPOW_CHAIN_ID); + + if (auxpow.get() != NULL) + { + if (!auxpow->Check(GetHash(), GetChainID())) + return error("CheckProofOfWork() : AUX POW is not valid"); + // Check proof of work matches claimed amount + if (!::CheckProofOfWork(auxpow->GetParentBlockHash(algo), nBits, algo)) + return error("CheckProofOfWork() : AUX proof of work failed"); + } + else + { + // Check proof of work matches claimed amount + if (!::CheckProofOfWork(GetPoWHash(algo), nBits, algo)) + return error("CheckProofOfWork() : proof of work failed"); + } + } + else + { + if (auxpow.get() != NULL) + { + return error("CheckProofOfWork() : AUX POW is not allowed at this block"); + } + + // Check if proof of work marches claimed amount + if (!::CheckProofOfWork(GetPoWHash(algo), nBits, algo)) + return error("CheckProofOfWork() : proof of work failed"); + } + return true; +} + // Return maximum amount of blocks that other nodes claim to have int GetNumBlocksOfPeers() { @@ -1627,7 +1696,8 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state } if (!state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; - pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); + //pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); + pblocktree->WriteBlockIndex(*pindex); setBlockIndexValid.erase(pindex); InvalidChainFound(pindex); } @@ -2033,8 +2103,9 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C pindex->nStatus = (pindex->nStatus & ~BLOCK_VALID_MASK) | BLOCK_VALID_SCRIPTS; - CDiskBlockIndex blockindex(pindex); - if (!pblocktree->WriteBlockIndex(blockindex)) + //CDiskBlockIndex blockindex(pindex); + //if (!pblocktree->WriteBlockIndex(blockindex)) + if (!pblocktree->WriteBlockIndex(*pindex)) return state.Abort(_("Failed to write block index")); } @@ -2305,6 +2376,27 @@ bool ActivateBestChain(CValidationState &state) { return true; } +CBlockHeader CBlockIndex::GetBlockHeader() const +{ + CBlockHeader block; + + if (nVersion & BLOCK_VERSION_AUXPOW) { + CDiskBlockIndex diskblockindex; + // auxpow is not in memory, load CDiskBlockHeader + // from database to get it + pblocktree->ReadDiskBlockIndex(*phashBlock, diskblockindex); + block.auxpow = diskblockindex.auxpow; + } + block.nVersion = nVersion; + if (pprev) + block.hashPrevBlock = pprev->GetBlockHash(); + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block; +} + bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos) { // Check for duplicate @@ -2341,7 +2433,10 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA; setBlockIndexValid.insert(pindexNew); - if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew))) + //if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew, block.auxpow)) || !pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew))) + if (!pblocktree->WriteDiskBlockIndex(CDiskBlockIndex(pindexNew, block.auxpow)) || !pblocktree->WriteBlockIndex(*pindexNew)) + /* write both the immutible data (CDiskBlockIndex) and the mutable data (BlockIndex) */ + //if (!pblocktree->WriteDiskBlockIndex(CDiskBlockIndex(pindexNew, block.auxpow)) || !pblocktree->WriteBlockIndex(*pindexNew)) return state.Abort(_("Failed to write block index")); // New best? diff --git a/src/main.h b/src/main.h index 96cda99d..a907172d 100644 --- a/src/main.h +++ b/src/main.h @@ -67,6 +67,14 @@ static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 128; /** Timeout in seconds before considering a block download peer unresponsive. */ static const unsigned int BLOCK_DOWNLOAD_TIMEOUT = 60; +/** AuxPow Block versions for sanity checks. */ +/** bare AuxPoW block version which will be modulated further. */ +static const int BLOCK_VERSION_AUXPOW_BARE = CBlockHeader::CURRENT_VERSION | (AUXPOW_CHAIN_ID * BLOCK_VERSION_CHAIN_START); +/** version when AuxPoW exists on the block */ +static const int BLOCK_VERSION_AUXPOW_WITH_AUX = BLOCK_VERSION_AUXPOW_BARE | BLOCK_VERSION_AUXPOW; +/** version when no AuxPoW exists on the block */ +static const int BLOCK_VERSION_AUXPOW_WITHOUT_AUX = BLOCK_VERSION_AUXPOW_BARE & ~BLOCK_VERSION_AUXPOW; + /** Stealth addresses */ static const int64_t BLOCK_STEALTH_START = 740000; @@ -180,6 +188,12 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev); +/** Determine whether the block version is modulated with auxpow logic */ +bool IsAuxPowVersion(int nVersion); + +/** Get the block at which AuxPoW is enabled for this network */ +int GetAuxPowStartBlock(); + /** Create a new block index entry for a given block hash */ CBlockIndex * InsertBlockIndex(uint256 hash); /** Verify a signature */ @@ -705,6 +719,8 @@ const int64_t nBlockTimeWarpPreventStart3 = 1048320; // block where time warp 3 * candidates to be the next block. A blockindex may have multiple pprev pointing * to it, but at most one of them can be part of the currently active branch. */ +class CDiskBlockIndex; + class CBlockIndex { public: @@ -797,6 +813,22 @@ public: nNonce = block.nNonce; } + IMPLEMENT_SERIALIZE + ( + /* mutable stuff goes here, immutable stuff + * has SERIALIZE functions in CDiskBlockIndex */ + if (!(nType & SER_GETHASH)) + READWRITE(VARINT(nVersion)); + + READWRITE(VARINT(nStatus)); + if (nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO)) + READWRITE(VARINT(nFile)); + if (nStatus & BLOCK_HAVE_DATA) + READWRITE(VARINT(nDataPos)); + if (nStatus & BLOCK_HAVE_UNDO) + READWRITE(VARINT(nUndoPos)); + ) + int GetAlgo() const { return ::GetAlgo(nVersion); } CDiskBlockPos GetBlockPos() const { @@ -817,18 +849,7 @@ public: return ret; } - CBlockHeader GetBlockHeader() const - { - CBlockHeader block; - block.nVersion = nVersion; - if (pprev) - block.hashPrevBlock = pprev->GetBlockHash(); - block.hashMerkleRoot = hashMerkleRoot; - block.nTime = nTime; - block.nBits = nBits; - block.nNonce = nNonce; - return block; - } + CBlockHeader GetBlockHeader() const; uint256 GetBlockHash() const { @@ -1030,12 +1051,20 @@ class CDiskBlockIndex : public CBlockIndex public: uint256 hashPrev; + // if this is an aux work block + boost::shared_ptr auxpow; + CDiskBlockIndex() { hashPrev = 0; } - explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex) { + //CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex) { + // hashPrev = (pprev ? pprev->GetBlockHash() : 0); + //} + + explicit CDiskBlockIndex(CBlockIndex* pindex, boost::shared_ptr auxpow) : CBlockIndex(*pindex) { hashPrev = (pprev ? pprev->GetBlockHash() : 0); + this->auxpow = auxpow; } IMPLEMENT_SERIALIZE @@ -1060,6 +1089,7 @@ public: READWRITE(nTime); READWRITE(nBits); READWRITE(nNonce); + ReadWriteAuxPow(s, auxpow, nType, this->nVersion, ser_action); ) uint256 GetBlockHash() const diff --git a/src/miner.cpp b/src/miner.cpp index 735730a2..439db192 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -8,6 +8,7 @@ #include "core.h" #include "main.h" #include "net.h" +#include "auxpow.h" #ifdef ENABLE_WALLET #include "wallet.h" #endif @@ -134,7 +135,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, int algo) error("CreateNewBlock: bad algo"); return NULL; } - + // Create coinbase tx CTransaction txNew; txNew.vin.resize(1); @@ -487,19 +488,39 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) { int algo = pblock->GetAlgo(); uint256 hashPoW = pblock->GetPoWHash(algo); - uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + uint256 hash = pblock->GetHash(); + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - if (hashPoW > hashTarget) - return false; + // LogPrintf("DEBUG| proof-of-work submitted \n algo: %s\nblock-PoWhash: %s\nblock-hash: %s\n ntarget: %s\n", + // algo, + // hashPoW.GetHex(), + // hash.GetHex(), + // hashTarget.GetHex()); + + CAuxPow *auxpow = pblock->auxpow.get(); + + if (auxpow != NULL) { + if (!auxpow->Check(pblock->GetHash(), pblock->GetChainID())) + return error("AUX POW is not valid"); + + if (auxpow->GetParentBlockHash(algo) > hashTarget) + return error("AUX POW parent hash %s is not under target %s", auxpow->GetParentBlockHash(algo).GetHex().c_str(), hashTarget.GetHex().c_str()); + + // print to log + LogPrintf("MyriadMiner: AUX proof-of-work found; our hash: %s ; parent hash: %s ; target: %s\n", + hash.GetHex().c_str(), + auxpow->GetParentBlockHash(algo).GetHex().c_str(), + hashTarget.GetHex().c_str()); + } + else + { + if (hashPoW > hashTarget) + return false; + + // print to log + LogPrintf("MyriadMiner: proof-of-work found; hashPoW: %s ; target: %s\n", hashPoW.GetHex().c_str(), hashTarget.GetHex().c_str()); + } - uint256 hashBlock = pblock->GetHash(); - - //// debug print - LogPrintf("MyriadcoinMiner:\n"); - LogPrintf("proof-of-work found \n block-hash: %s\n pow-hash: %s\ntarget: %s\n", - hashBlock.GetHex(), - hashPoW.GetHex(), - hashTarget.GetHex()); pblock->print(); LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue)); @@ -507,7 +528,7 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) { LOCK(cs_main); if (pblock->hashPrevBlock != chainActive.Tip()->GetBlockHash()) - return error("MyriadcoinMiner : generated block is stale"); + return error("MyriadMiner : generated block is stale"); // Remove key from key pool reservekey.KeepKey(); @@ -521,7 +542,7 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) // Process this block the same as if we had received it from another node CValidationState state; if (!ProcessBlock(state, NULL, pblock)) - return error("MyriadcoinMiner : ProcessBlock, block not accepted"); + return error("MyriadMiner : ProcessBlock, block not accepted"); } return true; @@ -529,7 +550,7 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) void static MinerWaitOnline() { - if (Params().NetworkID() != CChainParams::REGTEST) + if (Params().NetworkID() != CChainParams::REGTEST) { // Busy-wait for the network to come online so we don't waste time mining // on an obsolete chain. In regtest mode we expect to fly solo. @@ -683,7 +704,7 @@ void static ScryptMiner(CWallet *pwallet) while(true) { MinerWaitOnline(); - + // // Create new block // @@ -695,7 +716,7 @@ void static ScryptMiner(CWallet *pwallet) return; CBlock *pblock = &pblocktemplate->block; IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); - + LogPrintf("Running scrypt miner with %u transactions in block (%u bytes)\n", pblock->vtx.size(), ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); @@ -801,7 +822,7 @@ void static ScryptMiner(CWallet *pwallet) nBlockBits = ByteReverse(pblock->nBits); hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); } - + } } } @@ -828,7 +849,7 @@ void static GenericMiner(CWallet *pwallet, int algo) CBlock *pblock = &pblocktemplate->block; IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); - LogPrintf("Running %s miner with %u transactions in block (%u bytes)\n", + LogPrintf("Running %s miner with %u transactions in block (%u bytes)\n", GetAlgoName(algo).c_str(), pblock->vtx.size(), ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); @@ -853,7 +874,7 @@ void static GenericMiner(CWallet *pwallet, int algo) break; } ++pblock->nNonce; - + // Meter hashes/sec static int64_t nHashCounter; if (nHPSTimerStart == 0) @@ -904,7 +925,7 @@ void static GenericMiner(CWallet *pwallet, int algo) // hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); } } - } + } } void static ThreadBitcoinMiner(CWallet *pwallet) @@ -912,8 +933,8 @@ void static ThreadBitcoinMiner(CWallet *pwallet) LogPrintf("Myriadcoin miner started\n"); SetThreadPriority(THREAD_PRIORITY_LOWEST); RenameThread("bitcoin-miner"); - - try + + try { switch (miningAlgo) { diff --git a/src/miner.h b/src/miner.h index 5c80fe2d..5ae8fe98 100644 --- a/src/miner.h +++ b/src/miner.h @@ -7,6 +7,8 @@ #define BITCOIN_MINER_H #include +#include "auxpow.h" +#include "core.h" class CBlock; class CBlockIndex; @@ -22,6 +24,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, int algo); CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int algo); /** Modify the extranonce in a block */ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); +void IncrementExtraNonceWithAux(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce, std::vector& vchAux); /** Do mining precalculation */ void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1); /** Check mined block */ diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 92c55d2c..264dbec2 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -7,6 +7,7 @@ #include "main.h" #include "sync.h" #include "checkpoints.h" +#include "auxpow.h" #include diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index f7a74831..12e4bf7d 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -633,3 +633,290 @@ Value submitblock(const Array& params, bool fHelp) return Value::null; } + +#ifdef ENABLE_WALLET +Value getworkaux(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1) + throw runtime_error( + "getworkaux \n" + "getworkaux '' \n" + "getworkaux 'submit' \n" + "getworkaux '' *\n" + " get work with auxiliary data in coinbase, for multichain mining\n" + " is the merkle root of the auxiliary chain block hashes, concatenated with the aux chain merkle tree size and a nonce\n" + " is the aux chain index in the aux chain merkle tree\n" + " is the optional merkle branch of the aux chain\n" + "If is not specified, returns formatted hash data to work on:\n" + " \"midstate\" : precomputed hash state after hashing the first half of the data\n" + " \"data\" : block data\n" + " \"hash1\" : formatted hash buffer for second hash\n" + " \"target\" : little endian hash target\n" + "If is specified and 'submit', tries to solve the block for this (parent) chain and returns true if it was successful." + "If is specified and empty first argument, returns the aux merkle root, with size and nonce." + "If and are specified, creates an auxiliary proof of work for the chain specified and returns:\n" + " \"aux\" : merkle root of auxiliary chain block hashes\n" + " \"auxpow\" : aux proof of work to submit to aux chain\n" + ); + + if (vNodes.empty()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Unitus is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Unitus is downloading blocks..."); + + // We use height plus one because we're testing the next block + if ((chainActive.Tip()->nHeight+1) < GetAuxPowStartBlock()) { + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "getworkaux method is not available until switch-over block."); + } + + static map > mapNewBlock; + static vector vNewBlockTemplate; + static CReserveKey reservekey(pwalletMain); + + if (params.size() == 1) + { + static vector vchAuxPrev; + vector vchAux = ParseHex(params[0].get_str()); + + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static uint64_t nStart; + static CBlockTemplate* pblocktemplate; + if (pindexPrev != chainActive.Tip() || + (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)) + { + if (pindexPrev != chainActive.Tip()) + { + // Deallocate old blocks since they're obsolete now + mapNewBlock.clear(); + BOOST_FOREACH(CBlockTemplate* pblocktemplate, vNewBlockTemplate) + delete pblocktemplate; + vNewBlockTemplate.clear(); + } + nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); + pindexPrev = chainActive.Tip(); + vchAuxPrev = vchAux; + nStart = GetTime(); + + // Create new block + pblocktemplate = CreateNewBlockWithKey(*pMiningKey, miningAlgo); + if (!pblocktemplate) + throw JSONRPCError(-7, "Out of memory"); + vNewBlockTemplate.push_back(pblocktemplate); + } + CBlock* pblock = &pblocktemplate->block; // pointer for convenience + + // Update nTime + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + pblock->nNonce = 0; + + // Update nExtraNonce + static unsigned int nExtraNonce = 0; + IncrementExtraNonceWithAux(pblock, pindexPrev, nExtraNonce, vchAux); + + // Save + mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, nExtraNonce); + + // Prebuild hash buffers + char pmidstate[32]; + char pdata[128]; + char phash1[64]; + FormatHashBuffers(pblock, pmidstate, pdata, phash1); + + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + Object result; + result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); + result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); + result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); + result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); + return result; + } + else + { + if (params[0].get_str() != "submit" && params[0].get_str() != "") + throw JSONRPCError(-8, " must be the empty string or 'submit' if work is being submitted"); + // Parse parameters + vector vchData = ParseHex(params[1].get_str()); + if (vchData.size() != 128) + throw JSONRPCError(-8, "Invalid parameter"); + CBlock* pdata = (CBlock*)&vchData[0]; + + // Byte reverse + for (int i = 0; i < 128/4; i++) + ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]); + + // Get saved block + if (!mapNewBlock.count(pdata->hashMerkleRoot)) + return false; + CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; + unsigned int nExtraNonce = mapNewBlock[pdata->hashMerkleRoot].second; + + pblock->nTime = pdata->nTime; + pblock->nNonce = pdata->nNonce; + + // Get the aux merkle root from the coinbase + CScript script = pblock->vtx[0].vin[0].scriptSig; + opcodetype opcode; + CScript::const_iterator pc = script.begin(); + script.GetOp(pc, opcode); + script.GetOp(pc, opcode); + script.GetOp(pc, opcode); + if (opcode != OP_2) + throw runtime_error("invalid aux pow script"); + vector vchAux; + script.GetOp(pc, opcode, vchAux); + + RemoveMergedMiningHeader(vchAux); + + unsigned int nHeight = chainActive.Tip()->nHeight+1; // Height first in coinbase required for block.version=2 + pblock->vtx[0].vin[0].scriptSig = MakeCoinbaseWithAux(nHeight, nExtraNonce, vchAux); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + + if (params.size() > 2) + { + // Requested aux proof of work + int nChainIndex = params[2].get_int(); + + CAuxPow pow(pblock->vtx[0]); + + for (unsigned int i = 3 ; i < params.size() ; i++) + { + uint256 nHash; + nHash.SetHex(params[i].get_str()); + pow.vChainMerkleBranch.push_back(nHash); + } + + pow.SetMerkleBranch(pblock); + pow.nChainIndex = nChainIndex; + pow.parentBlockHeader = *pblock; + CDataStream ss(SER_GETHASH, PROTOCOL_VERSION); + ss << pow; + Object result; + result.push_back(Pair("auxpow", HexStr(ss.begin(), ss.end()))); + return result; + } + else + { + if (params[0].get_str() == "submit") + { + return CheckWork(pblock, *pwalletMain, reservekey); + } + else + { + Object result; + result.push_back(Pair("aux", HexStr(vchAux.begin(), vchAux.end()))); + result.push_back(Pair("hash", pblock->GetHash().GetHex())); + return result; + } + } + } +} + +Value getauxblock(const Array& params, bool fHelp) +{ + if (fHelp || (params.size() != 0 && params.size() != 2)) + throw runtime_error( + "getauxblock [ ]\n" + " create a new block" + "If , is not specified, returns a new block hash.\n" + "If , is specified, tries to solve the block based on " + "the aux proof of work and returns true if it was successful."); + + if (vNodes.empty()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Unitus is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Unitus is downloading blocks..."); + + // We use height plus one because we're testing the next block + if ((chainActive.Tip()->nHeight+1) < GetAuxPowStartBlock()) { + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "getauxblock method is not available until switch-over block."); + } + + static map mapNewBlock; + static vector vNewBlockTemplate; + static CReserveKey reservekey(pwalletMain); + + if (params.size() == 0) + { + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static uint64_t nStart; + static CBlock* pblock; + static CBlockTemplate* pblocktemplate; + if (pindexPrev != chainActive.Tip() || + (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)) + { + if (pindexPrev != chainActive.Tip()) + { + // Deallocate old blocks since they're obsolete now + mapNewBlock.clear(); + BOOST_FOREACH(CBlockTemplate* pblocktemplate, vNewBlockTemplate) + delete pblocktemplate; + vNewBlockTemplate.clear(); + } + nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); + pindexPrev = chainActive.Tip(); + nStart = GetTime(); + + // Create new block with nonce = 0 and extraNonce = 1 + pblocktemplate = CreateNewBlockWithKey(*pMiningKey, miningAlgo); + if (!pblocktemplate) + throw JSONRPCError(-7, "Out of memory"); + + pblock = &pblocktemplate->block; + // Update nTime + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + pblock->nNonce = 0; + + + // Update nExtraNonce + static unsigned int nExtraNonce = 0; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + + // Sets the version + pblock->SetAuxPow(new CAuxPow()); + + // Save + mapNewBlock[pblock->GetHash()] = pblock; + + vNewBlockTemplate.push_back(pblocktemplate); + } + + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + Object result; + result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); + result.push_back(Pair("hash", pblock->GetHash().GetHex())); + result.push_back(Pair("chainid", pblock->GetChainID())); + return result; + } + else + { + uint256 hash; + hash.SetHex(params[0].get_str()); + vector vchAuxPow = ParseHex(params[1].get_str()); + CDataStream ss(vchAuxPow, SER_GETHASH, PROTOCOL_VERSION); + CAuxPow* pow = new CAuxPow(); + ss >> *pow; + if (!mapNewBlock.count(hash)) + return ::error("getauxblock() : block not found"); + + CBlock* pblock = mapNewBlock[hash]; + pblock->SetAuxPow(pow); + + if (!CheckWork(pblock, *pwalletMain, reservekey)) + { + return false; + } + else + { + return true; + } + } +} +#endif \ No newline at end of file diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 0a15ed7a..d8e87cf2 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -325,6 +325,9 @@ static const CRPCCommand vRPCCommands[] = { "scanforalltxns", &scanforalltxns, false, false, true}, { "scanforstealthtxns", &scanforstealthtxns, false, false, true}, + { "getworkaux", &getworkaux, true, false, true }, + { "getauxblock", &getauxblock, true, false, true } + #endif // ENABLE_WALLET }; diff --git a/src/rpcserver.h b/src/rpcserver.h index fc529068..b8d546f3 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -198,4 +198,7 @@ extern json_spirit::Value clearwallettransactions(const json_spirit::Array& para extern json_spirit::Value scanforalltxns(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value scanforstealthtxns(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getworkaux(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getauxblock(const json_spirit::Array& params, bool fHelp); + #endif diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index d603b241..154da9db 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -10,6 +10,7 @@ #include "serialize.h" #include "uint256.h" #include "util.h" +#include "auxpow.h" #include diff --git a/src/test/checkblock_tests.cpp b/src/test/checkblock_tests.cpp index 10352240..b2c7cb88 100644 --- a/src/test/checkblock_tests.cpp +++ b/src/test/checkblock_tests.cpp @@ -9,6 +9,7 @@ #include "main.h" +#include "auxpow.h" #include diff --git a/src/txdb.cpp b/src/txdb.cpp index cb92922a..f25e9cec 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -7,6 +7,7 @@ #include "core.h" #include "uint256.h" +#include "auxpow.h" #include @@ -68,10 +69,27 @@ bool CCoinsViewDB::BatchWrite(const std::map &mapCoins, const u CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { } +/* bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) { return Write(make_pair('b', blockindex.GetBlockHash()), blockindex); } +*/ + +bool CBlockTreeDB::WriteBlockIndex(const CBlockIndex& blockindex) +{ + return Write(boost::tuples::make_tuple('b', blockindex.GetBlockHash(), 'b'), blockindex); +} + +bool CBlockTreeDB::ReadDiskBlockIndex(const uint256 &blkid, CDiskBlockIndex &diskblockindex) +{ + return Read(boost::tuples::make_tuple('b', blkid, 'a'), diskblockindex); +} + +bool CBlockTreeDB::WriteDiskBlockIndex(const CDiskBlockIndex& diskblockindex) +{ + return Write(boost::tuples::make_tuple('b', *diskblockindex.phashBlock, 'a'), diskblockindex); +} bool CBlockTreeDB::WriteBestInvalidWork(const CBigNum& bnBestInvalidWork) { diff --git a/src/txdb.h b/src/txdb.h index 5eb5731d..92db1b79 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -51,7 +51,10 @@ private: CBlockTreeDB(const CBlockTreeDB&); void operator=(const CBlockTreeDB&); public: - bool WriteBlockIndex(const CDiskBlockIndex& blockindex); + // bool WriteBlockIndex(const CDiskBlockIndex& blockindex); + bool WriteDiskBlockIndex(const CDiskBlockIndex& diskblockindex); + bool WriteBlockIndex(const CBlockIndex& blockindex); + bool ReadDiskBlockIndex(const uint256 &blkid, CDiskBlockIndex& diskblockindex); bool WriteBestInvalidWork(const CBigNum& bnBestInvalidWork); bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo); bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo);