Overview
We will add:
- altintegration::PopData entity into the
CBlock
class in primitives/block.h
file. This is needed to store POP-related consensus information, such as ATVs, VTBs, and VBk blocks.
POP_BLOCK_VERSION_BIT
flag - to distinguish if CBlockHeader
has altintegration::PopData or not.
- Update serialization for
CBlock
to include altintegration::PopData.
- Add serialization/deserialization code for altintegration::PopData entity.
1. Helper for the block hash serialization.
https://github.com/VeriBlock/vbk-ri-btc/blob/master/src/uint256.h
class base_blob
{
s.read((char*)data, sizeof(data));
}
+
+
+ std::vector<uint8_t> asVector() const {
+ return std::vector<uint8_t>{begin(), end()};
+ }
};
2. Define POP_BLOCK_VERSION_BIT flag.
This flag is set in a block header version and will tell users if given block header contains PopData or not.
In file src/version.h
define this flag:
static const int PROTOCOL_VERSION = 80000;
+namespace VeriBlock {
+static const int32_t POP_BLOCK_VERSION_BIT = 0x80000UL;
+}
https://github.com/VeriBlock/vbk-ri-btc/blob/master/src/primitives/block.h
#include <serialize.h>
#include <uint256.h>
+#include <version.h>
+#include <veriblock/pop.hpp>
class CBlock
public:
std::vector<CTransactionRef> vtx;
+
Represents ALT block body of POP-related info.
mutable bool fChecked;
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITEAS(CBlockHeader, *this);
READWRITE(vtx);
+
+ if (this->nVersion & VeriBlock::POP_BLOCK_VERSION_BIT) {
+ READWRITE(popData);
+ }
}
void SetNull()
{
CBlockHeader::SetNull();
vtx.clear();
+ popData.clear();
fChecked = false;
}
3. Add new PopData field into the BlockTransactions, CBlockHeaderAndShortTxIDs, PartiallyDownloadedBlock and update their serialization/deserialization.
Bitcoin uses these classes during sync protocol. We must ensure that altintegration::PopData is propagated alongside transactions during sync.
https://github.com/VeriBlock/vbk-ri-btc/blob/master/src/blockencodings.h
class BlockTransactions
class BlockTransactions {
public:
uint256 blockhash;
std::vector<CTransactionRef> txn;
for (size_t i = 0; i < txn.size(); i++)
READWRITE(TransactionCompressor(txn[i]));
}
+
+
+ READWRITE(popData);
}
class CBlockHeaderAndShortTxIDs
public:
CBlockHeader header;
}
}
+ if (this->header.nVersion & VeriBlock::POP_BLOCK_VERSION_BIT) {
+ READWRITE(popData);
+ }
READWRITE(prefilledtxn);
class PartiallyDownloadedBlock
public:
CBlockHeader header;
explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {}
ReadStatus InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<std::pair<uint256, CTransactionRef>>& extra_txn);
bool IsTxAvailable(size_t index) const;
ReadStatus FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing);
+ ReadStatus FillBlock(CBlock& block,
const std::vector<CTransactionRef>& vtx_missing,
const altintegration::PopData& popData) {
+ block.popData = popData;
+ return FillBlock(block, vtx_missing);
+ }
};
4. Update PartiallyDownloadedBlock initializing - set PopData field.
https://github.com/VeriBlock/vbk-ri-btc/blob/master/src/blockencodings.cpp
method CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs
const CTransaction& tx = *block.vtx[i];
shorttxids[i - 1] = GetShortID(fUseWTXID ? tx.GetWitnessHash() : tx.GetHash());
}
+
+ this->popData = block.popData;
method PartiallyDownloadedBlock::InitData
if (mempool_count == shorttxids.size())
break;
}
+
+ this->popData = cmpctblock.popData;
return READ_STATUS_OK;
method PartiallyDownloadedBlock::FillBlock
if (vtx_missing.size() != tx_missing_offset)
return READ_STATUS_INVALID;
+
+ block.popData = this->popData;
+
BlockValidationState state;
5. Update setting up the PopData fields during the net processing.
https://github.com/VeriBlock/vbk-ri-btc/blob/master/src/net_processing.cpp
method SendBlockTransactions
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
int nSendFlags = State(pfrom->GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
+
+
+ resp.popData = block.popData;
+
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
method ProcessMessage
BlockTransactions txn;
txn.blockhash = cmpctblock.header.GetHash();
+ txn.popData = cmpctblock.popData;
blockTxnMsg << txn;
fProcessBLOCKTXN = true;
if (status == READ_STATUS_OK) {
fBlockReconstructed = true;
+ if(pblock && pblock->nVersion & VeriBlock::POP_BLOCK_VERSION_BIT) {
+ assert(!pblock->popData.empty() && "POP bit is set and POP data is empty");
+ }
}
PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
- ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
+ ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn, resp.popData);
if (status == READ_STATUS_INVALID) {
for (unsigned int n = 0; n < nCount; n++) {
vRecv >> headers[n];
ReadCompactSize(vRecv);
+ if (headers[n].nVersion & VeriBlock::POP_BLOCK_VERSION_BIT) {
+ vRecv >> tmp;
+ }
}
6. Update validation rules.
Add check that if block contains VeriBlock PopData then block.nVersion
must contain POP_BLOCK_VERSION_BIT
. Otherwise block.nVersion
should not contain POP_BLOCK_VERSION_BIT
.
https://github.com/VeriBlock/vbk-ri-btc/blob/master/src/validation.cpp
method UpdateTip
int32_t nExpectedVersion = ComputeBlockVersion(pindex->pprev, chainParams.GetConsensus());
- if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && (pindex->nVersion & ~nExpectedVersion) != 0)
+
+ auto version = pindex->nVersion & (~VeriBlock::POP_BLOCK_VERSION_BIT);
+ if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && (version & ~nExpectedVersion) != 0)
++nUpgraded;
pindex = pindex->pprev;
Update CheckBlock
:
- merkle root verification with POP depends on current state, so this validation has been migrated to
ContextuallyCheckBlock
.
- if POP_BLOCK_VERSION_BIT is set, then PopData MUST exist and be non-empty.
method CheckBlock
if (!CheckBlockHeader(block, state, consensusParams, fCheckPOW))
return false;
-
- if (fCheckMerkleRoot) {
- bool mutated;
- uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated);
- if (block.hashMerkleRoot != hashMerkleRoot2)
- return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-txnmrklroot", "hashMerkleRoot mismatch");
-
-
-
-
- if (mutated)
- return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-txns-duplicate", "duplicate transaction");
- }
+
+ if ((block.nVersion & VeriBlock::POP_BLOCK_VERSION_BIT) && block.popData.empty()) {
+ return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-block-pop-version", "POP bit is set, but pop data is empty");
+ }
+ if (!(block.nVersion & VeriBlock::POP_BLOCK_VERSION_BIT) && !block.popData.empty()) {
+ return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-block-pop-version", "POP bit is NOT set, and pop data is NOT empty");
+ }
7. Update the mining code to setup POP_BLOCK_VERSION_BIT if VeriBlock PopData is contained in the block.
https://github.com/VeriBlock/vbk-ri-btc/blob/master/src/miner.cpp
method BlockAssembler::CreateNewBlock
addPackageTxs<ancestor_score>(nPackagesSelected, nDescendantsUpdated);
+
+ if (chainparams.isPopActive(nHeight))
+ {
+ pblock->popData = VeriBlock::getPopData(*pindexPrev);
+ }
+
+ if (!pblock->popData.empty()) {
+ pblock->nVersion |= VeriBlock::POP_BLOCK_VERSION_BIT;
+ }
+
int64_t nTime1 = GetTimeMicros();
m_last_block_num_txs = nBlockTx;
m_last_block_weight = nBlockWeight;
8. Overload serialization operations for the VeriBlock PopData and other VeriBlock entities.
- Warning
- In our example we use VeriBlock-native serialization (function
toVbkEncoding
). It essentially couples Altchain CBlock serialization to VBK encoding. If VBK encoding ever change, Altchain will get hard fork. To mitigate this, it is stongly suggested to implement your own serialization.
During deserialization, if any of internal entities can not be deserialized, throw an exception.
https://github.com/VeriBlock/vbk-ri-btc/blob/master/src/serialize.h
#include <prevector.h>
#include <span.h>
+#include <veriblock/pop.hpp>
+
static const unsigned int MAX_SIZE = 0x02000000;
+
+ Serialize(s, bytes_data);
+}
+template <typename T>
+void UnserializeOrThrow(const std::vector<uint8_t>& in, T& out) {
+ throw std::invalid_argument(state.toString());
+ }
+}
+
+template <typename T>
+void UnserializeOrThrow(const std::vector<uint8_t>& in, T& out, typename T::hash_t precalculatedHash) {
+ throw std::invalid_argument(state.toString());
+ }
+}
+
+ std::vector<uint8_t> bytes_data;
+ Unserialize(s, bytes_data);
+ UnserializeOrThrow(bytes_data, pop_data);
+}
+
+ Serialize(s, bytes_data);
+}
+
+ std::vector<uint8_t> bytes_data;
+ Unserialize(s, bytes_data);
+ UnserializeOrThrow(bytes_data, atv);
+}
+ Serialize(s, bytes_data);
+}
+ std::vector<uint8_t> bytes_data;
+ Unserialize(s, bytes_data);
+ UnserializeOrThrow(bytes_data, vtb);
+}
+
+ std::vector<uint8_t> bytes_data = b.toVbkEncoding();
+ Serialize(s, bytes_data);
+}
+ std::vector<uint8_t> bytes_data;
+ Unserialize(s, bytes_data);
+ UnserializeOrThrow(bytes_data, b);
+}
+ std::vector<uint8_t> bytes_data = b.toVbkEncoding();
+ Serialize(s, bytes_data);
+}
+ std::vector<uint8_t> bytes_data;
+ Unserialize(s, bytes_data);
+ UnserializeOrThrow(bytes_data, b);
+}
+ std::vector<uint8_t> bytes_data = b.toVbkEncoding();
+ Serialize(s, bytes_data);
+}
+ std::vector<uint8_t> bytes_data;
+ Unserialize(s, bytes_data);
+ UnserializeOrThrow(bytes_data, b);
+}
+ Serialize(s, b.asVector());
+}
+ std::vector<uint8_t> bytes;
+ Unserialize(s, bytes);
+ if(bytes.size() > N) {
+ throw std::invalid_argument("Blob: bad size. Expected <= " + std::to_string(N) + ", got=" + std::to_string(bytes.size()));
+ }
+ b = bytes;
+}
+
+ Serialize(s, stream.data());
+}
+ std::vector<uint8_t> bytes_data;
+ Unserialize(s, bytes_data);
+ UnserializeOrThrow(bytes_data, block);
+}
+template <typename Stream>
+{
+ std::vector<uint8_t> bytes_data;
+ Unserialize(s, bytes_data);
+ UnserializeOrThrow(bytes_data, block, precalculatedHash);
+}
Class that is used for storing validation state.
Binary writer that is useful for binary serialization.
bool DeserializeFromVbkEncoding(ReadStream &stream, AltBlockAddon &out, ValidationState &state)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void toVbkEncoding(WriteStream &stream) const
Convert ATV to data stream using Vbk byte format.
Contiguous byte array of fixed size.
void toVbkEncoding(WriteStream &stream) const
Convert PopData to data stream using Vbk byte format.
Binary reading stream, that is useful during binary deserialization.
Veriblock to Bitcoin publication, committed to Veriblock blockchain in containingBlock.
void toVbkEncoding(WriteStream &stream) const
Convert VTB to data stream using Vbk byte format.
std::vector< uint8_t > toVbkEncoding() const
Convert VbkBlock to raw bytes data using VbkBlock byte format.
Next Section