Exemplo n.º 1
0
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);
}
Exemplo n.º 2
0
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); }
}
Exemplo n.º 3
0
	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");
		}
	}
Exemplo n.º 4
0
	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);
			}
		}
	}