void hash256 (uint8_t* dest, const uint8_t* src, size_t n) { CSHA256 hash; hash.Write(src, n); hash.Finalize(dest); hash.Reset(); hash.Write(dest, 32); hash.Finalize(dest); }
void hash160 (uint8_t* dest, const uint8_t* src, size_t n) { CSHA256 hash; hash.Write(src, n); hash.Finalize(dest); CRIPEMD160 hashR; hashR.Write(dest, 32); hashR.Finalize(dest); }
void CBlockHeaderAndShortTxIDs::FillShortTxIDSelector() const { CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); stream << header << nonce; CSHA256 hasher; hasher.Write((unsigned char*)&(*stream.begin()), stream.end() - stream.begin()); uint256 shorttxidhash; hasher.Finalize(shorttxidhash.begin()); shorttxidk0 = shorttxidhash.GetUint64(0); shorttxidk1 = shorttxidhash.GetUint64(1); }
// Sprout uint256 PRF(bool a, bool b, bool c, bool d, const uint252& x, const uint256& y) { uint256 res; unsigned char blob[64]; memcpy(&blob[0], x.begin(), 32); memcpy(&blob[32], y.begin(), 32); blob[0] &= 0x0F; blob[0] |= (a ? 1 << 7 : 0) | (b ? 1 << 6 : 0) | (c ? 1 << 5 : 0) | (d ? 1 << 4 : 0); CSHA256 hasher; hasher.Write(blob, 64); hasher.FinalizeNoPadding(res.begin()); return res; }
int main(int argc, char** argv) { if (argc != 4) { printf("USAGE: %s BITCOIND_ADDRESS BITCOIND_PORT LOCAL_ADDRESS\n", argv[0]); return -1; } #ifdef WIN32 WSADATA wsaData; if (WSAStartup(MAKEWORD(2,2), &wsaData)) return -1; #endif struct sockaddr_in6 addr; if (!lookup_address(argv[1], &addr)) { printf("Failed to lookup hostname\n"); return -1; } std::string host(gethostname(&addr)); P2PClient* inbound; P2PClient outbound(argv[1], std::stoul(argv[2]), [&](std::vector<unsigned char>& bytes, struct timeval) { struct timeval tv; gettimeofday(&tv, NULL); inbound->receive_block(bytes); std::vector<unsigned char> fullhash(32); CSHA256 hash; // Probably not BE-safe hash.Write(&bytes[sizeof(struct bitcoin_msg_header)], 80).Finalize(&fullhash[0]); hash.Reset().Write(&fullhash[0], fullhash.size()).Finalize(&fullhash[0]); for (unsigned int i = 0; i < fullhash.size(); i++) printf("%02x", fullhash[fullhash.size() - i - 1]); printf(" recv'd %s %lu\n", argv[1], uint64_t(tv.tv_sec)*1000 + uint64_t(tv.tv_usec)/1000); }, [&](std::shared_ptr<std::vector<unsigned char> >& bytes) { inbound->receive_transaction(bytes); }); inbound = new P2PClient(argv[3], 8334, [&](std::vector<unsigned char>& bytes, struct timeval) { outbound.receive_block(bytes); }, [&](std::shared_ptr<std::vector<unsigned char> >& bytes) { }); while (true) { sleep(1000); } }
void net_process() { recv_tx_cache.clear(); send_tx_cache.clear(); while (true) { relay_msg_header header; if (read_all(sock, (char*)&header, 4*3) != 4*3) return disconnect("failed to read message header"); if (header.magic != RELAY_MAGIC_BYTES) return disconnect("invalid magic bytes"); uint32_t message_size = ntohl(header.length); if (message_size > 1000000) return disconnect("got message too large"); if (header.type == VERSION_TYPE) { char data[message_size]; if (read_all(sock, data, message_size) < (int64_t)(message_size)) return disconnect("failed to read version message"); if (strncmp(VERSION_STRING, data, std::min(sizeof(VERSION_STRING), size_t(message_size)))) { relay_msg_header version_header = { RELAY_MAGIC_BYTES, MAX_VERSION_TYPE, htonl(strlen(VERSION_STRING)) }; if (send_all(sock, (char*)&version_header, sizeof(version_header)) != sizeof(version_header)) return disconnect("failed to write max version header"); if (send_all(sock, VERSION_STRING, strlen(VERSION_STRING)) != strlen(VERSION_STRING)) return disconnect("failed to write max version string"); return disconnect("unknown version string"); } relay_msg_header version_header = { RELAY_MAGIC_BYTES, VERSION_TYPE, htonl(strlen(VERSION_STRING)) }; if (send_all(sock, (char*)&version_header, sizeof(version_header)) != sizeof(version_header)) return disconnect("failed to write version header"); if (send_all(sock, VERSION_STRING, strlen(VERSION_STRING)) != strlen(VERSION_STRING)) return disconnect("failed to write version string"); connected = 2; send_mutex.unlock(); printf("%s Connected to relay node with protocol version %s\n", host.c_str(), VERSION_STRING); } else if (connected != 2) { return disconnect("got non-version before version"); } else if (header.type == MAX_VERSION_TYPE) { char data[message_size]; if (read_all(sock, data, message_size) < (int64_t)(message_size)) return disconnect("failed to read max_version string"); if (strncmp(VERSION_STRING, data, std::min(sizeof(VERSION_STRING), size_t(message_size)))) printf("%s peer sent us a MAX_VERSION message\n", host.c_str()); else return disconnect("got MAX_VERSION of same version as us"); } else if (header.type == BLOCK_TYPE) { struct timeval start, finish_read, finish_send; gettimeofday(&start, NULL); auto res = decompressRelayBlock(sock, message_size); if (std::get<2>(res)) return disconnect(std::get<2>(res)); gettimeofday(&finish_read, NULL); std::vector<unsigned char> fullhash(32); CSHA256 hash; // Probably not BE-safe hash.Write(&(*std::get<1>(res))[sizeof(struct bitcoin_msg_header)], 80).Finalize(&fullhash[0]); hash.Reset().Write(&fullhash[0], fullhash.size()).Finalize(&fullhash[0]); blocksAlreadySeen.insert(fullhash); bool relayed = provide_block(this, std::get<1>(res)); gettimeofday(&finish_send, NULL); if (relayed) { for (unsigned int i = 0; i < fullhash.size(); i++) printf("%02x", fullhash[fullhash.size() - i - 1]); printf(" BLOCK %lu %s UNTRUSTED_RELAY %u / %u TIMES: %ld %ld\n", uint64_t(finish_send.tv_sec)*1000 + uint64_t(finish_send.tv_usec)/1000, host.c_str(), (unsigned)std::get<0>(res), (unsigned)std::get<1>(res)->size(), int64_t(finish_read.tv_sec - start.tv_sec)*1000 + (int64_t(finish_read.tv_usec) - start.tv_usec)/1000, int64_t(finish_send.tv_sec - finish_read.tv_sec)*1000 + (int64_t(finish_send.tv_usec) - finish_read.tv_usec)/1000); } } else if (header.type == END_BLOCK_TYPE) { } else if (header.type == TRANSACTION_TYPE) { if (message_size > MAX_RELAY_TRANSACTION_BYTES && (recv_tx_cache.flagCount() >= MAX_EXTRA_OVERSIZE_TRANSACTIONS || message_size > MAX_RELAY_OVERSIZE_TRANSACTION_BYTES)) return disconnect("got freely relayed transaction too large"); auto tx = std::make_shared<std::vector<unsigned char> > (message_size); if (read_all(sock, (char*)&(*tx)[0], message_size) < (int64_t)(message_size)) return disconnect("failed to read loose transaction data"); recv_tx_cache.add(tx, message_size > MAX_RELAY_TRANSACTION_BYTES); provide_transaction(this, tx); } else return disconnect("got unknown message type"); } }
void sha256 (uint8_t* dest, const uint8_t* src, size_t n) { CSHA256 hash; hash.Write(src, n); hash.Finalize(dest); }
void net_process() { while (true) { struct bitcoin_msg_header header; if (read_all(sock, (char*)&header, sizeof(header)) != sizeof(header)) return disconnect("failed to read message header"); if (header.magic != BITCOIN_MAGIC) return disconnect("invalid magic bytes"); struct timeval start_read; gettimeofday(&start_read, NULL); header.length = le32toh(header.length); if (header.length > 5000000) return disconnect("got message too large"); auto msg = std::make_shared<std::vector<unsigned char> > (sizeof(struct bitcoin_msg_header) + uint32_t(header.length)); if (read_all(sock, (char*)&(*msg)[sizeof(struct bitcoin_msg_header)], header.length) != int(header.length)) return disconnect("failed to read message"); unsigned char fullhash[32]; CSHA256 hash; hash.Write(&(*msg)[sizeof(struct bitcoin_msg_header)], header.length).Finalize(fullhash); hash.Reset().Write(fullhash, sizeof(fullhash)).Finalize(fullhash); if (memcmp((char*)fullhash, header.checksum, sizeof(header.checksum))) return disconnect("got invalid message checksum"); if (!strncmp(header.command, "version", strlen("version"))) { if (connected != 0) return disconnect("got invalid version"); connected = 1; if (header.length < sizeof(struct bitcoin_version_start)) return disconnect("got short version"); struct bitcoin_version_start *their_version = (struct bitcoin_version_start*) &(*msg)[sizeof(struct bitcoin_msg_header)]; printf("%s Protocol version %u\n", host.c_str(), le32toh(their_version->protocol_version)); struct bitcoin_version_with_header version_msg; version_msg.version.start.timestamp = htole64(time(0)); memcpy(((char*)&version_msg.version.end.user_agent) + 27, location, 7); static_assert(BITCOIN_UA_LENGTH == 27 + 7 + 2 /* 27 + 7 + '/' + '\0' */, "BITCOIN_UA changed in header but file not updated"); prepare_message("version", (unsigned char*)&version_msg, sizeof(struct bitcoin_version)); if (send_all(sock, (char*)&version_msg, sizeof(struct bitcoin_version_with_header)) != sizeof(struct bitcoin_version_with_header)) return disconnect("failed to send version message"); struct bitcoin_msg_header verack_header; prepare_message("verack", (unsigned char*)&verack_header, 0); if (send_all(sock, (char*)&verack_header, sizeof(struct bitcoin_msg_header)) != sizeof(struct bitcoin_msg_header)) return disconnect("failed to send verack"); continue; } else if (!strncmp(header.command, "verack", strlen("verack"))) { if (connected != 1) return disconnect("got invalid verack"); connected = 2; send_mutex.unlock(); continue; } if (connected != 2) return disconnect("got non-version, non-verack before version+verack"); if (!strncmp(header.command, "ping", strlen("ping"))) { memcpy(&header.command, "pong", sizeof("pong")); memcpy(&(*msg)[0], &header, sizeof(struct bitcoin_msg_header)); std::lock_guard<std::mutex> lock(send_mutex); if (send_all(sock, (char*)&(*msg)[0], sizeof(struct bitcoin_msg_header) + header.length) != int64_t(sizeof(struct bitcoin_msg_header) + header.length)) return disconnect("failed to send pong"); continue; } else if (!strncmp(header.command, "inv", strlen("inv"))) { std::lock_guard<std::mutex> lock(send_mutex); try { std::set<std::vector<unsigned char> > setRequestBlocks; std::set<std::vector<unsigned char> > setRequestTxn; std::vector<unsigned char>::const_iterator it = msg->begin(); it += sizeof(struct bitcoin_msg_header); uint64_t count = read_varint(it, msg->end()); if (count > 50000) return disconnect("inv count > MAX_INV_SZ"); uint32_t MSG_TX = htole32(1); uint32_t MSG_BLOCK = htole32(2); for (uint64_t i = 0; i < count; i++) { move_forward(it, 4 + 32, msg->end()); std::vector<unsigned char> hash(it-32, it); const uint32_t type = (*(it-(1+32)) << 24) | (*(it-(2+32)) << 16) | (*(it-(3+32)) << 8) | *(it-(4+32)); if (type == MSG_TX) { if (!txnAlreadySeen.insert(hash).second) continue; setRequestTxn.insert(hash); } else if (type == MSG_BLOCK) { if (!blocksAlreadySeen.insert(hash).second) continue; setRequestBlocks.insert(hash); } else return disconnect("unknown inv type"); } if (setRequestBlocks.size()) { std::vector<unsigned char> getdataMsg; std::vector<unsigned char> invCount = varint(setRequestBlocks.size()); getdataMsg.reserve(sizeof(struct bitcoin_msg_header) + invCount.size() + setRequestBlocks.size()*36); getdataMsg.insert(getdataMsg.end(), sizeof(struct bitcoin_msg_header), 0); getdataMsg.insert(getdataMsg.end(), invCount.begin(), invCount.end()); for (auto& hash : setRequestBlocks) { getdataMsg.insert(getdataMsg.end(), (unsigned char*)&MSG_BLOCK, ((unsigned char*)&MSG_BLOCK) + 4); getdataMsg.insert(getdataMsg.end(), hash.begin(), hash.end()); } prepare_message("getdata", (unsigned char*)&getdataMsg[0], invCount.size() + setRequestBlocks.size()*36); if (send_all(sock, (char*)&getdataMsg[0], sizeof(struct bitcoin_msg_header) + invCount.size() + setRequestBlocks.size()*36) != int(sizeof(struct bitcoin_msg_header) + invCount.size() + setRequestBlocks.size()*36)) return disconnect("error sending getdata"); for (auto& hash : setRequestBlocks) { struct timeval tv; gettimeofday(&tv, NULL); for (unsigned int i = 0; i < hash.size(); i++) printf("%02x", hash[hash.size() - i - 1]); printf(" requested from %s at %lu\n", host.c_str(), uint64_t(tv.tv_sec) * 1000 + uint64_t(tv.tv_usec) / 1000); } } if (setRequestTxn.size()) { std::vector<unsigned char> getdataMsg; std::vector<unsigned char> invCount = varint(setRequestTxn.size()); getdataMsg.reserve(sizeof(struct bitcoin_msg_header) + invCount.size() + setRequestTxn.size()*36); getdataMsg.insert(getdataMsg.end(), sizeof(struct bitcoin_msg_header), 0); getdataMsg.insert(getdataMsg.end(), invCount.begin(), invCount.end()); for (const std::vector<unsigned char>& hash : setRequestTxn) { getdataMsg.insert(getdataMsg.end(), (unsigned char*)&MSG_TX, ((unsigned char*)&MSG_TX) + 4); getdataMsg.insert(getdataMsg.end(), hash.begin(), hash.end()); } prepare_message("getdata", (unsigned char*)&getdataMsg[0], invCount.size() + setRequestTxn.size()*36); if (send_all(sock, (char*)&getdataMsg[0], sizeof(struct bitcoin_msg_header) + invCount.size() + setRequestTxn.size()*36) != int(sizeof(struct bitcoin_msg_header) + invCount.size() + setRequestTxn.size()*36)) return disconnect("error sending getdata"); } } catch (read_exception) { return disconnect("failed to process inv"); } continue; } memcpy(&(*msg)[0], &header, sizeof(struct bitcoin_msg_header)); if (!strncmp(header.command, "block", strlen("block"))) { provide_block(this, msg, start_read); } else if (!strncmp(header.command, "tx", strlen("tx"))) { provide_transaction(this, msg); } } }