2017-09-03 19:46:57 +02:00
|
|
|
/*
|
2017-11-09 19:34:51 +01:00
|
|
|
* This file is part of the Flowee project
|
2021-02-16 19:22:33 +01:00
|
|
|
* Copyright (C) 2017-2021 Tom Zander <tom@flowee.org>
|
2017-09-03 19:46:57 +02:00
|
|
|
*
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
2021-11-02 10:23:33 +01:00
|
|
|
#include "Block.h"
|
2017-09-03 19:46:57 +02:00
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
|
#include <hash.h>
|
2019-03-11 14:47:12 +01:00
|
|
|
#include <streaming/streams.h>
|
2017-09-03 19:46:57 +02:00
|
|
|
#include <streaming/BufferPool.h>
|
|
|
|
|
|
2026-04-15 21:35:08 +02:00
|
|
|
#include "primitives/MutableBlock.h"
|
2019-11-13 15:11:39 +01:00
|
|
|
|
2017-09-03 19:46:57 +02:00
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
uint64_t readCompactSize(const Streaming::ConstBuffer &buf, size_t &posInStream) {
|
|
|
|
|
if ((size_t) buf.size() <= posInStream)
|
|
|
|
|
throw std::runtime_error("readCompactSize not enough bytes");
|
|
|
|
|
unsigned char byte = static_cast<unsigned char>(buf[posInStream]);
|
|
|
|
|
if (byte < 253) {
|
|
|
|
|
++posInStream;
|
|
|
|
|
return byte;
|
|
|
|
|
}
|
|
|
|
|
if (byte == 253) {// next 2 bytes
|
|
|
|
|
if ((size_t) buf.size() <= posInStream + 3)
|
|
|
|
|
throw std::runtime_error("readCompactSize not enough bytes");
|
|
|
|
|
posInStream += 3;
|
|
|
|
|
return le16toh(*((uint16_t*)(buf.begin() + posInStream - 2)));
|
|
|
|
|
}
|
|
|
|
|
if (byte == 254) {// next 4 bytes
|
|
|
|
|
if ((size_t) buf.size() <= posInStream + 5)
|
|
|
|
|
throw std::runtime_error("readCompactSize not enough bytes");
|
|
|
|
|
posInStream += 5;
|
|
|
|
|
return le32toh(*((uint32_t*)(buf.begin() + posInStream - 4)));
|
|
|
|
|
}
|
|
|
|
|
// next 8 bytes
|
|
|
|
|
if ((size_t) buf.size() <= posInStream + 9)
|
|
|
|
|
throw std::runtime_error("readCompactSize not enough bytes");
|
|
|
|
|
posInStream += 9;
|
|
|
|
|
return le64toh(*((uint64_t*)(buf.begin() + posInStream - 8)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// from data, tell me how large a transaction is.
|
|
|
|
|
int transactionSize(const Streaming::ConstBuffer &buf, const int pos) {
|
|
|
|
|
size_t positionInStream = pos + 4;
|
|
|
|
|
uint64_t inCount = readCompactSize(buf, positionInStream);
|
|
|
|
|
for (uint64_t i = 0; i < inCount; ++i) {
|
|
|
|
|
positionInStream += 32 + 4;
|
|
|
|
|
uint64_t scriptLength = readCompactSize(buf, positionInStream);
|
|
|
|
|
positionInStream += scriptLength + 4;
|
|
|
|
|
if (positionInStream > (size_t) buf.size() + pos)
|
|
|
|
|
throw std::runtime_error("transaction malformed error");
|
|
|
|
|
}
|
|
|
|
|
uint64_t outCount = readCompactSize(buf, positionInStream);
|
|
|
|
|
for (uint64_t i = 0; i < outCount; ++i) {
|
|
|
|
|
positionInStream += 8;
|
|
|
|
|
uint64_t scriptLength = readCompactSize(buf, positionInStream);
|
|
|
|
|
positionInStream += scriptLength;
|
|
|
|
|
if (positionInStream > (size_t) buf.size() + pos)
|
|
|
|
|
throw std::runtime_error("transaction malformed error");
|
|
|
|
|
}
|
|
|
|
|
positionInStream += 4;
|
|
|
|
|
return positionInStream - pos;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 10:18:24 +01:00
|
|
|
Block::Block()
|
2017-09-03 19:46:57 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 10:18:24 +01:00
|
|
|
Block::Block(const Streaming::ConstBuffer &rawBlock)
|
2017-09-03 19:46:57 +02:00
|
|
|
: m_data(rawBlock)
|
|
|
|
|
{
|
|
|
|
|
if (rawBlock.size() < 80)
|
|
|
|
|
throw std::runtime_error("Block too small to fit header");
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 10:18:24 +01:00
|
|
|
int32_t Block::blockVersion() const
|
2017-09-03 19:46:57 +02:00
|
|
|
{
|
|
|
|
|
return static_cast<int32_t>(le32toh(*((uint32_t*)m_data.begin())));
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 10:18:24 +01:00
|
|
|
uint256 Block::previousBlockId() const
|
2017-09-03 19:46:57 +02:00
|
|
|
{
|
|
|
|
|
return uint256(m_data.begin() + 4);
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 10:18:24 +01:00
|
|
|
uint256 Block::merkleRoot() const
|
2017-09-03 19:46:57 +02:00
|
|
|
{
|
|
|
|
|
return uint256(m_data.begin() + 36);
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 10:18:24 +01:00
|
|
|
uint32_t Block::timestamp() const
|
2017-09-03 19:46:57 +02:00
|
|
|
{
|
|
|
|
|
return le32toh(*((uint32_t*)(m_data.begin() + 68)));
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 10:18:24 +01:00
|
|
|
uint32_t Block::bits() const
|
2017-09-03 19:46:57 +02:00
|
|
|
{
|
|
|
|
|
return le32toh(*((uint32_t*)(m_data.begin() + 72)));
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 10:18:24 +01:00
|
|
|
uint32_t Block::nonce() const
|
2017-09-03 19:46:57 +02:00
|
|
|
{
|
|
|
|
|
return le32toh(*((uint32_t*)(m_data.begin() + 76)));
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 10:18:24 +01:00
|
|
|
uint256 Block::createHash() const
|
2017-09-03 19:46:57 +02:00
|
|
|
{
|
|
|
|
|
assert(m_data.size() >= 80);
|
|
|
|
|
CHash256 ctx;
|
2022-05-17 00:46:54 +02:00
|
|
|
ctx.write((const unsigned char*)m_data.begin(), 80);
|
2017-09-03 19:46:57 +02:00
|
|
|
uint256 result;
|
2022-05-17 00:46:54 +02:00
|
|
|
ctx.finalize((unsigned char*)&result);
|
2017-09-03 19:46:57 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 10:18:24 +01:00
|
|
|
void Block::findTransactions()
|
2017-09-03 19:46:57 +02:00
|
|
|
{
|
|
|
|
|
if (!m_transactions.empty())
|
|
|
|
|
return;
|
|
|
|
|
size_t pos = 80;
|
2021-02-16 19:22:33 +01:00
|
|
|
const uint64_t transactionCount = readCompactSize(m_data, pos);
|
|
|
|
|
if (transactionCount > 0xFFFFFF)
|
|
|
|
|
throw std::runtime_error("FastBlock::findTransactions: Tx count decoding failed");
|
2017-09-03 19:46:57 +02:00
|
|
|
std::vector<Tx> txs;
|
|
|
|
|
txs.reserve(transactionCount);
|
2021-02-16 19:22:33 +01:00
|
|
|
for (uint64_t i = 0; i < transactionCount; ++i) {
|
2017-09-03 19:46:57 +02:00
|
|
|
int txSize = transactionSize(m_data, pos);
|
|
|
|
|
if (txSize + pos > (uint64_t) m_data.size())
|
|
|
|
|
throw std::runtime_error("FastBlock::findTransactions: not enough bytes");
|
|
|
|
|
txs.push_back(Tx(m_data.mid(pos, txSize)));
|
|
|
|
|
pos += txSize;
|
|
|
|
|
}
|
|
|
|
|
m_transactions = std::move(txs);
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 10:18:24 +01:00
|
|
|
Tx Block::findTransaction(const uint256 &txid, bool CTORlayout) const
|
2021-03-03 10:17:14 +01:00
|
|
|
{
|
|
|
|
|
bool first = true;
|
|
|
|
|
Tx::Iterator iter(*this);
|
|
|
|
|
bool endFound = false;
|
|
|
|
|
while (iter.next()) {
|
|
|
|
|
if (iter.tag() == Tx::End) {
|
|
|
|
|
if (endFound)
|
|
|
|
|
break;
|
|
|
|
|
Tx tx = iter.prevTx();
|
|
|
|
|
int comp = tx.createHash().Compare(txid);
|
|
|
|
|
if (comp == 0) {
|
|
|
|
|
return tx;
|
|
|
|
|
} else if (!first && CTORlayout && comp > 0) {
|
|
|
|
|
break; // CTOR, stop searching in sorted list
|
|
|
|
|
}
|
|
|
|
|
first = false; // only the first transaction does not follow the CTOR layout.
|
|
|
|
|
endFound = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
endFound = false;
|
|
|
|
|
}
|
|
|
|
|
return Tx();
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 10:18:24 +01:00
|
|
|
MutableBlock Block::createOldBlock() const
|
2017-09-03 19:46:57 +02:00
|
|
|
{
|
|
|
|
|
if (!isFullBlock())
|
|
|
|
|
throw std::runtime_error("Not enough bytes to create a block");
|
2021-11-02 09:36:09 +01:00
|
|
|
MutableBlock answer;
|
2017-09-03 19:46:57 +02:00
|
|
|
CDataStream buf(m_data.begin(), m_data.end(), 0 , 0);
|
|
|
|
|
answer.Unserialize(buf, 0, 0);
|
2021-03-08 13:46:10 +01:00
|
|
|
return answer;
|
2017-09-03 19:46:57 +02:00
|
|
|
}
|
|
|
|
|
|
2026-04-15 21:35:08 +02:00
|
|
|
Block Block::fromOldBlock(const BlockHeader &header, Streaming::BufferPool *pool)
|
2017-09-03 19:46:57 +02:00
|
|
|
{
|
2026-04-15 21:35:08 +02:00
|
|
|
uint8_t data[80];
|
|
|
|
|
*reinterpret_cast<uint32_t*>(data) = header.nVersion;
|
|
|
|
|
memcpy(data + 4, header.hashPrevBlock.begin(), 32);
|
|
|
|
|
memcpy(data + 36, header.hashMerkleRoot.begin(), 32);
|
|
|
|
|
*reinterpret_cast<uint32_t*>(data + 68) = header.nTime;
|
|
|
|
|
*reinterpret_cast<uint32_t*>(data + 72) = header.nBits;
|
|
|
|
|
*reinterpret_cast<uint32_t*>(data + 76) = header.nNonce;
|
|
|
|
|
|
2017-09-03 19:46:57 +02:00
|
|
|
if (pool) {
|
2026-04-15 21:35:08 +02:00
|
|
|
pool->reserve(80);
|
|
|
|
|
pool->write(data, 80);
|
2021-11-02 10:18:24 +01:00
|
|
|
return Block(pool->commit());
|
2017-09-03 19:46:57 +02:00
|
|
|
}
|
2026-04-15 21:35:08 +02:00
|
|
|
Streaming::BufferPool pl(80);
|
|
|
|
|
pl.write(data, 80);
|
2021-11-02 10:18:24 +01:00
|
|
|
return Block(pl.commit());
|
2017-09-03 19:46:57 +02:00
|
|
|
}
|
|
|
|
|
|
2026-04-15 21:35:08 +02:00
|
|
|
BlockHeader Block::header() const
|
|
|
|
|
{
|
|
|
|
|
return BlockHeader(*this);
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 10:49:17 +01:00
|
|
|
Block Block::fromOldBlock(const BlockHeaderFields &block, Streaming::BufferPool *pool)
|
2017-09-03 19:46:57 +02:00
|
|
|
{
|
|
|
|
|
CSizeComputer sc(0, 0);
|
|
|
|
|
sc << block;
|
|
|
|
|
if (pool) {
|
|
|
|
|
pool->reserve(sc.size());
|
|
|
|
|
block.Serialize(*pool, 0, 0);
|
2021-11-02 10:18:24 +01:00
|
|
|
return Block(pool->commit());
|
2017-09-03 19:46:57 +02:00
|
|
|
}
|
|
|
|
|
Streaming::BufferPool pl(sc.size());
|
|
|
|
|
block.Serialize(pl, 0, 0);
|
2021-11-02 10:18:24 +01:00
|
|
|
return Block(pl.commit());
|
2017-09-03 19:46:57 +02:00
|
|
|
}
|
2026-04-15 21:35:08 +02:00
|
|
|
|
|
|
|
|
Block Block::fromOldBlock(const MutableBlock &block, Streaming::BufferPool *pool)
|
|
|
|
|
{
|
|
|
|
|
CSizeComputer sc(0, 0);
|
|
|
|
|
sc << block;
|
|
|
|
|
if (pool) {
|
|
|
|
|
pool->reserve(sc.size());
|
|
|
|
|
block.Serialize(*pool, 0, 0);
|
|
|
|
|
return Block(pool->commit());
|
|
|
|
|
}
|
|
|
|
|
Streaming::BufferPool pl(sc.size());
|
|
|
|
|
block.Serialize(pl, 0, 0);
|
|
|
|
|
return Block(pl.commit());
|
|
|
|
|
}
|