mirror of
https://github.com/SChernykh/p2pool.git
synced 2025-01-03 17:29:24 +00:00
Merge branch 'compact_broadcast'
This commit is contained in:
commit
aa14620a50
8 changed files with 195 additions and 59 deletions
|
@ -201,7 +201,7 @@ void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block.deserialize(data + sizeof(uint32_t), n, side_chain, uv_default_loop_checked()) == 0) {
|
if (block.deserialize(data + sizeof(uint32_t), n, side_chain, uv_default_loop_checked(), false) == 0) {
|
||||||
server.add_cached_block(block);
|
server.add_cached_block(block);
|
||||||
++blocks_loaded;
|
++blocks_loaded;
|
||||||
}
|
}
|
||||||
|
|
|
@ -568,7 +568,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||||
buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end());
|
buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end());
|
||||||
|
|
||||||
PoolBlock check;
|
PoolBlock check;
|
||||||
const int result = check.deserialize(buf.data(), buf.size(), m_pool->side_chain(), nullptr);
|
const int result = check.deserialize(buf.data(), buf.size(), m_pool->side_chain(), nullptr, false);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
|
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
|
||||||
}
|
}
|
||||||
|
@ -1104,7 +1104,7 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
|
||||||
buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end());
|
buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end());
|
||||||
|
|
||||||
PoolBlock check;
|
PoolBlock check;
|
||||||
const int result = check.deserialize(buf.data(), buf.size(), side_chain, nullptr);
|
const int result = check.deserialize(buf.data(), buf.size(), side_chain, nullptr, false);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
|
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -726,7 +726,7 @@ void P2PServer::remove_peer_from_list(const raw_ip& ip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void P2PServer::broadcast(const PoolBlock& block)
|
void P2PServer::broadcast(const PoolBlock& block, const PoolBlock* parent)
|
||||||
{
|
{
|
||||||
MinerData miner_data = m_pool->miner_data();
|
MinerData miner_data = m_pool->miner_data();
|
||||||
|
|
||||||
|
@ -766,13 +766,48 @@ void P2PServer::broadcast(const PoolBlock& block)
|
||||||
writeVarint(outputs_blob_size, data->pruned_blob);
|
writeVarint(outputs_blob_size, data->pruned_blob);
|
||||||
|
|
||||||
data->pruned_blob.insert(data->pruned_blob.end(), mainchain_data.begin() + outputs_offset + outputs_blob_size, mainchain_data.end());
|
data->pruned_blob.insert(data->pruned_blob.end(), mainchain_data.begin() + outputs_offset + outputs_blob_size, mainchain_data.end());
|
||||||
|
|
||||||
|
const size_t N = block.m_transactions.size();
|
||||||
|
if ((N > 1) && parent && (parent->m_transactions.size() > 1)) {
|
||||||
|
unordered_map<hash, size_t> parent_transactions;
|
||||||
|
parent_transactions.reserve(parent->m_transactions.size());
|
||||||
|
|
||||||
|
for (size_t i = 1; i < parent->m_transactions.size(); ++i) {
|
||||||
|
parent_transactions.emplace(parent->m_transactions[i], i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reserve 1 additional byte per transaction to be ready for the worst case (all transactions are different in the parent block)
|
||||||
|
data->compact_blob.reserve(data->pruned_blob.capacity() + (N - 1));
|
||||||
|
|
||||||
|
// Copy pruned_blob without the transaction list
|
||||||
|
data->compact_blob.assign(data->pruned_blob.begin(), data->pruned_blob.end() - (N - 1) * HASH_SIZE);
|
||||||
|
|
||||||
|
// Process transaction hashes one by one
|
||||||
|
size_t num_found = 0;
|
||||||
|
for (size_t i = 1; i < N; ++i) {
|
||||||
|
const hash& tx = block.m_transactions[i];
|
||||||
|
auto it = parent_transactions.find(tx);
|
||||||
|
if (it != parent_transactions.end()) {
|
||||||
|
writeVarint(it->second, data->compact_blob);
|
||||||
|
++num_found;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
data->compact_blob.push_back(0);
|
||||||
|
data->compact_blob.insert(data->compact_blob.end(), tx.h, tx.h + HASH_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOGINFO(6, "compact blob: " << num_found << '/' << (N - 1) << " transactions were found in the parent block");
|
||||||
|
|
||||||
|
data->compact_blob.insert(data->compact_blob.end(), sidechain_data.begin(), sidechain_data.end());
|
||||||
|
}
|
||||||
|
|
||||||
data->pruned_blob.insert(data->pruned_blob.end(), sidechain_data.begin(), sidechain_data.end());
|
data->pruned_blob.insert(data->pruned_blob.end(), sidechain_data.begin(), sidechain_data.end());
|
||||||
|
|
||||||
data->ancestor_hashes.reserve(block.m_uncles.size() + 1);
|
data->ancestor_hashes.reserve(block.m_uncles.size() + 1);
|
||||||
data->ancestor_hashes = block.m_uncles;
|
data->ancestor_hashes = block.m_uncles;
|
||||||
data->ancestor_hashes.push_back(block.m_parent);
|
data->ancestor_hashes.push_back(block.m_parent);
|
||||||
|
|
||||||
LOGINFO(5, "Broadcasting block " << block.m_sidechainId << " (height " << block.m_sidechainHeight << "): " << data->pruned_blob.size() << '/' << data->blob.size() << " bytes (pruned/full)");
|
LOGINFO(5, "Broadcasting block " << block.m_sidechainId << " (height " << block.m_sidechainHeight << "): " << data->compact_blob.size() << '/' << data->pruned_blob.size() << '/' << data->blob.size() << " bytes (compact/pruned/full)");
|
||||||
|
|
||||||
{
|
{
|
||||||
MutexLock lock(m_broadcastLock);
|
MutexLock lock(m_broadcastLock);
|
||||||
|
@ -840,6 +875,7 @@ void P2PServer::on_broadcast()
|
||||||
uint8_t* p = p0;
|
uint8_t* p = p0;
|
||||||
|
|
||||||
bool send_pruned = true;
|
bool send_pruned = true;
|
||||||
|
bool send_compact = (client->m_protocolVersion >= PROTOCOL_VERSION_1_1) && !data->compact_blob.empty() && (data->compact_blob.size() < data->pruned_blob.size());
|
||||||
|
|
||||||
const hash* a = client->m_broadcastedHashes;
|
const hash* a = client->m_broadcastedHashes;
|
||||||
const hash* b = client->m_broadcastedHashes + array_size(&P2PClient::m_broadcastedHashes);
|
const hash* b = client->m_broadcastedHashes + array_size(&P2PClient::m_broadcastedHashes);
|
||||||
|
@ -847,25 +883,27 @@ void P2PServer::on_broadcast()
|
||||||
for (const hash& id : data->ancestor_hashes) {
|
for (const hash& id : data->ancestor_hashes) {
|
||||||
if (std::find(a, b, id) == b) {
|
if (std::find(a, b, id) == b) {
|
||||||
send_pruned = false;
|
send_pruned = false;
|
||||||
|
send_compact = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (send_pruned) {
|
if (send_pruned) {
|
||||||
LOGINFO(6, "sending BLOCK_BROADCAST (pruned) to " << log::Gray() << static_cast<char*>(client->m_addrString));
|
LOGINFO(6, "sending BLOCK_BROADCAST (" << (send_compact ? "compact" : "pruned") << ") to " << log::Gray() << static_cast<char*>(client->m_addrString));
|
||||||
|
const std::vector<uint8_t>& blob = send_compact ? data->compact_blob : data->pruned_blob;
|
||||||
|
|
||||||
const uint32_t len = static_cast<uint32_t>(data->pruned_blob.size());
|
const uint32_t len = static_cast<uint32_t>(blob.size());
|
||||||
if (buf_size < SEND_BUF_MIN_SIZE + 1 + sizeof(uint32_t) + len) {
|
if (buf_size < SEND_BUF_MIN_SIZE + 1 + sizeof(uint32_t) + len) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST);
|
*(p++) = static_cast<uint8_t>(send_compact ? MessageId::BLOCK_BROADCAST_COMPACT : MessageId::BLOCK_BROADCAST);
|
||||||
|
|
||||||
memcpy(p, &len, sizeof(uint32_t));
|
memcpy(p, &len, sizeof(uint32_t));
|
||||||
p += sizeof(uint32_t);
|
p += sizeof(uint32_t);
|
||||||
|
|
||||||
if (len) {
|
if (len) {
|
||||||
memcpy(p, data->pruned_blob.data(), len);
|
memcpy(p, blob.data(), len);
|
||||||
p += len;
|
p += len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -940,7 +978,7 @@ int P2PServer::listen_port() const
|
||||||
return params.m_p2pExternalPort ? params.m_p2pExternalPort : m_listenPort;
|
return params.m_p2pExternalPort ? params.m_p2pExternalPort : m_listenPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size)
|
int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size, bool compact)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
|
@ -949,7 +987,7 @@ int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size)
|
||||||
result = m_blockDeserializeResult;
|
result = m_blockDeserializeResult;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result = m_block->deserialize(buf, size, m_pool->side_chain(), &m_loop);
|
result = m_block->deserialize(buf, size, m_pool->side_chain(), &m_loop, compact);
|
||||||
m_blockDeserializeBuf.assign(buf, buf + size);
|
m_blockDeserializeBuf.assign(buf, buf + size);
|
||||||
m_blockDeserializeResult = result;
|
m_blockDeserializeResult = result;
|
||||||
m_lookForMissingBlocks = true;
|
m_lookForMissingBlocks = true;
|
||||||
|
@ -1134,6 +1172,7 @@ P2PServer::P2PClient::P2PClient()
|
||||||
, m_nextOutgoingPeerListRequest(0)
|
, m_nextOutgoingPeerListRequest(0)
|
||||||
, m_lastPeerListRequestTime{}
|
, m_lastPeerListRequestTime{}
|
||||||
, m_peerListPendingRequests(0)
|
, m_peerListPendingRequests(0)
|
||||||
|
, m_protocolVersion(PROTOCOL_VERSION_1_0)
|
||||||
, m_pingTime(-1)
|
, m_pingTime(-1)
|
||||||
, m_blockPendingRequests(0)
|
, m_blockPendingRequests(0)
|
||||||
, m_chainTipBlockRequest(false)
|
, m_chainTipBlockRequest(false)
|
||||||
|
@ -1179,6 +1218,7 @@ void P2PServer::P2PClient::reset()
|
||||||
m_nextOutgoingPeerListRequest = 0;
|
m_nextOutgoingPeerListRequest = 0;
|
||||||
m_lastPeerListRequestTime = {};
|
m_lastPeerListRequestTime = {};
|
||||||
m_peerListPendingRequests = 0;
|
m_peerListPendingRequests = 0;
|
||||||
|
m_protocolVersion = PROTOCOL_VERSION_1_0;
|
||||||
m_pingTime = -1;
|
m_pingTime = -1;
|
||||||
m_blockPendingRequests = 0;
|
m_blockPendingRequests = 0;
|
||||||
m_chainTipBlockRequest = false;
|
m_chainTipBlockRequest = false;
|
||||||
|
@ -1365,16 +1405,20 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageId::BLOCK_BROADCAST:
|
case MessageId::BLOCK_BROADCAST:
|
||||||
LOGINFO(6, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent BLOCK_BROADCAST");
|
case MessageId::BLOCK_BROADCAST_COMPACT:
|
||||||
|
{
|
||||||
|
const bool compact = (id == MessageId::BLOCK_BROADCAST_COMPACT);
|
||||||
|
LOGINFO(6, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent " << (compact ? "BLOCK_BROADCAST_COMPACT" : "BLOCK_BROADCAST"));
|
||||||
|
|
||||||
if (bytes_left >= 1 + sizeof(uint32_t)) {
|
if (bytes_left >= 1 + sizeof(uint32_t)) {
|
||||||
const uint32_t block_size = read_unaligned(reinterpret_cast<uint32_t*>(buf + 1));
|
const uint32_t block_size = read_unaligned(reinterpret_cast<uint32_t*>(buf + 1));
|
||||||
if (bytes_left >= 1 + sizeof(uint32_t) + block_size) {
|
if (bytes_left >= 1 + sizeof(uint32_t) + block_size) {
|
||||||
bytes_read = 1 + sizeof(uint32_t) + block_size;
|
bytes_read = 1 + sizeof(uint32_t) + block_size;
|
||||||
if (!on_block_broadcast(buf + 1 + sizeof(uint32_t), block_size)) {
|
if (!on_block_broadcast(buf + 1 + sizeof(uint32_t), block_size, compact)) {
|
||||||
ban(DEFAULT_BAN_TIME);
|
ban(DEFAULT_BAN_TIME);
|
||||||
server->remove_peer_from_list(this);
|
server->remove_peer_from_list(this);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1863,7 +1907,7 @@ bool P2PServer::P2PClient::on_block_response(const uint8_t* buf, uint32_t size)
|
||||||
|
|
||||||
MutexLock lock(server->m_blockLock);
|
MutexLock lock(server->m_blockLock);
|
||||||
|
|
||||||
const int result = server->deserialize_block(buf, size);
|
const int result = server->deserialize_block(buf, size, false);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " sent an invalid block, error " << result);
|
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " sent an invalid block, error " << result);
|
||||||
return false;
|
return false;
|
||||||
|
@ -1892,7 +1936,7 @@ bool P2PServer::P2PClient::on_block_response(const uint8_t* buf, uint32_t size)
|
||||||
return handle_incoming_block_async(server->get_block(), max_time_delta);
|
return handle_incoming_block_async(server->get_block(), max_time_delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size, bool compact)
|
||||||
{
|
{
|
||||||
if (!size) {
|
if (!size) {
|
||||||
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " broadcasted an empty block");
|
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " broadcasted an empty block");
|
||||||
|
@ -1903,7 +1947,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||||
|
|
||||||
MutexLock lock(server->m_blockLock);
|
MutexLock lock(server->m_blockLock);
|
||||||
|
|
||||||
const int result = server->deserialize_block(buf, size);
|
const int result = server->deserialize_block(buf, size, compact);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " sent an invalid block, error " << result);
|
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " sent an invalid block, error " << result);
|
||||||
return false;
|
return false;
|
||||||
|
@ -1958,6 +2002,7 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
|
||||||
{
|
{
|
||||||
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
||||||
const uint64_t cur_time = seconds_since_epoch();
|
const uint64_t cur_time = seconds_since_epoch();
|
||||||
|
const bool first = (m_prevIncomingPeerListRequest == 0);
|
||||||
|
|
||||||
// Allow peer list requests no more than once every 30 seconds
|
// Allow peer list requests no more than once every 30 seconds
|
||||||
if (cur_time - m_prevIncomingPeerListRequest < 30) {
|
if (cur_time - m_prevIncomingPeerListRequest < 30) {
|
||||||
|
@ -2002,6 +2047,24 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Protocol version message:
|
||||||
|
// - IPv4 address = 255.255.255.255
|
||||||
|
// - port = 65535
|
||||||
|
// - first 12 bytes of the 16-byte raw IP address are ignored by older clients if it's IPv4
|
||||||
|
// - use first 4 bytes of the 16-byte raw IP address to send supported protocol version
|
||||||
|
if (first) {
|
||||||
|
LOGINFO(5, "sending protocol version " << (SUPPORTED_PROTOCOL_VERSION >> 16) << '.' << (SUPPORTED_PROTOCOL_VERSION & 0xFFFF) << " to peer " << log::Gray() << static_cast<char*>(m_addrString));
|
||||||
|
|
||||||
|
peers[0] = {};
|
||||||
|
*reinterpret_cast<uint32_t*>(peers[0].m_addr.data) = SUPPORTED_PROTOCOL_VERSION;
|
||||||
|
*reinterpret_cast<uint32_t*>(peers[0].m_addr.data + 12) = 0xFFFFFFFFU;
|
||||||
|
peers[0].m_port = 0xFFFF;
|
||||||
|
|
||||||
|
if (num_selected_peers == 0) {
|
||||||
|
num_selected_peers = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return server->send(this,
|
return server->send(this,
|
||||||
[this, &peers, num_selected_peers](void* buf, size_t buf_size) -> size_t
|
[this, &peers, num_selected_peers](void* buf, size_t buf_size) -> size_t
|
||||||
{
|
{
|
||||||
|
@ -2033,7 +2096,7 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
|
bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf)
|
||||||
{
|
{
|
||||||
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
||||||
const uint64_t cur_time = seconds_since_epoch();
|
const uint64_t cur_time = seconds_since_epoch();
|
||||||
|
@ -2048,6 +2111,10 @@ bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
|
||||||
memcpy(ip.data, buf, sizeof(ip.data));
|
memcpy(ip.data, buf, sizeof(ip.data));
|
||||||
buf += sizeof(ip.data);
|
buf += sizeof(ip.data);
|
||||||
|
|
||||||
|
int port = 0;
|
||||||
|
memcpy(&port, buf, 2);
|
||||||
|
buf += 2;
|
||||||
|
|
||||||
// Treat IPv4-mapped addresses as regular IPv4 addresses
|
// Treat IPv4-mapped addresses as regular IPv4 addresses
|
||||||
if (is_v6 && ip.is_ipv4_prefix()) {
|
if (is_v6 && ip.is_ipv4_prefix()) {
|
||||||
is_v6 = false;
|
is_v6 = false;
|
||||||
|
@ -2057,8 +2124,12 @@ bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
|
||||||
const uint32_t b = ip.data[12];
|
const uint32_t b = ip.data[12];
|
||||||
if ((b == 0) || (b >= 224)) {
|
if ((b == 0) || (b >= 224)) {
|
||||||
// Ignore 0.0.0.0/8 (special-purpose range for "this network") and 224.0.0.0/3 (IP multicast and reserved ranges)
|
// Ignore 0.0.0.0/8 (special-purpose range for "this network") and 224.0.0.0/3 (IP multicast and reserved ranges)
|
||||||
// Some values in these ranges will be used to enable future P2Pool binary protocol versions
|
|
||||||
buf += 2;
|
// Check for protocol version message
|
||||||
|
if ((*reinterpret_cast<uint32_t*>(ip.data + 12) == 0xFFFFFFFFU) && (port == 0xFFFF)) {
|
||||||
|
m_protocolVersion = *reinterpret_cast<uint32_t*>(ip.data);
|
||||||
|
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " supports protocol version " << (m_protocolVersion >> 16) << '.' << (m_protocolVersion & 0xFFFF));
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2068,10 +2139,6 @@ bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
|
||||||
ip.data[11] = 0xFF;
|
ip.data[11] = 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
int port = 0;
|
|
||||||
memcpy(&port, buf, 2);
|
|
||||||
buf += 2;
|
|
||||||
|
|
||||||
bool already_added = false;
|
bool already_added = false;
|
||||||
for (Peer& p : server->m_peerList) {
|
for (Peer& p : server->m_peerList) {
|
||||||
if ((p.m_isV6 == is_v6) && (p.m_addr == ip)) {
|
if ((p.m_isV6 == is_v6) && (p.m_addr == ip)) {
|
||||||
|
|
|
@ -30,6 +30,11 @@ static constexpr size_t PEER_LIST_RESPONSE_MAX_PEERS = 16;
|
||||||
static constexpr int DEFAULT_P2P_PORT = 37889;
|
static constexpr int DEFAULT_P2P_PORT = 37889;
|
||||||
static constexpr int DEFAULT_P2P_PORT_MINI = 37888;
|
static constexpr int DEFAULT_P2P_PORT_MINI = 37888;
|
||||||
|
|
||||||
|
static constexpr uint32_t PROTOCOL_VERSION_1_0 = 0x00010000UL;
|
||||||
|
static constexpr uint32_t PROTOCOL_VERSION_1_1 = 0x00010001UL;
|
||||||
|
|
||||||
|
static constexpr uint32_t SUPPORTED_PROTOCOL_VERSION = PROTOCOL_VERSION_1_1;
|
||||||
|
|
||||||
class P2PServer : public TCPServer<P2P_BUF_SIZE, P2P_BUF_SIZE>
|
class P2PServer : public TCPServer<P2P_BUF_SIZE, P2P_BUF_SIZE>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -42,6 +47,7 @@ public:
|
||||||
BLOCK_BROADCAST = 5,
|
BLOCK_BROADCAST = 5,
|
||||||
PEER_LIST_REQUEST = 6,
|
PEER_LIST_REQUEST = 6,
|
||||||
PEER_LIST_RESPONSE = 7,
|
PEER_LIST_RESPONSE = 7,
|
||||||
|
BLOCK_BROADCAST_COMPACT = 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit P2PServer(p2pool *pool);
|
explicit P2PServer(p2pool *pool);
|
||||||
|
@ -94,9 +100,9 @@ public:
|
||||||
bool on_listen_port(const uint8_t* buf);
|
bool on_listen_port(const uint8_t* buf);
|
||||||
bool on_block_request(const uint8_t* buf);
|
bool on_block_request(const uint8_t* buf);
|
||||||
bool on_block_response(const uint8_t* buf, uint32_t size);
|
bool on_block_response(const uint8_t* buf, uint32_t size);
|
||||||
bool on_block_broadcast(const uint8_t* buf, uint32_t size);
|
bool on_block_broadcast(const uint8_t* buf, uint32_t size, bool compact);
|
||||||
bool on_peer_list_request(const uint8_t* buf);
|
bool on_peer_list_request(const uint8_t* buf);
|
||||||
bool on_peer_list_response(const uint8_t* buf) const;
|
bool on_peer_list_response(const uint8_t* buf);
|
||||||
|
|
||||||
bool handle_incoming_block_async(const PoolBlock* block, uint64_t max_time_delta = 0);
|
bool handle_incoming_block_async(const PoolBlock* block, uint64_t max_time_delta = 0);
|
||||||
void handle_incoming_block(p2pool* pool, PoolBlock& block, const uint32_t reset_counter, const raw_ip& addr, std::vector<hash>& missing_blocks);
|
void handle_incoming_block(p2pool* pool, PoolBlock& block, const uint32_t reset_counter, const raw_ip& addr, std::vector<hash>& missing_blocks);
|
||||||
|
@ -117,6 +123,9 @@ public:
|
||||||
uint64_t m_nextOutgoingPeerListRequest;
|
uint64_t m_nextOutgoingPeerListRequest;
|
||||||
std::chrono::high_resolution_clock::time_point m_lastPeerListRequestTime;
|
std::chrono::high_resolution_clock::time_point m_lastPeerListRequestTime;
|
||||||
int m_peerListPendingRequests;
|
int m_peerListPendingRequests;
|
||||||
|
|
||||||
|
uint32_t m_protocolVersion;
|
||||||
|
|
||||||
int64_t m_pingTime;
|
int64_t m_pingTime;
|
||||||
|
|
||||||
int m_blockPendingRequests;
|
int m_blockPendingRequests;
|
||||||
|
@ -130,7 +139,7 @@ public:
|
||||||
std::atomic<uint32_t> m_broadcastedHashesIndex{ 0 };
|
std::atomic<uint32_t> m_broadcastedHashesIndex{ 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
void broadcast(const PoolBlock& block);
|
void broadcast(const PoolBlock& block, const PoolBlock* parent);
|
||||||
uint64_t get_random64();
|
uint64_t get_random64();
|
||||||
uint64_t get_peerId() const { return m_peerId; }
|
uint64_t get_peerId() const { return m_peerId; }
|
||||||
|
|
||||||
|
@ -146,7 +155,7 @@ public:
|
||||||
void set_max_outgoing_peers(uint32_t n) { m_maxOutgoingPeers = std::min(std::max(n, 10U), 450U); }
|
void set_max_outgoing_peers(uint32_t n) { m_maxOutgoingPeers = std::min(std::max(n, 10U), 450U); }
|
||||||
void set_max_incoming_peers(uint32_t n) { m_maxIncomingPeers = std::min(std::max(n, 10U), 450U); }
|
void set_max_incoming_peers(uint32_t n) { m_maxIncomingPeers = std::min(std::max(n, 10U), 450U); }
|
||||||
|
|
||||||
int deserialize_block(const uint8_t* buf, uint32_t size);
|
int deserialize_block(const uint8_t* buf, uint32_t size, bool compact);
|
||||||
const PoolBlock* get_block() const { return m_block; }
|
const PoolBlock* get_block() const { return m_block; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -212,6 +221,7 @@ private:
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> blob;
|
std::vector<uint8_t> blob;
|
||||||
std::vector<uint8_t> pruned_blob;
|
std::vector<uint8_t> pruned_blob;
|
||||||
|
std::vector<uint8_t> compact_blob;
|
||||||
std::vector<hash> ancestor_hashes;
|
std::vector<hash> ancestor_hashes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,7 @@ struct PoolBlock
|
||||||
std::vector<uint8_t> serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const;
|
std::vector<uint8_t> serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const;
|
||||||
std::vector<uint8_t> serialize_sidechain_data() const;
|
std::vector<uint8_t> serialize_sidechain_data() const;
|
||||||
|
|
||||||
int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop);
|
int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop, bool compact);
|
||||||
void reset_offchain_data();
|
void reset_offchain_data();
|
||||||
|
|
||||||
bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash);
|
bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash);
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace p2pool {
|
||||||
// Since data here can come from external and possibly malicious sources, check everything
|
// Since data here can come from external and possibly malicious sources, check everything
|
||||||
// Only the syntax (i.e. the serialized block binary format) and the keccak hash are checked here
|
// Only the syntax (i.e. the serialized block binary format) and the keccak hash are checked here
|
||||||
// Semantics must also be checked elsewhere before accepting the block (PoW, reward split between miners, difficulty calculation and so on)
|
// Semantics must also be checked elsewhere before accepting the block (PoW, reward split between miners, difficulty calculation and so on)
|
||||||
int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop)
|
int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop, bool compact)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
// Sanity check
|
// Sanity check
|
||||||
|
@ -193,23 +193,60 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
|
||||||
uint64_t num_transactions;
|
uint64_t num_transactions;
|
||||||
READ_VARINT(num_transactions);
|
READ_VARINT(num_transactions);
|
||||||
|
|
||||||
if (num_transactions > std::numeric_limits<uint64_t>::max() / HASH_SIZE) return __LINE__;
|
const int transactions_offset = static_cast<int>(data - data_begin);
|
||||||
if (static_cast<uint64_t>(data_end - data) < num_transactions * HASH_SIZE) return __LINE__;
|
|
||||||
|
|
||||||
m_transactions.resize(1);
|
std::vector<uint64_t> parent_indices;
|
||||||
m_transactions.reserve(num_transactions + 1);
|
if (compact) {
|
||||||
|
if (static_cast<uint64_t>(data_end - data) < num_transactions) return __LINE__;
|
||||||
|
|
||||||
for (uint64_t i = 0; i < num_transactions; ++i) {
|
m_transactions.resize(1);
|
||||||
hash id;
|
parent_indices.resize(1);
|
||||||
READ_BUF(id.h, HASH_SIZE);
|
|
||||||
m_transactions.emplace_back(std::move(id));
|
// limit reserved memory size because we can't check "num_transactions" properly here
|
||||||
|
const uint64_t k = std::min<uint64_t>(num_transactions + 1, 256);
|
||||||
|
m_transactions.reserve(k);
|
||||||
|
parent_indices.reserve(k);
|
||||||
|
|
||||||
|
for (uint64_t i = 0; i < num_transactions; ++i) {
|
||||||
|
uint64_t parent_index;
|
||||||
|
READ_VARINT(parent_index);
|
||||||
|
|
||||||
|
hash id;
|
||||||
|
if (parent_index == 0) {
|
||||||
|
READ_BUF(id.h, HASH_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_transactions.emplace_back(id);
|
||||||
|
parent_indices.emplace_back(parent_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (num_transactions > std::numeric_limits<uint64_t>::max() / HASH_SIZE) return __LINE__;
|
||||||
|
if (static_cast<uint64_t>(data_end - data) < num_transactions * HASH_SIZE) return __LINE__;
|
||||||
|
|
||||||
|
m_transactions.resize(1);
|
||||||
|
m_transactions.reserve(num_transactions + 1);
|
||||||
|
|
||||||
|
for (uint64_t i = 0; i < num_transactions; ++i) {
|
||||||
|
hash id;
|
||||||
|
READ_BUF(id.h, HASH_SIZE);
|
||||||
|
m_transactions.emplace_back(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int transactions_actual_blob_size = static_cast<int>(data - data_begin) - transactions_offset;
|
||||||
|
const int transactions_blob_size = static_cast<int>(num_transactions) * HASH_SIZE;
|
||||||
|
const int transactions_blob_size_diff = transactions_blob_size - transactions_actual_blob_size;
|
||||||
|
|
||||||
|
m_transactions.shrink_to_fit();
|
||||||
|
|
||||||
#if POOL_BLOCK_DEBUG
|
#if POOL_BLOCK_DEBUG
|
||||||
m_mainChainDataDebug.reserve((data - data_begin) + outputs_blob_size_diff);
|
m_mainChainDataDebug.reserve((data - data_begin) + outputs_blob_size_diff + transactions_blob_size_diff);
|
||||||
m_mainChainDataDebug.assign(data_begin, data_begin + outputs_offset);
|
m_mainChainDataDebug.assign(data_begin, data_begin + outputs_offset);
|
||||||
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), outputs_blob_size, 0);
|
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), outputs_blob_size, 0);
|
||||||
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), data_begin + outputs_offset + outputs_actual_blob_size, data);
|
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), data_begin + outputs_offset + outputs_actual_blob_size, data_begin + transactions_offset);
|
||||||
|
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), transactions_blob_size, 0);
|
||||||
|
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), data_begin + transactions_offset + transactions_actual_blob_size, data);
|
||||||
|
|
||||||
const uint8_t* sidechain_data_begin = data;
|
const uint8_t* sidechain_data_begin = data;
|
||||||
#endif
|
#endif
|
||||||
|
@ -239,6 +276,23 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
|
||||||
|
|
||||||
READ_BUF(m_parent.h, HASH_SIZE);
|
READ_BUF(m_parent.h, HASH_SIZE);
|
||||||
|
|
||||||
|
if (compact) {
|
||||||
|
const PoolBlock* parent = sidechain.find_block(m_parent);
|
||||||
|
if (!parent) {
|
||||||
|
return __LINE__;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint64_t i = 1, n = m_transactions.size(); i < n; ++i) {
|
||||||
|
const uint64_t parent_index = parent_indices[i];
|
||||||
|
if (parent_index) {
|
||||||
|
if (parent_index >= parent->m_transactions.size()) {
|
||||||
|
return __LINE__;
|
||||||
|
}
|
||||||
|
m_transactions[i] = parent->m_transactions[parent_index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t num_uncles;
|
uint64_t num_uncles;
|
||||||
READ_VARINT(num_uncles);
|
READ_VARINT(num_uncles);
|
||||||
|
|
||||||
|
@ -251,7 +305,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
|
||||||
for (uint64_t i = 0; i < num_uncles; ++i) {
|
for (uint64_t i = 0; i < num_uncles; ++i) {
|
||||||
hash id;
|
hash id;
|
||||||
READ_BUF(id.h, HASH_SIZE);
|
READ_BUF(id.h, HASH_SIZE);
|
||||||
m_uncles.emplace_back(std::move(id));
|
m_uncles.emplace_back(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
READ_VARINT(m_sidechainHeight);
|
READ_VARINT(m_sidechainHeight);
|
||||||
|
@ -279,14 +333,18 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
|
||||||
return __LINE__;
|
return __LINE__;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint8_t* transactions_blob = reinterpret_cast<uint8_t*>(m_transactions.data() + 1);
|
||||||
|
|
||||||
#if POOL_BLOCK_DEBUG
|
#if POOL_BLOCK_DEBUG
|
||||||
memcpy(m_mainChainDataDebug.data() + outputs_offset, outputs_blob.data(), outputs_blob_size);
|
memcpy(m_mainChainDataDebug.data() + outputs_offset, outputs_blob.data(), outputs_blob_size);
|
||||||
|
memcpy(m_mainChainDataDebug.data() + transactions_offset + outputs_blob_size_diff, transactions_blob, transactions_blob_size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
hash check;
|
hash check;
|
||||||
const std::vector<uint8_t>& consensus_id = sidechain.consensus_id();
|
const std::vector<uint8_t>& consensus_id = sidechain.consensus_id();
|
||||||
|
const int data_size = static_cast<int>((data_end - data_begin) + outputs_blob_size_diff + transactions_blob_size_diff);
|
||||||
keccak_custom(
|
keccak_custom(
|
||||||
[nonce_offset, extra_nonce_offset, sidechain_hash_offset, data_begin, data_end, &consensus_id, &outputs_blob, outputs_blob_size_diff, outputs_offset, outputs_blob_size](int offset) -> uint8_t
|
[nonce_offset, extra_nonce_offset, sidechain_hash_offset, data_begin, data_size, &consensus_id, &outputs_blob, outputs_blob_size_diff, outputs_offset, outputs_blob_size, transactions_blob, transactions_blob_size_diff, transactions_offset, transactions_blob_size](int offset) -> uint8_t
|
||||||
{
|
{
|
||||||
uint32_t k = static_cast<uint32_t>(offset - nonce_offset);
|
uint32_t k = static_cast<uint32_t>(offset - nonce_offset);
|
||||||
if (k < NONCE_SIZE) {
|
if (k < NONCE_SIZE) {
|
||||||
|
@ -303,24 +361,25 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int data_size = static_cast<int>((data_end - data_begin) + outputs_blob_size_diff);
|
|
||||||
if (offset < data_size) {
|
if (offset < data_size) {
|
||||||
if (offset < outputs_offset) {
|
if (offset < outputs_offset) {
|
||||||
return data_begin[offset];
|
return data_begin[offset];
|
||||||
}
|
}
|
||||||
else if (offset < outputs_offset + outputs_blob_size) {
|
else if (offset < outputs_offset + outputs_blob_size) {
|
||||||
const int tmp = offset - outputs_offset;
|
return outputs_blob[offset - outputs_offset];
|
||||||
return outputs_blob[tmp];
|
|
||||||
}
|
}
|
||||||
else {
|
else if (offset < transactions_offset + outputs_blob_size_diff) {
|
||||||
return data_begin[offset - outputs_blob_size_diff];
|
return data_begin[offset - outputs_blob_size_diff];
|
||||||
}
|
}
|
||||||
|
else if (offset < transactions_offset + outputs_blob_size_diff + transactions_blob_size) {
|
||||||
|
return transactions_blob[offset - (transactions_offset + outputs_blob_size_diff)];
|
||||||
|
}
|
||||||
|
return data_begin[offset - outputs_blob_size_diff - transactions_blob_size_diff];
|
||||||
}
|
}
|
||||||
offset -= data_size;
|
|
||||||
|
|
||||||
return consensus_id[offset];
|
return consensus_id[offset - data_size];
|
||||||
},
|
},
|
||||||
static_cast<int>(size + outputs_blob_size_diff + consensus_id.size()), check.h, HASH_SIZE);
|
static_cast<int>(size + outputs_blob_size_diff + transactions_blob_size_diff + consensus_id.size()), check.h, HASH_SIZE);
|
||||||
|
|
||||||
if (check != m_sidechainId) {
|
if (check != m_sidechainId) {
|
||||||
return __LINE__;
|
return __LINE__;
|
||||||
|
|
|
@ -1275,7 +1275,7 @@ void SideChain::verify_loop(PoolBlock* block)
|
||||||
if (block->m_wantBroadcast && !block->m_broadcasted) {
|
if (block->m_wantBroadcast && !block->m_broadcasted) {
|
||||||
block->m_broadcasted = true;
|
block->m_broadcasted = true;
|
||||||
if (server && (block->m_depth < UNCLE_BLOCK_DEPTH)) {
|
if (server && (block->m_depth < UNCLE_BLOCK_DEPTH)) {
|
||||||
server->broadcast(*block);
|
server->broadcast(*block, get_parent(block));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1674,7 +1674,7 @@ void SideChain::update_chain_tip(const PoolBlock* block)
|
||||||
|
|
||||||
if (p2pServer() && block->m_wantBroadcast && !block->m_broadcasted) {
|
if (p2pServer() && block->m_wantBroadcast && !block->m_broadcasted) {
|
||||||
block->m_broadcasted = true;
|
block->m_broadcasted = true;
|
||||||
p2pServer()->broadcast(*block);
|
p2pServer()->broadcast(*block, get_parent(block));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ TEST(pool_block, deserialize)
|
||||||
f.read(reinterpret_cast<char*>(buf.data()), buf.size());
|
f.read(reinterpret_cast<char*>(buf.data()), buf.size());
|
||||||
ASSERT_EQ(f.good(), true);
|
ASSERT_EQ(f.good(), true);
|
||||||
|
|
||||||
ASSERT_EQ(b.deserialize(buf.data(), buf.size(), sidechain, nullptr), 0);
|
ASSERT_EQ(b.deserialize(buf.data(), buf.size(), sidechain, nullptr, false), 0);
|
||||||
|
|
||||||
size_t header_size, miner_tx_size;
|
size_t header_size, miner_tx_size;
|
||||||
int outputs_offset, outputs_blob_size;
|
int outputs_offset, outputs_blob_size;
|
||||||
|
@ -137,7 +137,7 @@ TEST(pool_block, verify)
|
||||||
p += sizeof(uint32_t);
|
p += sizeof(uint32_t);
|
||||||
|
|
||||||
ASSERT_TRUE(p + n <= e);
|
ASSERT_TRUE(p + n <= e);
|
||||||
ASSERT_EQ(b.deserialize(p, n, sidechain, nullptr), 0);
|
ASSERT_EQ(b.deserialize(p, n, sidechain, nullptr, false), 0);
|
||||||
p += n;
|
p += n;
|
||||||
|
|
||||||
sidechain.add_block(b);
|
sidechain.add_block(b);
|
||||||
|
|
Loading…
Reference in a new issue