diff --git a/src/config.h b/src/config.h --- a/src/config.h +++ b/src/config.h @@ -64,4 +64,7 @@ // Temporary woraround. const Config &GetConfig(); +// TODO (EBP): a non-const reference is needed to updated the blocksize +Config &GetConfigTemp(); + #endif diff --git a/src/config.cpp b/src/config.cpp --- a/src/config.cpp +++ b/src/config.cpp @@ -47,6 +47,11 @@ return gConfig; } +// TODO (EBP): a non-const reference is needed to updated the blocksize +Config &GetConfigTemp() { + return gConfig; +} + void GlobalConfig::SetCashAddrEncoding(bool c) { useCashAddr = c; } diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -153,6 +153,10 @@ /** chainwork for the last block that preciousblock has been applied to. */ arith_uint256 nLastPreciousChainwork = 0; +/** Extensible Blockchain Protocol . */ +std::map EBPBlockMap; +uint64_t max_size_ebp_block = 0; + /** Dirty block index entries. */ std::set setDirtyBlockIndex; @@ -2355,8 +2359,8 @@ nLastSetChain = nNow; } } catch (const std::runtime_error &e) { - return AbortNode( - state, std::string("System error while flushing: ") + e.what()); + return AbortNode(state, std::string("System error while flushing: ") + + e.what()); } return true; } @@ -3347,21 +3351,8 @@ "first tx is not coinbase"); } - // Size limits. - auto nMaxBlockSize = config.GetMaxBlockSize(); - - // Bail early if there is no way this block is of reasonable size. - if ((block.vtx.size() * MIN_TRANSACTION_SIZE) > nMaxBlockSize) { - return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, - "size limits failed"); - } - auto currentBlockSize = ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION); - if (currentBlockSize > nMaxBlockSize) { - return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, - "size limits failed"); - } // And a valid coinbase. if (!CheckCoinbase(*block.vtx[0], state, false)) { @@ -3685,6 +3676,112 @@ return true; } +static std::pair CheckBlockIsEBP(const CBlock &block, + const Config &config) { + // Return true if the block is EBP and also returns its blockssize + + // EBP Rules: + // 1-Checkblocksize + auto nMaxBlockSize = config.GetMaxBlockSize(); + auto currentBlockSize = + ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION); + if ((block.vtx.size() * MIN_TRANSACTION_SIZE) > nMaxBlockSize) { + return std::make_pair(true, currentBlockSize); + } + if (currentBlockSize > nMaxBlockSize) { + return std::make_pair(true, currentBlockSize); + } + + // TODO: start to ban the node with low values to avoid getting spammed with + // invalid blocks + // state.DoS(3, false, REJECT_INVALID, "bad-blk-length", false,"size limits + // failed"); + + return std::make_pair(false, currentBlockSize); +} + +static bool AddToEBPIndex(CBlockIndex *&pindex, const CBlock &block, + bool is_ebp) { + if (is_ebp) { + // The block is EBP so added to the index + EBPBlockMap.insert(std::make_pair(pindex, block)); + return true; + } else { + // Check if the block is a son of an EBP block + for (auto &i : EBPBlockMap) { + if (i.first == pindex->pprev) { + EBPBlockMap.insert(std::make_pair(pindex, block)); + return true; + } + } + } + return false; +} + +static bool SaveBlockToDisk(CBlockIndex *&pindex, const CBlock &block, + const CDiskBlockPos *dbp, CValidationState &state, + const CChainParams &chainparams) { + int nHeight = pindex->nHeight; + // Write block to history file + try { + unsigned int nBlockSize = + ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); + CDiskBlockPos blockPos; + if (dbp != nullptr) { + blockPos = *dbp; + } + if (!FindBlockPos(state, blockPos, nBlockSize + 8, nHeight, + block.GetBlockTime(), dbp != nullptr)) { + return error("AcceptBlock(): FindBlockPos failed"); + } + if (dbp == nullptr) { + if (!WriteBlockToDisk(block, blockPos, chainparams.DiskMagic())) { + return AbortNode(state, "Failed to write block"); + } + } + if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) { + return error("AcceptBlock(): ReceivedBlockTransactions failed"); + } + } catch (const std::runtime_error &e) { + return AbortNode(state, std::string("System error: ") + e.what()); + } + + return true; +} + +static uint64_t GetNewConsensusBlockSize(const Config &config, + const uint64_t ebp_block_size) { + // Duplicate the current blocksize until it is bigger than the + // ebp_block_size + uint64_t next_block_size = config.GetMaxBlockSize(); + while (next_block_size < ebp_block_size) { + next_block_size = next_block_size * 2; + } + return next_block_size; +} + +static bool FindAllEBPBlocks(CBlockIndex *&last_block, + std::list &blocks_to_store) { + // Check if all the blocks on the EBP chain are ready to be stored and + // reorganize + + auto pindexFork = chainActive.FindFork(last_block); + auto temp = last_block; + + while (temp->pprev != pindexFork) { + // TODO: pindexFork may not be in EBPBlockMap if the fork started before + // the first EBP block + // It also needs to check if the block is already stored in the database + if (EBPBlockMap.find(temp) != EBPBlockMap.end()) { + blocks_to_store.push_front(temp); + } else { + return false; + } + temp = temp->pprev; + } + return true; +} + /** * Store block on disk. If dbp is non-null, the file is known to already reside * on disk. @@ -3757,9 +3854,14 @@ *fNewBlock = true; } + // TODO: In case of an EBP block, it's only needed to check for valid PoW + // and Size, the rest of the validation can be done right before storing the + // block to disk if (!CheckBlock(config, block, state) || !ContextualCheckBlock(config, block, state, pindex->pprev)) { if (state.IsInvalid() && !state.CorruptionPossible()) { + // TODO: check what happens when a EBP blocks arrives in a wrong + // order (w/o parent already stored) pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); } @@ -3767,43 +3869,78 @@ block.GetHash().ToString()); } - // Header is valid/has work, merkle tree and segwit merkle tree are - // good...RELAY NOW (but if it does not build on our best tip, let the - // SendMessages loop relay it) - if (!IsInitialBlockDownload() && chainActive.Tip() == pindex->pprev) { - GetMainSignals().NewPoWValidBlock(pindex, pblock); - } + // Check if the block is EBP or if it's a block included in a ebp chain + const auto ebp = CheckBlockIsEBP(block, config); + if (AddToEBPIndex(pindex, block, ebp.first)) { + // TODO : Save the size of the biggest block on the EBP chain + if (ebp.second > max_size_ebp_block) { + max_size_ebp_block = ebp.second; + } + + // Check if the EBP chain is at least 12 blocks bigger than the current + // active chain (using PoW) + auto diff = chainActive.Tip()->nChainWork - + chainActive.Tip()->pprev->nChainWork; + if (pindex->nChainWork >= (chainActive.Tip()->nChainWork + 12 * diff)) { + std::list blocks_to_store; + if (FindAllEBPBlocks(pindex, blocks_to_store)) { + // Ready to update consensus maxblocksize and reorganize the + // chain + // TODO: this new_max_size must be saved to disk + const auto new_max_size = + GetNewConsensusBlockSize(config, max_size_ebp_block); + GetConfigTemp().SetMaxBlockSize(new_max_size); + + // Push the first block, the one that its previous == pindexFork + blocks_to_store.push_front(pindex); + + // Save to disk the EBP chain + for (auto block_index : blocks_to_store) { + auto temp_block = EBPBlockMap.find(block_index); + if (temp_block != EBPBlockMap.end()) { + if (!SaveBlockToDisk(block_index, temp_block->second, + dbp, state, + config.GetChainParams())) { + return false; + } + } + EBPBlockMap.erase(block_index); + } - int nHeight = pindex->nHeight; - const CChainParams &chainparams = config.GetChainParams(); + // Reorganization is complete + return true; - // Write block to history file - try { - unsigned int nBlockSize = - ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); - CDiskBlockPos blockPos; - if (dbp != nullptr) { - blockPos = *dbp; - } - if (!FindBlockPos(state, blockPos, nBlockSize + 8, nHeight, - block.GetBlockTime(), dbp != nullptr)) { - return error("AcceptBlock(): FindBlockPos failed"); - } - if (dbp == nullptr) { - if (!WriteBlockToDisk(block, blockPos, chainparams.DiskMagic())) { - AbortNode(state, "Failed to write block"); + } else { + // The chain is not complete, wait for the nodes to send the + // blocks + return false; } + } else { + // Not enough PoW to activate EBP + return error( + "%s: %s (block %s). Not enough PoW to reorganize (EBP)", + __func__, FormatStateMessage(state), + block.GetHash().ToString()); } - if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) { - return error("AcceptBlock(): ReceivedBlockTransactions failed"); + + } else { + // Header is valid/has work, merkle tree and segwit merkle tree are + // good...RELAY NOW (but if it does not build on our best tip, let the + // SendMessages loop relay it) + if (!IsInitialBlockDownload() && chainActive.Tip() == pindex->pprev) { + GetMainSignals().NewPoWValidBlock(pindex, pblock); + } + + // Write block to history file + if (!SaveBlockToDisk(pindex, block, dbp, state, + config.GetChainParams())) { + return false; } - } catch (const std::runtime_error &e) { - return AbortNode(state, std::string("System error: ") + e.what()); - } - if (fCheckForPruning) { - // we just allocated more disk space for block files. - FlushStateToDisk(config.GetChainParams(), state, FLUSH_STATE_NONE); + if (fCheckForPruning) { + // we just allocated more disk space for block files. + FlushStateToDisk(config.GetChainParams(), state, FLUSH_STATE_NONE); + } } return true; @@ -4399,11 +4536,10 @@ boost::this_thread::interruption_point(); uiInterface.ShowProgress( _("Verifying blocks..."), - std::max(1, - std::min(99, - 100 - (int)(((double)(chainActive.Height() - - pindex->nHeight)) / - (double)nCheckDepth * 50)))); + std::max( + 1, std::min(99, 100 - (int)(((double)(chainActive.Height() - + pindex->nHeight)) / + (double)nCheckDepth * 50)))); pindex = chainActive.Next(pindex); CBlock block; if (!ReadBlockFromDisk(block, pindex, config)) {