Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
[package]
name = "prototype"
version = "0.1.0"
edition = "2021"
authors = ["Lola Rigaut-Luczak <me@laflemme.lol>"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
hex = "0.4.3"
bitcoin-network = { git = "https://github.com/cyber-coop/bitcoin-network", branch = "main" }
magic-bytes = "0.1.0"
postgres = { version = "0.19" }
log = "0.4"
env_logger = "0.11"
serde = { version = "1.0", features = ["derive"] }
toml = "0.8"
sha2 = "0.10.9"
varint = {package = "bitcoin-varint", version = "0.1.0"}
5 changes: 1 addition & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,5 @@ postgres:
build-docker:
docker build -t prototype .

run-docker:
docker run -it --rm -e NETWORK=dogecoin -e TESTNET=true --name prototype-dogecoin-testnet prototype

run:
RUST_LOG="prototype=trace" cargo r -- $(network)
RUST_LOG="prototype=info" cargo r -- $(network)
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@
- [x] Resolve the mistery of the missing txid `8f311e28fb852de8456b1a55e68cf8e9be501fbfd8e386cc9c551b41f3e0809b` (probably wrong hash...) ... hash registered `\x83c4938e238bef8bf2b64333abf106226c68abd36a80f5b916cf1b6d8fe8d5bc`
- [ ] Adding connection timeout
- [ ] Need nodes fallback (or we can use dns look up to find nodes)
- [ ] Send an email once a long sql script is done (see `mail`)
- [ ] ~~Send an email once a long sql script is done (see `mail`)~~
- [ ] Create table on database at start
Binary file added raw_50057.bin
Binary file not shown.
2 changes: 2 additions & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"
2 changes: 1 addition & 1 deletion src/database.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use bitcoin_network::block::Block;
use crate::p2p::block::Block;
use postgres::Client;
use std::io::prelude::*;
use std::time::Instant;
Expand Down
13 changes: 8 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
use bitcoin_network::{block::Block, get_blocks::GetBlocks, get_data::GetData, message::Message};
use std::env;
use std::sync::mpsc::sync_channel;
use std::thread;
use std::time::Instant;
#![feature(cursor_split)]

pub mod configs;
pub mod database;
pub mod duplicate;
pub mod networks;
pub mod p2p;
pub mod peer;
pub mod utils;

use p2p::{block::Block, get_blocks::GetBlocks, get_data::GetData, message::Message};
use std::env;
use std::sync::mpsc::sync_channel;
use std::thread;
use std::time::Instant;

use crate::database::save_blocks;
use crate::peer::Peer;

Expand Down
38 changes: 38 additions & 0 deletions src/p2p/address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use super::error::DeserializeError;
use std::io::{Cursor, Read};

#[derive(Debug, Clone, PartialEq)]
pub struct Address {
pub services: u64,
pub ip: [u8; 16],
pub port: u16,
}

impl Address {
pub fn serialize(&self) -> Vec<u8> {
let mut result: Vec<u8> = vec![];
result.extend_from_slice(&self.services.to_le_bytes());
result.extend_from_slice(&self.ip);
result.extend_from_slice(&self.port.to_le_bytes());

result
}

pub fn deserialize(bytes: &[u8]) -> Result<Address, DeserializeError> {
let mut cur = Cursor::new(bytes);

let mut buf = [0u8; 8];
cur.read_exact(&mut buf)?;
let services = u64::from_le_bytes(buf);

let mut buf = [0u8; 16];
cur.read_exact(&mut buf)?;
let ip = buf;

let mut buf = [0u8; 2];
cur.read_exact(&mut buf)?;
let port = u16::from_le_bytes(buf);

Ok(Self { services, ip, port })
}
}
208 changes: 208 additions & 0 deletions src/p2p/block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
use super::error::DeserializeError;
use super::tx::{Tx, TxIn, TxOut};
use super::utils;
use std::io::{Cursor, Read};
use varint::VarInt;

const BLOCK_VERSION_AUXPOW_BIT: u32 = 0x100;

#[derive(Debug, Clone, PartialEq)]
pub struct Block {
pub version: u32,
// auxpow header (to be compatible with Namecoin and Dogecoin)
pub auxpow_header: Option<AuxPoWHeader>,
pub previous_hash: [u8; 32],
pub merkle_root: [u8; 32],
pub timestamp: u32,
pub bits: u32,
pub nonce: u32,
pub transactions: Vec<Tx>,
}

impl Block {
pub fn hash(&self) -> [u8; 32] {
let block_header = &self.serialize_header();
utils::double_hash(block_header)
}

pub fn serialize_header(&self) -> Vec<u8> {
let mut result: Vec<u8> = vec![];
result.extend(self.version.to_le_bytes());
result.extend(self.previous_hash);
result.extend(self.merkle_root);
result.extend(self.timestamp.to_le_bytes());
result.extend(self.bits.to_le_bytes());
result.extend(self.nonce.to_le_bytes());
result
}

pub fn serialize(&self) -> Vec<u8> {
let mut result: Vec<u8> = vec![];
result.extend(self.version.to_le_bytes());
result.extend(self.previous_hash);
result.extend(self.merkle_root);
result.extend(self.timestamp.to_le_bytes());
result.extend(self.bits.to_le_bytes());
result.extend(self.nonce.to_le_bytes());
//TODO: serialize transactions

/*self.transactions
.iter()
.for_each(|t| {
result.extend(t.serialize());
});*/

result
}

pub fn deserialize(bytes: &[u8], auxpow_activated: bool) -> Result<Block, DeserializeError> {
let mut cur = Cursor::new(bytes);

// Block headers
let mut buf = [0u8; 4];
cur.read_exact(&mut buf)?;
let version = u32::from_le_bytes(buf);

let mut buf = [0u8; 32];
cur.read_exact(&mut buf)?;
let previous_hash = buf;

let mut buf = [0u8; 32];
cur.read_exact(&mut buf)?;
let merkle_root = buf;

let mut buf = [0u8; 4];
cur.read_exact(&mut buf)?;
let timestamp = u32::from_le_bytes(buf);

let mut buf = [0u8; 4];
cur.read_exact(&mut buf)?;
let bits = u32::from_le_bytes(buf);

let mut buf = [0u8; 4];
cur.read_exact(&mut buf)?;
let nonce = u32::from_le_bytes(buf);

if auxpow_activated && (version & BLOCK_VERSION_AUXPOW_BIT) != 0 {
let (aux_power, size) = match AuxPoWHeader::deserialize_with_size(cur.split().1) {
Ok((aux_power, size)) => (aux_power, size),
Err(error) => {
return Err(error);
}
};
cur.set_position(cur.position() + size);
}

let count = VarInt::decode(cur.split().1)?;
let varint_size = VarInt::get_size(count)?;
cur.set_position(cur.position() + varint_size as u64);

let mut transactions: Vec<Tx> = vec![];
for _ in 0..count {
let (tx, tx_size) = Tx::deserialize_with_size(cur.split().1)?;
cur.set_position(cur.position() + tx_size);

transactions.push(tx);
}

Ok(Self {
version,
auxpow_header: None,
previous_hash,
merkle_root,
timestamp,
bits,
nonce,
transactions,
})
}
}

#[derive(Debug, Clone, PartialEq)]
pub struct AuxPoWHeader {
pub version: u32,
}

impl AuxPoWHeader {
pub fn deserialize_with_size(bytes: &[u8]) -> Result<(Self, u64), DeserializeError> {
let mut cur = Cursor::new(bytes);

let mut buf = [0u8; 4];
cur.read_exact(&mut buf)?;
let version = u32::from_le_bytes(buf);

let count = VarInt::decode(cur.split().1)?;
let varint_size = VarInt::get_size(count)? as u64;
cur.set_position(cur.position() + varint_size);

let mut tx_ins: Vec<TxIn> = vec![];
for _ in 0..count {
let (tx_in, size) = TxIn::deserialize_with_size(cur.split().1)?;
cur.set_position(cur.position() + size);
}

let count = VarInt::decode(cur.split().1)?;
let varint_size = VarInt::get_size(count)? as u64;
cur.set_position(cur.position() + varint_size);

let mut tx_outs: Vec<TxOut> = vec![];
for _ in 0..count {
let (tx_out, size) = TxOut::deserialize_with_size(cur.split().1)?;
cur.set_position(cur.position() + size);
}

let mut buf = [0u8; 4];
cur.read_exact(&mut buf)?;
let lock_time = u32::from_le_bytes(buf);

let mut buf = [0u8; 32];
cur.read_exact(&mut buf)?;
let parent_hash = buf;

let count = VarInt::decode(cur.split().1)?;
let varint_size = VarInt::get_size(count)? as u64;
cur.set_position(cur.position() + varint_size);

for _ in 0..count {
let mut buf = [0u8; 32];
cur.read_exact(&mut buf)?;
let merkle_hash = buf;
}

let mut buf = [0u8; 4];
cur.read_exact(&mut buf)?;
let bitmask = u32::from_le_bytes(buf);

let count = VarInt::decode(cur.split().1)?;
let varint_size = VarInt::get_size(count)? as u64;
cur.set_position(cur.position() + varint_size);

for _ in 0..count {
let mut buf = [0u8; 32];
cur.read_exact(&mut buf)?;
let merkle_hash = buf;
}

let mut buf = [0u8; 4];
cur.read_exact(&mut buf)?;
let bitmask = u32::from_le_bytes(buf);

let mut buf = [0u8; 80];
cur.read_exact(&mut buf)?;

Ok((Self { version }, cur.position()))
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::fs;

#[test]
fn test_block_deserialize() {
let f = fs::read("./raw_50057.bin").unwrap();

let block = Block::deserialize(&f, false).expect("should deserialize raw block");
}
}
25 changes: 25 additions & 0 deletions src/p2p/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use std::error::Error;
use std::fmt::Display;

#[derive(Debug)]
pub struct DeserializeError(pub String);

impl Display for DeserializeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}

impl Error for DeserializeError {}

impl From<std::io::Error> for DeserializeError {
fn from(_e: std::io::Error) -> Self {
DeserializeError("Failed to read varint".to_owned())
}
}

impl From<std::string::FromUtf8Error> for DeserializeError {
fn from(_e: std::string::FromUtf8Error) -> Self {
DeserializeError("Failed to convert from utf8".to_owned())
}
}
Loading