2020-05-20 12:11:23 +02:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Flowee project
|
2021-02-17 15:35:17 +01:00
|
|
|
* Copyright (C) 2017-2021 Tom Zander <tom@flowee.org>
|
2020-05-20 12:11:23 +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/>.
|
|
|
|
|
*/
|
|
|
|
|
#include "FastBlock.h"
|
|
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
|
#include <hash.h>
|
|
|
|
|
#include <streaming/streams.h>
|
|
|
|
|
#include <streaming/BufferPool.h>
|
|
|
|
|
|
|
|
|
|
#include "block.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FastBlock::FastBlock()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FastBlock::FastBlock(const Streaming::ConstBuffer &rawBlock)
|
|
|
|
|
: m_data(rawBlock)
|
|
|
|
|
{
|
|
|
|
|
if (rawBlock.size() < 80)
|
|
|
|
|
throw std::runtime_error("Block too small to fit header");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t FastBlock::blockVersion() const
|
|
|
|
|
{
|
|
|
|
|
return static_cast<int32_t>(le32toh(*((uint32_t*)m_data.begin())));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint256 FastBlock::previousBlockId() const
|
|
|
|
|
{
|
|
|
|
|
return uint256(m_data.begin() + 4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint256 FastBlock::merkleRoot() const
|
|
|
|
|
{
|
|
|
|
|
return uint256(m_data.begin() + 36);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t FastBlock::timestamp() const
|
|
|
|
|
{
|
|
|
|
|
return le32toh(*((uint32_t*)(m_data.begin() + 68)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t FastBlock::bits() const
|
|
|
|
|
{
|
|
|
|
|
return le32toh(*((uint32_t*)(m_data.begin() + 72)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t FastBlock::nonce() const
|
|
|
|
|
{
|
|
|
|
|
return le32toh(*((uint32_t*)(m_data.begin() + 76)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint256 FastBlock::createHash() const
|
|
|
|
|
{
|
|
|
|
|
assert(m_data.size() >= 80);
|
|
|
|
|
CHash256 ctx;
|
|
|
|
|
ctx.Write((const unsigned char*)m_data.begin(), 80);
|
|
|
|
|
uint256 result;
|
|
|
|
|
ctx.Finalize((unsigned char*)&result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FastBlock::findTransactions()
|
|
|
|
|
{
|
|
|
|
|
if (!m_transactions.empty())
|
|
|
|
|
return;
|
|
|
|
|
size_t pos = 80;
|
2021-02-17 15:35:17 +01:00
|
|
|
const uint64_t transactionCount = readCompactSize(m_data, pos);
|
|
|
|
|
if (transactionCount > 0xFFFFFF)
|
|
|
|
|
throw std::runtime_error("FastBlock::findTransactions: Tx count decoding failed");
|
2020-05-20 12:11:23 +02:00
|
|
|
std::vector<Tx> txs;
|
|
|
|
|
txs.reserve(transactionCount);
|
2021-02-17 15:35:17 +01:00
|
|
|
for (uint64_t i = 0; i < transactionCount; ++i) {
|
2020-05-20 12:11:23 +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-03-18 12:31:27 +01:00
|
|
|
Tx FastBlock::findTransaction(const uint256 &txid, bool CTORlayout) const
|
|
|
|
|
{
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-20 12:11:23 +02:00
|
|
|
CBlock FastBlock::createOldBlock() const
|
|
|
|
|
{
|
|
|
|
|
if (!isFullBlock())
|
|
|
|
|
throw std::runtime_error("Not enough bytes to create a block");
|
|
|
|
|
CBlock answer;
|
|
|
|
|
CDataStream buf(m_data.begin(), m_data.end(), 0 , 0);
|
|
|
|
|
answer.Unserialize(buf, 0, 0);
|
2021-03-18 12:31:27 +01:00
|
|
|
return answer;
|
2020-05-20 12:11:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FastBlock FastBlock::fromOldBlock(const CBlock &block, Streaming::BufferPool *pool)
|
|
|
|
|
{
|
|
|
|
|
CSizeComputer sc(0, 0);
|
|
|
|
|
sc << block;
|
|
|
|
|
if (pool) {
|
|
|
|
|
pool->reserve(sc.size());
|
|
|
|
|
block.Serialize(*pool, 0, 0);
|
|
|
|
|
return FastBlock(pool->commit());
|
|
|
|
|
}
|
|
|
|
|
Streaming::BufferPool pl(sc.size());
|
|
|
|
|
block.Serialize(pl, 0, 0);
|
|
|
|
|
return FastBlock(pl.commit());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FastBlock FastBlock::fromOldBlock(const CBlockHeader &block, Streaming::BufferPool *pool)
|
|
|
|
|
{
|
|
|
|
|
CSizeComputer sc(0, 0);
|
|
|
|
|
sc << block;
|
|
|
|
|
if (pool) {
|
|
|
|
|
pool->reserve(sc.size());
|
|
|
|
|
block.Serialize(*pool, 0, 0);
|
|
|
|
|
return FastBlock(pool->commit());
|
|
|
|
|
}
|
|
|
|
|
Streaming::BufferPool pl(sc.size());
|
|
|
|
|
block.Serialize(pl, 0, 0);
|
|
|
|
|
return FastBlock(pl.commit());
|
|
|
|
|
}
|