From 883527ec6fc3dbb7c56b88cd8ff93a1f1a1ec8bd Mon Sep 17 00:00:00 2001 From: rllola Date: Tue, 16 Jul 2024 11:17:02 +0200 Subject: [PATCH 1/2] refactoring --- .github/workflows/audit.yml | 17 +++++ .github/workflows/docker.yml | 26 ++++++++ .github/workflows/test.yml | 25 ++++++++ .gitignore | 3 +- Cargo.toml | 1 + Dockerfile | 5 +- TODO.md | 2 +- config.example.toml | 10 +++ contrib/docker-compose.yml | 116 +++++++++++++++++++++++++++++++++++ docker-compose.yml | 98 ----------------------------- src/database.rs | 49 ++++++++++++--- src/main.rs | 44 +++++-------- src/networks.rs | 72 +++------------------- src/peer.rs | 13 ++-- src/utils.rs | 6 +- 15 files changed, 273 insertions(+), 214 deletions(-) create mode 100644 .github/workflows/audit.yml create mode 100644 .github/workflows/docker.yml create mode 100644 .github/workflows/test.yml create mode 100644 config.example.toml create mode 100644 contrib/docker-compose.yml delete mode 100644 docker-compose.yml diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 0000000..376918a --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,17 @@ +name: Security audit + +on: + schedule: + - cron: '0 0 * * 0' + push: + paths: + - 'Cargo.toml' + - 'Cargo.lock' + +jobs: + security_audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Scan for vulnerabilities + run: cargo audit diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..af49cc5 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,26 @@ +name: Publish Docker image + +# on: +# release: +# types: [published] +on: + push: + tags: + - '*' + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Log in to Docker Hub + run: docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and push Docker image + run: docker build . --file Dockerfile --tag rllola/prototype:${{ github.ref_name }} + + - name: Push Docker image + run: docker push rllola/eth-prototype:${{ github.ref_name }} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..7b2228e --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,25 @@ +name: Run tests + +on: + pull_request: + branches: [ main ] + +jobs: + fmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + - run: rustup update nightly && rustup default nightly + - name: Enforce formatting + run: cargo fmt --check + + # test: + # name: Tests + # runs-on: ubuntu-latest + # steps: + # - name: Checkout Repo + # uses: actions/checkout@v3 + # - name: Run tests + # run: cargo test --verbose \ No newline at end of file diff --git a/.gitignore b/.gitignore index 87d8a27..3fd2b63 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ archive/ data/ script/ privatekeys.txt -Makefile.prod \ No newline at end of file +Makefile.prod +docker-compose.prod.yml diff --git a/Cargo.toml b/Cargo.toml index 2ea0c35..2a71581 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "prototype" version = "0.1.0" edition = "2021" +authors = ["Lola Rigaut-Luczak "] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/Dockerfile b/Dockerfile index e4e4d1b..9ebb965 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,8 +16,7 @@ COPY --from=builder /usr/local/cargo/bin/prototype /usr/local/bin/prototype RUN apt-get update && rm -rf /var/lib/apt/lists/* # default env -ENV NETWORK "dogecoin" -ENV TESTNET "true" +ENV NETWORK "dogecoin_testnet" ENV RUST_LOG "prototype=info" -CMD prototype ${NETWORK} ${TESTNET} \ No newline at end of file +CMD prototype ${NETWORK} \ No newline at end of file diff --git a/TODO.md b/TODO.md index 75c083e..f8ce25e 100644 --- a/TODO.md +++ b/TODO.md @@ -4,7 +4,7 @@ - [x] Create a thread that would queue data to save in the database - [x] ~~Prometheus + grafana~~ influxdb data collector and syncing monitoring - [x] Dockerfile -- [ ] Remove warnings +- [x] Remove warnings - [ ] Improve development process - [ ] Use `init.sh` to create tables - [ ] Improve sql query (in NOTES.md) to avoid the encoding call diff --git a/config.example.toml b/config.example.toml new file mode 100644 index 0000000..dffc80a --- /dev/null +++ b/config.example.toml @@ -0,0 +1,10 @@ +[database] +host = "localhost" +user = "postgres" +password = "wow" +dbname = "blockchains" + +# Mainnet +[peer] +ip = "127.0.0.1" +port = 8333 diff --git a/contrib/docker-compose.yml b/contrib/docker-compose.yml new file mode 100644 index 0000000..11d7eb2 --- /dev/null +++ b/contrib/docker-compose.yml @@ -0,0 +1,116 @@ +version: "3.9" + +services: + # Bitcoin Mainnet + indexer_bitcoin_mainnet: + build: ../. + depends_on: + - postgres + environment: + NETWORK: "bitcoin_mainnet" + RUST_LOG: "prototype=info" + volumes: + - ../config.bitcoin_mainnet.toml:/config.toml + depends_on: + postgres: + condition: service_healthy + + # Bitcoin testnet + indexer_bitcoin_testnet: + build: ../. + depends_on: + - postgres + environment: + NETWORK: "bitcoin_testnet" + RUST_LOG: "prototype=info" + volumes: + - ../config.bitcoin_testnet.toml:/config.toml + depends_on: + postgres: + condition: service_healthy + + # Dogecoin Mainnet + indexer_dogecoin_mainnet: + build: ../. + depends_on: + - postgres + environment: + NETWORK: "dogecoin_mainnet" + RUST_LOG: "prototype=info" + volumes: + - ../config.dogecoin_mainnet.toml:/config.toml + depends_on: + postgres: + condition: service_healthy + + # Dogecoin testnet + indexer_dogecoin_testnet: + build: ../. + depends_on: + - postgres + environment: + NETWORK: "dogecoin_testnet" + RUST_LOG: "prototype=info" + volumes: + - ../config.dogecoin_testnet.toml:/config.toml + depends_on: + postgres: + condition: service_healthy + + # Litecoin Mainnet + indexer_litecoin_mainnet: + build: ../. + depends_on: + - postgres + environment: + NETWORK: "litecoin_mainnet" + RUST_LOG: "prototype=info" + volumes: + - ../config.litecoin_mainnet.toml:/config.toml + depends_on: + postgres: + condition: service_healthy + + # Litecoin Testnet + indexer_litecoin_testnet: + build: ../. + depends_on: + - postgres + environment: + NETWORK: "litecoin_testnet" + RUST_LOG: "prototype=info" + volumes: + - ../config.litecoin_testnet.toml:/config.toml + depends_on: + postgres: + condition: service_healthy + + # Namecoin Mainnet + indexer_namecoin_mainnet: + build: ../. + depends_on: + - postgres + environment: + NETWORK: "namecoin_mainnet" + RUST_LOG: "prototype=info" + volumes: + - ../config.namecoin_mainnet.toml:/config.toml + depends_on: + postgres: + condition: service_healthy + + postgres: + image: "postgres:latest" + container_name: "postgres" + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: wow + POSTGRES_DB: blockchains + # ¡IMPORTANT! If you don't want to lose your data you should uncomment this + volumes: + - ./data/:/var/lib/postgresql/data/ + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index fd97124..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,98 +0,0 @@ - version: "3.9" - - services: - indexer_dogecoin_testnet: - build: ../. - depends_on: - - postgres - - influxdb - environment: - NETWORK: "dogecoin" - TESTNET: "true" - RUST_BACKTRACE: "full" - RUST_LOG: "prototype=trace" - networks: - - prototypes - - indexer_dogecoin_mainnet: - build: ../. - depends_on: - - postgres - - influxdb - environment: - NETWORK: "dogecoin" - TESTNET: "false" - RUST_BACKTRACE: "full" - RUST_LOG: "prototype=trace" - networks: - - prototypes - - indexer_namecoin_mainnet: - build: ../. - depends_on: - - postgres - - influxdb - environment: - NETWORK: "namecoin" - TESTNET: "false" - RUST_BACKTRACE: "full" - RUST_LOG: "prototype=trace" - networks: - - prototypes - - indexer_litecoin_testnet: - build: ../. - depends_on: - - postgres - - influxdb - environment: - NETWORK: "litecoin" - TESTNET: "true" - RUST_BACKTRACE: "full" - RUST_LOG: "prototype=trace" - networks: - - prototypes - - indexer_litecoin_mainnet: - build: ../. - depends_on: - - postgres - - influxdb - environment: - NETWORK: "litecoin" - TESTNET: "false" - RUST_BACKTRACE: "full" - RUST_LOG: "prototype=trace" - networks: - - prototypes - - indexer_bitcoin_testnet: - build: ../. - depends_on: - - postgres - - influxdb - environment: - NETWORK: "bitcoin" - TESTNET: "true" - RUST_BACKTRACE: "full" - RUST_LOG: "prototype=trace" - networks: - - prototypes - - indexer_bitcoin_mainnet: - build: ../. - depends_on: - - postgres - - influxdb - environment: - NETWORK: "bitcoin" - TESTNET: "false" - RUST_BACKTRACE: "full" - RUST_LOG: "prototype=trace" - networks: - - prototypes - -networks: - prototypes: - name: prototypes - external: true diff --git a/src/database.rs b/src/database.rs index 1ed5384..5868e7c 100644 --- a/src/database.rs +++ b/src/database.rs @@ -4,6 +4,41 @@ use postgres::Client; use std::io::prelude::*; use std::time::Instant; +pub fn create_tables(schema_name: &String, postgres_client: &mut Client) { + let query = format!(" + CREATE SCHEMA IF NOT EXISTS {schema_name}; + CREATE TABLE IF NOT EXISTS {schema_name}.blocks ( + height INTEGER NOT NULL, + hash BYTEA NOT NULL, + prevhash BYTEA NOT NULL, + merkleroot BYTEA NOT NULL + ); + CREATE TABLE IF NOT EXISTS {schema_name}.transactions ( + version INTEGER NOT NULL, + txid BYTEA, + block BYTEA NOT NULL, + locktime BIGINT NOT NULL + ); + CREATE TABLE IF NOT EXISTS {schema_name}.txins ( + txid BYTEA NOT NULL, + index INTEGER NOT NULL, + outputhash BYTEA NOT NULL, + outputindex INTEGER NOT NULL, + sigscript BYTEA, + sequence INTEGER NOT NULL + ); + CREATE TABLE IF NOT EXISTS {schema_name}.txouts ( + txid BYTEA NOT NULL, + index INTEGER NOT NULL, + value BIGINT, + pkscript BYTEA + ); + "); + + postgres_client.batch_execute(&query).unwrap(); +} + + pub fn save_blocks( blocks: Vec, schema_name: &String, @@ -96,7 +131,8 @@ pub fn save_blocks( }); trace!("Finished formating blocks and transactions message"); - let mut block_writer = postgres_client + let mut transaction = postgres_client.transaction().unwrap(); + let mut block_writer = transaction .copy_in(format!("COPY {}.blocks FROM stdin (DELIMITER ',')", schema_name).as_str()) .unwrap(); block_writer @@ -104,7 +140,7 @@ pub fn save_blocks( .unwrap(); block_writer.finish().unwrap(); - let mut transaction_writer = postgres_client + let mut transaction_writer = transaction .copy_in( format!( "COPY {}.transactions FROM stdin (DELIMITER ',')", @@ -118,7 +154,7 @@ pub fn save_blocks( .unwrap(); transaction_writer.finish().unwrap(); - let mut txins_writer = postgres_client + let mut txins_writer = transaction .copy_in(format!("COPY {}.txins FROM stdin (DELIMITER ',')", schema_name).as_str()) .unwrap(); txins_writer @@ -126,7 +162,7 @@ pub fn save_blocks( .unwrap(); txins_writer.finish().unwrap(); - let mut txouts_writer = postgres_client + let mut txouts_writer = transaction .copy_in(format!("COPY {}.txouts FROM stdin (DELIMITER ',')", schema_name).as_str()) .unwrap(); txouts_writer @@ -134,9 +170,8 @@ pub fn save_blocks( .unwrap(); txouts_writer.finish().unwrap(); - // let mut transaction_writer = client.copy_in(format!("COPY {}.txs FROM stdin (DELIMITER ',')", schema_name).as_str()).unwrap(); - // transaction_writer.write(transactions_string.as_bytes()).unwrap(); - // transaction_writer.finish().unwrap(); + // commit the transaction + transaction.commit().unwrap(); info!( "Blocks registered in database (current_height {}) {:.2?}", diff --git a/src/main.rs b/src/main.rs index 6960b84..58eec56 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,9 @@ use bitcoin_network::{ - block::Block, get_blocks::GetBlocks, get_data::GetData, message::Message, version::Version, + block::Block, get_blocks::GetBlocks, get_data::GetData, message::Message, }; use std::env; -use std::io::prelude::*; -use std::net::TcpStream; -use std::str::FromStr; use std::sync::mpsc::sync_channel; use std::thread; -use std::time::Duration; use std::time::Instant; pub mod database; @@ -32,24 +28,12 @@ fn main() { // Read cli args let network_arg: String = env::args() .nth(1) - .expect("expecting a network (Bitcoin, Namecoin, Litecoin or Dogecoin)."); - let testnet_arg: String = env::args() - .nth(2) - .expect("expecting 'true' or 'false' to indicate if you want to connect to testnet."); - let network = networks::Networks::from_str(network_arg.as_str()).unwrap(); - let testnet: bool = FromStr::from_str(testnet_arg.as_str()).unwrap(); - - // Lazy hack to wait for postgres to be up and running - info!("wait 60 secs"); - thread::sleep(Duration::from_secs(60)); + .expect("expecting a network (bitcoin_mainnet, bitcoin_testnet, dogecoin_mainnet, dogecoin_testnet, litecoin_mainnet, litecoin_tesnet or namecoin_mainnet)."); + let network = networks::Network::find(network_arg.as_str()).unwrap(); // Load config values from the config file let config = configs::read_config(); - // Find network based on args - let network_info = networks::Network::find(network, testnet).unwrap(); - let schema_name = network.get_schema_name(testnet); - /******************** * * CONNECT @@ -57,10 +41,10 @@ fn main() { ********************/ let mut message_rcv: Message; - let mut peer = Peer::new(format!("{},{}", config.peer.ip, config.peer.port), network_info.magic_bytes); + let mut peer = Peer::new(format!("{},{}", config.peer.ip, config.peer.port), network.magic_bytes); let mut current_height: u32 = 0; - let mut hash = network_info.genesis_hash.to_vec(); + let mut hash = network.genesis_hash.to_vec(); hash.reverse(); let height = peer.connect(); @@ -84,8 +68,10 @@ fn main() { ) .unwrap(); - let result = postgres_client.query(format!("SELECT * FROM {}.blocks a JOIN (SELECT MAX(height) as h FROM {}.blocks) b ON a.height = b.h;", schema_name, schema_name).as_str(), &[]).unwrap(); + // create the tables if they don't exist + database::create_tables(&network_arg, &mut postgres_client); + let result = postgres_client.query(format!("SELECT * FROM {0}.blocks a JOIN (SELECT MAX(height) as h FROM {0}.blocks) b ON a.height = b.h;", network_arg).as_str(), &[]).unwrap(); if result.len() > 0 { let row = &result[0]; current_height = row.get(0); @@ -105,7 +91,7 @@ fn main() { // Create a simple streaming channel (limited buffer of 4 batch of 500 blocks to avoid filling ram) let (tx, rx) = sync_channel(4); - let thread_handle = thread::spawn(move || { + let _thread_handle = thread::spawn(move || { info!("Starting database thread"); let mut process_current_height = current_height; @@ -118,11 +104,9 @@ fn main() { // while recv save blocks in database loop { - let mut blocks: Vec = vec![]; - - blocks = rx.recv().unwrap(); + let blocks: Vec = rx.recv().unwrap(); - save_blocks(blocks, &schema_name, &mut postgres_client, &mut process_current_height); + save_blocks(blocks, &network_arg, &mut postgres_client, &mut process_current_height); // We are synced if process_current_height > height { @@ -147,7 +131,7 @@ fn main() { let get_blocks = GetBlocks::new(70004, vec![hash.clone().try_into().unwrap()], None).serialize(); let message_get_blocks = - Message::new(network_info.magic_bytes, "getblocks".to_owned(), get_blocks); + Message::new(network.magic_bytes, "getblocks".to_owned(), get_blocks); peer.send(&message_get_blocks); @@ -172,7 +156,7 @@ fn main() { info!("Inv block count : {}", blocks_inv.count); // Inv message and get_data are the same message let get_data_message = Message::new( - network_info.magic_bytes, + network.magic_bytes, "getdata".to_owned(), message_rcv.payload, ); @@ -194,7 +178,7 @@ fn main() { count += 1; // NOTES: we won't receive the blocks in order. We would need to match with previous_hash to rebuild the chain. - let block = Block::deserialize(&message_rcv.payload, network_info.aux_pow).expect( + let block = Block::deserialize(&message_rcv.payload, network.aux_pow).expect( &format!("Fail to deserialize {}", hex::encode(&message_rcv.payload)), ); diff --git a/src/networks.rs b/src/networks.rs index 88e7ede..f4c1bd9 100644 --- a/src/networks.rs +++ b/src/networks.rs @@ -1,51 +1,5 @@ use magic_bytes::MagicBytes; -use std::str::FromStr; use std::error::Error; -use std::string::ToString; - -#[derive(Debug, Clone, Copy)] -pub enum Networks { - Bitcoin, - Namecoin, - Litecoin, - Dogecoin, -} - -impl Networks { - pub fn to_string(&self) -> String { - match self { - Networks::Bitcoin => "bitcoin".to_string(), - Networks::Namecoin => "namecoin".to_string(), - Networks::Litecoin => "litecoin".to_string(), - Networks::Dogecoin => "dogecoin".to_string(), - } - } - - pub fn get_schema_name(&self, testnet: bool) -> String { - let mut schema = self.to_string(); - if testnet { - schema = format!("{}_testnet", schema) - } else { - schema = format!("{}_mainnet", schema) - } - - schema - } -} - -impl FromStr for Networks { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - match s { - "bitcoin" => Ok(Networks::Bitcoin), - "namecoin" => Ok(Networks::Namecoin), - "litecoin" => Ok(Networks::Litecoin), - "dogecoin" => Ok(Networks::Dogecoin), - _ => Err("not matching 'bitcoin', 'namecoin', 'litecoin' or 'dogecoin' available networks."), - } - } -} pub struct Network { pub testnet: bool, @@ -105,24 +59,16 @@ impl Network { genesis_hash: [187, 10, 120, 38, 70, 55, 64, 107, 99, 96, 170, 217, 38, 40, 77, 84, 77, 112, 73, 244, 81, 137, 219, 86, 100, 243, 196, 208, 115, 80, 85, 158], }; - pub fn find(network: Networks, testnet: bool) -> Result> { + pub fn find(network: &str) -> Result> { match network { - Networks::Bitcoin => { - if testnet { Ok(Self::BITCOIN_TESTNET) } - else { Ok(Self::BITCOIN_MAINNET) } - }, - Networks::Namecoin => { - if testnet { Err("not supported".into()) } - else { Ok(Self::NAMECOIN_MAINNET) } - }, - Networks::Litecoin => { - if testnet { Ok(Self::LITECOIN_TESTNET) } - else { Ok(Self::LITECOIN_MAINNET) } - }, - Networks::Dogecoin => { - if testnet { Ok(Self::DOGECOIN_TESTNET) } - else { Ok(Self::DOGECOIN_MAINNET) } - } + "bitcoin_mainnet" => Ok(Self::BITCOIN_MAINNET), + "bitcoin_testnet" => Ok(Self::BITCOIN_TESTNET), + "dogecoin_mainnet" => Ok(Self::DOGECOIN_MAINNET), + "dogecoin_testnet" => Ok(Self::DOGECOIN_TESTNET), + "litecoin_mainnet" => Ok(Self::LITECOIN_MAINNET), + "litecoin_testnet" => Ok(Self::LITECOIN_TESTNET), + "namecoin_mainnet" => Ok(Self::NAMECOIN_MAINNET), + _ => Err("not matching available networks.".into()), } } } \ No newline at end of file diff --git a/src/peer.rs b/src/peer.rs index b50ca4f..4e8e239 100644 --- a/src/peer.rs +++ b/src/peer.rs @@ -1,13 +1,12 @@ use bitcoin_network::message::Message; -use bitcoin_network::get_blocks::GetBlocks; -use bitcoin_network::get_data::GetData; use bitcoin_network::version::Version; use std::net::TcpStream; use std::sync::mpsc::channel; use std::error::Error; use std::io::prelude::*; -use std::thread::{self, sleep_ms}; +use std::thread; use std::sync::mpsc::{Receiver, Sender}; +use std::time::Duration; use crate::utils; @@ -52,12 +51,12 @@ impl Peer { let mut buf = [0; 24]; // we have this loop to be sure we have received at least 24 bytes - while stream.peek(&mut buf).unwrap() != 24 { thread::sleep_ms(50); } + while stream.peek(&mut buf).unwrap() != 24 { thread::sleep(Duration::from_millis(50)); } stream.read_exact(&mut buf).unwrap(); data.extend(&buf); let payload_size = u32::from_le_bytes(buf[16..20].try_into().unwrap()) as usize; - let command = String::from_utf8(buf[4..16].to_vec()).unwrap(); + let _command = String::from_utf8(buf[4..16].to_vec()).unwrap(); // Message payload let mut payload : Vec = vec![]; @@ -68,7 +67,7 @@ impl Peer { let l = stream.read(&mut buf).unwrap(); payload.extend(&buf[0..l]); - thread::sleep_ms(500); + thread::sleep(Duration::from_millis(500)); } data.extend(payload); @@ -92,7 +91,7 @@ impl Peer { continue; } - tx.send(message); + let _ = tx.send(message); } }) } diff --git a/src/utils.rs b/src/utils.rs index 62462c5..b39b8a2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,6 +1,4 @@ -use std::net::TcpStream; -use std::io::prelude::*; -use bitcoin_network::{message::Message, block::Block, inventory::Inventory}; +use bitcoin_network::{block::Block, inventory::Inventory}; use std::error::Error; use bitcoin_network::{address::Address, version::Version}; use std::time::{SystemTime, UNIX_EPOCH}; @@ -32,7 +30,7 @@ pub fn check_chain(blocks: &mut Vec, hash: Vec) -> Result, pub fn create_version(host: &str) -> Version { let split: Vec<&str> = host.split(":").collect(); let ip_string: Vec<&str> = split[0].split(".").collect(); - let port: u16 = split[1].parse().unwrap(); + let _port: u16 = split[1].parse().unwrap(); // FIXME: Only support ipv4 let ip = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, ip_string[0].parse::().unwrap(), ip_string[1].parse::().unwrap(), ip_string[2].parse::().unwrap(), ip_string[3].parse::().unwrap()]; From b322068e731dec553cb17a6aa7ea4b99c8e03084 Mon Sep 17 00:00:00 2001 From: rllola Date: Wed, 17 Jul 2024 10:44:51 +0200 Subject: [PATCH 2/2] rustfmt --- .github/workflows/test.yml | 2 +- src/database.rs | 41 +- src/duplicate.rs | 791 +++++++++++++++++++++++++++++-------- src/main.rs | 56 +-- src/networks.rs | 37 +- src/peer.rs | 35 +- src/utils.rs | 45 ++- 7 files changed, 760 insertions(+), 247 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7b2228e..a396a12 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout Repo uses: actions/checkout@v4 - - run: rustup update nightly && rustup default nightly + - run: rustup update nightly && rustup default nightly && rustup component add rustfmt - name: Enforce formatting run: cargo fmt --check diff --git a/src/database.rs b/src/database.rs index 5868e7c..609323f 100644 --- a/src/database.rs +++ b/src/database.rs @@ -5,7 +5,8 @@ use std::io::prelude::*; use std::time::Instant; pub fn create_tables(schema_name: &String, postgres_client: &mut Client) { - let query = format!(" + let query = format!( + " CREATE SCHEMA IF NOT EXISTS {schema_name}; CREATE TABLE IF NOT EXISTS {schema_name}.blocks ( height INTEGER NOT NULL, @@ -33,16 +34,16 @@ pub fn create_tables(schema_name: &String, postgres_client: &mut Client) { value BIGINT, pkscript BYTEA ); - "); + " + ); postgres_client.batch_execute(&query).unwrap(); } - pub fn save_blocks( blocks: Vec, schema_name: &String, - postgres_client:&mut Client, + postgres_client: &mut Client, current_height: &mut u32, ) { // register in database @@ -133,41 +134,35 @@ pub fn save_blocks( let mut transaction = postgres_client.transaction().unwrap(); let mut block_writer = transaction - .copy_in(format!("COPY {}.blocks FROM stdin (DELIMITER ',')", schema_name).as_str()) - .unwrap(); - block_writer - .write_all(blocks_string.as_bytes()) + .copy_in(format!("COPY {}.blocks FROM stdin (DELIMITER ',')", schema_name).as_str()) .unwrap(); + block_writer.write_all(blocks_string.as_bytes()).unwrap(); block_writer.finish().unwrap(); let mut transaction_writer = transaction - .copy_in( - format!( - "COPY {}.transactions FROM stdin (DELIMITER ',')", - schema_name + .copy_in( + format!( + "COPY {}.transactions FROM stdin (DELIMITER ',')", + schema_name + ) + .as_str(), ) - .as_str(), - ) - .unwrap(); + .unwrap(); transaction_writer .write_all(transactions_string.as_bytes()) .unwrap(); transaction_writer.finish().unwrap(); let mut txins_writer = transaction - .copy_in(format!("COPY {}.txins FROM stdin (DELIMITER ',')", schema_name).as_str()) - .unwrap(); - txins_writer - .write_all(txins_string.as_bytes()) + .copy_in(format!("COPY {}.txins FROM stdin (DELIMITER ',')", schema_name).as_str()) .unwrap(); + txins_writer.write_all(txins_string.as_bytes()).unwrap(); txins_writer.finish().unwrap(); let mut txouts_writer = transaction - .copy_in(format!("COPY {}.txouts FROM stdin (DELIMITER ',')", schema_name).as_str()) - .unwrap(); - txouts_writer - .write_all(txouts_string.as_bytes()) + .copy_in(format!("COPY {}.txouts FROM stdin (DELIMITER ',')", schema_name).as_str()) .unwrap(); + txouts_writer.write_all(txouts_string.as_bytes()).unwrap(); txouts_writer.finish().unwrap(); // commit the transaction diff --git a/src/duplicate.rs b/src/duplicate.rs index 790fa73..7a1ecc8 100644 --- a/src/duplicate.rs +++ b/src/duplicate.rs @@ -1,4 +1,4 @@ -/* +/* Because of a bug when creating coinbase transaction we can find in block chains duplicate txid in different blocks. Those lists show duplicate in and their blockhash to avoid adding them in our database. */ @@ -9,167 +9,638 @@ pub const BITCOIN_TESTNET_DUPLICATE: [(&str, &str); 0] = []; // There is no duplicates for mainnet pub const DOGECOIN_MAINNET_DUPLICATE: [(&str, &str); 0] = []; pub const DOGECOIN_TESTNET_DUPLICATE: [(&str, &str); 17] = [ - ("8d281bd319b700820f1460dfefa9515b36809d680d6f2b34e1430bddd2250290", "9f4715099dd242c3b4e8d2264f4c57e2c96d63e45ba08703c2efd80751b3e254"), - ("9a686aafe142bf9402d401512c82ae5c014a3736c3c845180bf88f1f1f702ba5", "473ffa92e9539b81e36f133d3428d53646f0332f72e6002b3f54a2a1007d66c9"), - ("a7bfda8d75dd96da7dcedb3b46ad86f6f6eb79f1df9535545ce2057d33a4310d", "b261875f4e45b33be3ba4463c6289c9338ac8d6c0cdf6241e02efaf90063a134"), - ("b31f0bb0af33091d9e3ae6863afbd07f053f92b54911da4b0133ce693f1dd7c4", "b5efe56a9b6bb4c8c8104237dee294085870b07c4841ccbebdf8acaed6ca715d"), - ("2b40f71ee3ac903d109c8b7da077f6dff9e0afe9554c9ab92f4d4d73c2a91725", "bc8949acf1d08a92aae8dba73dabd6aed1dffd751fe2d8a5f287918b10f69403"), - ("d75fef40be8bd252851d3ea2c30b5c25530d63f815cc915a595500b969fa7a9c", "c385458050378228af225d8b9e85d280ee1c7cfc87ce913e567ac807d9afe5c3"), - ("56626810fd64940aab38221b9887cf9873ab98ad3b76be65d1a9dd7eb57a6b8f", "6b23ead109d6c195cf7de8541e256d9d731c766888e8b311aa6ce3f5a472d670"), - ("327769c8eb3cf4399573877b5f109f83eb84165d5835fa5ffa27d6ab5dfdb653", "4f655ea06fa37a1822109025bb03fb0d570d5bfd8e7318cc03fd6849f823859d"), - ("bdfa401c768dea8ce7c0d6b06220f18363f09428e3eefc7f75d448cc46811c16", "ed226852ae405b7ef2f8f51833f41edb1cc39301b87ef69713697693f9bb3362"), - ("7329977757575938ca8577539977df983c8e0f91ed43d5a352ac494dbc3fc678", "60bd0e14a72cfa62e9619d17c510839ab9f06b27bf147c514a982bf7dbbb54f2"), - ("950323d93ad0ca2037a2de03be0847fc30f3f0c972156bee9923d2b78496b09c", "9418de3a8e7b5d5c508ea1055ebf1fc2b5a8cce25033529c5506621583694376"), - ("273caeea6ebb49f31f75649b83981f8f8e360733b163219086ace6d1c3d01436", "4fa284e6f71a0350c5102ac0ea4e0a30e702ff68ca58d6a69868b4ad43f386b1"), - ("18b093907779a1d85261a65d182a38814708ca445647f8258b977b3cd7525a3b", "483ce53db22363a6ea3e52fd246b80f907cba3152ebc4c491b8f013292c9f546"), - ("1a3c04a55d7d20ba267a4169360871059652d2552e18a8224dee4c1a0f7fc7ac", "c6478a963abdf346315871c6cfba7d8be1ed18d0fea9169c3e597a0e02da4f97"), - ("fae3653cdb042f08fca6802798f25fbdc3f96ea9b6cfa7b06e03e393c16d13da", "66013ba954ed1d14c47d1d765ceb7af76c8248f0d7b87f99c937943446bd98da"), - ("10158e9ac8fede19af59cf59a7a138f3dbcf277703827946b6af2e4f6d95932c", "b55598f73a860744033656f4ed12d37b48ac70004f4fc00042feb6154f3e8f00"), - ("207235b4920d3dd4a80cc9e8c4dbacc8e7889de66802bdd0aaa99aa67a19660d", "3178a8d3ef80b39b008c1ba45462582d877daecf93d31fe1b5e619a74bbdc505"), - ]; + ( + "8d281bd319b700820f1460dfefa9515b36809d680d6f2b34e1430bddd2250290", + "9f4715099dd242c3b4e8d2264f4c57e2c96d63e45ba08703c2efd80751b3e254", + ), + ( + "9a686aafe142bf9402d401512c82ae5c014a3736c3c845180bf88f1f1f702ba5", + "473ffa92e9539b81e36f133d3428d53646f0332f72e6002b3f54a2a1007d66c9", + ), + ( + "a7bfda8d75dd96da7dcedb3b46ad86f6f6eb79f1df9535545ce2057d33a4310d", + "b261875f4e45b33be3ba4463c6289c9338ac8d6c0cdf6241e02efaf90063a134", + ), + ( + "b31f0bb0af33091d9e3ae6863afbd07f053f92b54911da4b0133ce693f1dd7c4", + "b5efe56a9b6bb4c8c8104237dee294085870b07c4841ccbebdf8acaed6ca715d", + ), + ( + "2b40f71ee3ac903d109c8b7da077f6dff9e0afe9554c9ab92f4d4d73c2a91725", + "bc8949acf1d08a92aae8dba73dabd6aed1dffd751fe2d8a5f287918b10f69403", + ), + ( + "d75fef40be8bd252851d3ea2c30b5c25530d63f815cc915a595500b969fa7a9c", + "c385458050378228af225d8b9e85d280ee1c7cfc87ce913e567ac807d9afe5c3", + ), + ( + "56626810fd64940aab38221b9887cf9873ab98ad3b76be65d1a9dd7eb57a6b8f", + "6b23ead109d6c195cf7de8541e256d9d731c766888e8b311aa6ce3f5a472d670", + ), + ( + "327769c8eb3cf4399573877b5f109f83eb84165d5835fa5ffa27d6ab5dfdb653", + "4f655ea06fa37a1822109025bb03fb0d570d5bfd8e7318cc03fd6849f823859d", + ), + ( + "bdfa401c768dea8ce7c0d6b06220f18363f09428e3eefc7f75d448cc46811c16", + "ed226852ae405b7ef2f8f51833f41edb1cc39301b87ef69713697693f9bb3362", + ), + ( + "7329977757575938ca8577539977df983c8e0f91ed43d5a352ac494dbc3fc678", + "60bd0e14a72cfa62e9619d17c510839ab9f06b27bf147c514a982bf7dbbb54f2", + ), + ( + "950323d93ad0ca2037a2de03be0847fc30f3f0c972156bee9923d2b78496b09c", + "9418de3a8e7b5d5c508ea1055ebf1fc2b5a8cce25033529c5506621583694376", + ), + ( + "273caeea6ebb49f31f75649b83981f8f8e360733b163219086ace6d1c3d01436", + "4fa284e6f71a0350c5102ac0ea4e0a30e702ff68ca58d6a69868b4ad43f386b1", + ), + ( + "18b093907779a1d85261a65d182a38814708ca445647f8258b977b3cd7525a3b", + "483ce53db22363a6ea3e52fd246b80f907cba3152ebc4c491b8f013292c9f546", + ), + ( + "1a3c04a55d7d20ba267a4169360871059652d2552e18a8224dee4c1a0f7fc7ac", + "c6478a963abdf346315871c6cfba7d8be1ed18d0fea9169c3e597a0e02da4f97", + ), + ( + "fae3653cdb042f08fca6802798f25fbdc3f96ea9b6cfa7b06e03e393c16d13da", + "66013ba954ed1d14c47d1d765ceb7af76c8248f0d7b87f99c937943446bd98da", + ), + ( + "10158e9ac8fede19af59cf59a7a138f3dbcf277703827946b6af2e4f6d95932c", + "b55598f73a860744033656f4ed12d37b48ac70004f4fc00042feb6154f3e8f00", + ), + ( + "207235b4920d3dd4a80cc9e8c4dbacc8e7889de66802bdd0aaa99aa67a19660d", + "3178a8d3ef80b39b008c1ba45462582d877daecf93d31fe1b5e619a74bbdc505", + ), +]; pub const LITECOIN_MAINNET_DUPLICATE: [(&str, &str); 0] = []; pub const LITECOIN_TESTNET_DUPLICATE: [(&str, &str); 0] = []; - + pub const NAMECOIN_MAINNET_DUPLICATE: [(&str, &str); 140] = [ - ("03979691af1a615984ee0a19d51a10618983edbaaf10ff0961a2cac55611c761" , "58120477776250c7ad6d4b1f2a37a32ab41294dd5e1f649b1b7cb1bfbb603a3d"), - ("03979691af1a615984ee0a19d51a10618983edbaaf10ff0961a2cac55611c761" , "6c339b52275535ad0ef3de8dfd084a1c11d5d6d02a88c684d16105cca02d8457"), - ("03979691af1a615984ee0a19d51a10618983edbaaf10ff0961a2cac55611c761" , "c78f92983ba1513b6a39bafa945749acb5afa0d2202c92606ca77f66030aaba8"), - ("06c58c71b42286d170282041d2b8c2980385408d430ae48581fdf8bdd31bac97" , "ff781852670cca017913f608826b905d6832d533c6ec14abe1e1bcd87619ead9"), - ("06c58c71b42286d170282041d2b8c2980385408d430ae48581fdf8bdd31bac97" , "5c3737281fa287ac5f08990be764ab552b0d4413d3f4343091c42ef2861f9ad6"), - ("0ba3dfbfa11fade09ef80a4ca37c65eff1d845b9ea075c2f9b5c6c3cdc3cdff2" , "c67554ff62e0ec3c3c6741313d13d498121ca1ed48920534879a9288c30b15c1"), - ("0ba3dfbfa11fade09ef80a4ca37c65eff1d845b9ea075c2f9b5c6c3cdc3cdff2" , "87055ffada2486990af665916bdc26f2ed0f486959799c3c327518dee092736b"), - ("0c4f987b932d4766f2e02b1caf33e1c6f0758f8d5610f265f39edd06d90a1bde" , "91b58380f87c7f3372bbbbd07a85dbec1f09cdef118a094505c9f9b789f98036"), - ("0c4f987b932d4766f2e02b1caf33e1c6f0758f8d5610f265f39edd06d90a1bde" , "c179f3270563372ef79237da9ef489ee20e983a8a1d40505ef2a4dcfe2aad8a2"), - ("0ffbbae98fe857cdc1ff50b6d58e8c804263cdb320285e3d6778297833a4ccd8" , "f0821b73de313853652085c329c6c60e805b8758b4a6f4d8b6d4ee0299300f18"), - ("0ffbbae98fe857cdc1ff50b6d58e8c804263cdb320285e3d6778297833a4ccd8" , "bf1a46b5d33143d45a73980df7a8688051443baaaabf4351a15e5a271cc3a1e9"), - ("11e173d85e4f580f7ed611a6f45ce20fd5f21d526ce3ba73fac0114e9031480b" , "7ffdc5a5265e12d2d4bc9981d2b577a87945e934b17813995bb1636a669a16df"), - ("11e173d85e4f580f7ed611a6f45ce20fd5f21d526ce3ba73fac0114e9031480b" , "244311b247ba303d07ac2dcb13b93b777978e4b92dd5b810a72f455bf6f18b5f"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "bec0d4fd6edb94d8402a14d1e7d84af92b0f8df0c4787f8f538e6430a9545e2f"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "19e7ffcc5a0b1868c70f3ee6d000cd6d0c397967e3e1cb9f870e127ecb92bd0b"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "ad9e9aac1781335ae13e37ad8f7754f876509bc0bbd53f31d71cea73b4acda23"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "daea845eef06101bf421f358328deb633d0f91bd529ac0b100e71ca1e8fc10fe"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "f3fe7d771eb9cef54d99538f612231a0a219340a88653f536e7f57b497df176d"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "699e9cc7d871e875046c9bf291eeff1191fd035dd1aeb3928126c8b8842aa54b"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "bf8fcfa796bba3ca8da5e2e2ff5ae6556081765508d56de4e0603fc16a0de527"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "8e937cf0b14ded0c56a58997e91be99d6452a73d2023c0343aa11a5e09eadd6c"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "bee58ed2068441cf1376f2fd7c59892ee62c426aeff170d84f676f38210bb607"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "d1e2b9b470af6b52d54627a01605a2309b823108b90387260b1eee8a158af5ec"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "dad396c559740ecfe762d54b6d8d27da7cb20b5d25d1ef17a0e4d435f91aeb3b"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "5b71c45d0fee078c3ef8a0c83a5ca089084d498cd35ce673783deff7dd0a4080"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "5476fb6652c64569e796062087cd712abc35b220a987d16649f1a14700111b8e"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "bcaa2f4aae1ffa43a37ff8dea324bc27b0989295573868e1c2ffe6e303c52a54"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "c88987c698807dc3f868fb12710bdd959af8ca9bc40e9a7d5bce711e047c82c9"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "77a0c25c66832a444cfd893cec81a23fa7623f04fa173174264c61bf17d9deb7"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "f28196e5af1bccdd289c40e7800119e490167c10451f5d80bd0e83b35331c6c0"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "a4869a08f052be93017d8a41478832a736c5fdd40b7b1e661a4a877c7401a152"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "ab0b1b589837634e07206c13ee59cf796ed7788a3868991967ce2c3d5418fd90"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "ff699ed6779eb5f05575e4f3fcc9293fbcc56e58b47c9e01d224f13c868fab33"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "a15ef3b26817cceba98078367ecc858c8e387f5c40bde033bdeac328d6a93508"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "68cc4b48d15d2948d68360a408678a710282d119f8636bd4f24476434ac77a8d"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "0d1fd17d9afda993877567307d88ace3b3af6931c50222b5678f1544766608ad"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "ca50065b949ee5e772a2c8c727d5794d42bd6b383793afe5173d190a5ff00339"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "4dcc1e737b361df01a0d15316643de9f4df4e5fa157fd7927645e7ea03438280"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "f8eafe1e3efff8cd729c05ecd8d071c41a0749c9ff460bf02cf1e59da776740d"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "204855dc22a62a9f484e8af72a5d3738667e492b1fa67c2bbf4e47aa9c7ca191"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "cbd48b5a9d71681d14ececc8234a5abbbe3dba41d8bd8160b3a1b432ecec1068"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "aa911ae6a623737dce11d867cb7011abb8d9ac6dae79fdce73deac6e2a8b52db"), - ("1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169" , "53c54ddd2f5176cad94c5e581e6196c8f54806ffeba17b28a1ffa9c901d00c5e"), - ("25c4f73e26e35e683deee9d2dac6cf551b95a8f5038a8c5e34be1edea0074c02" , "1e5e4dfa76ec170b6c100b5c4db859cefda711cbf2a36372c0bf23c797aa4296"), - ("25c4f73e26e35e683deee9d2dac6cf551b95a8f5038a8c5e34be1edea0074c02" , "66a1785af96941a84cf47f4603436c778906e6f17bc8236e0051814e973bba88"), - ("2ceb336eb39fdc18e3cb305ced128739394d4300be8cf974409813686528f1dc" , "0fdba12a64e014adce95fc7f0f26949bf89cc4d4522e195e2785b4745f796286"), - ("2ceb336eb39fdc18e3cb305ced128739394d4300be8cf974409813686528f1dc" , "c91d94956870181b951465119c40f29835894dd68c27a89145aa127c1b59ff12"), - ("301554b304deeb1a6b8036d874435cff7100c0a00a232dc70c3843062efeec08" , "20c11f81dbcd97fd1507ee7bc50a2d96e195b83ad5c00326c97f243c238923b9"), - ("301554b304deeb1a6b8036d874435cff7100c0a00a232dc70c3843062efeec08" , "a84416dd5aa511e87f65b45d6c3d750d1b31e9195673c160ca6944d2a344484e"), - ("36578daefeddb91f040d46cb7793127c34cfa79858695f8ce7ac13d55ebe1c8d" , "9287147c5765bbd4fc3dfe9adf857493ce30700dc31ae144e14e51ab1766db14"), - ("36578daefeddb91f040d46cb7793127c34cfa79858695f8ce7ac13d55ebe1c8d" , "208a68893b55cb122c08a0c72b2972717fbb13a463bda2c42280150db0365cbd"), - ("399b6b152a20c5bd2ef4e921db50bf13ab3807744c99fe971e5e41e41782698f" , "7bc37b445f569792a34f6ecbe81ba95cf5ff31a0aaa9730702a03aad602304c4"), - ("399b6b152a20c5bd2ef4e921db50bf13ab3807744c99fe971e5e41e41782698f" , "c943047ac52aebcf6e592699c9d480f6e7f71683962efaa5f2b109cb9e636947"), - ("399b6b152a20c5bd2ef4e921db50bf13ab3807744c99fe971e5e41e41782698f" , "bd50a9893274f8403d12c3b5868cd0aac7e90e5dc37896a970c121345ff5f4e7"), - ("3fa18ccf37a343292ee81d06b1216943f55c8b452ae5bb1498a3a30dafc8e823" , "c51ad9b72cf1fcc1a7b9ddafbd6cd05dd413ec2a1e12e563a0e73f1231e5156f"), - ("3fa18ccf37a343292ee81d06b1216943f55c8b452ae5bb1498a3a30dafc8e823" , "c8fffe85aa626c29f5f4e8c9837fbe0296828319058a0588c46bbb555272e515"), - ("6036697990cab852888319acc39b2891fdac4c54666031c68d8a75163fc980e9" , "746a46d24478a1da5867daa34ba11c1b23d5b688abd15e2a8ac910af06f00d53"), - ("6036697990cab852888319acc39b2891fdac4c54666031c68d8a75163fc980e9" , "31c1d2b95d4dfeb27ca611a60ddf35388d81fe89b973e08d8dd68867f916b01d"), - ("6422a976822b1250c0302d71748cbb91103f1fcb027e30cbd0fe8a83604d8da7" , "ac3705916f68bccbd7ee33fcf8431ba3a35fa10c18f78fe5f13df8ea30b94ee4"), - ("6422a976822b1250c0302d71748cbb91103f1fcb027e30cbd0fe8a83604d8da7" , "a617e370a112350309ae0b1efa8612b08e18c6163b6f41b2eb3e62436ec26e5c"), - ("6866d471f59022829d5ff56e78e5f8425401ed9d4ec2b79eb5092f88883f7675" , "984792affc14dcbce1b7610dbd8a19019b99d26b4f0bcd702228cea9835e7306"), - ("6866d471f59022829d5ff56e78e5f8425401ed9d4ec2b79eb5092f88883f7675" , "a50770438931db99140dce451784dae73f2a6ebc94b41734d269db7c8eeeadd5"), - ("6b96b8dec83d0a3e11e9534089d8dab439f567d9df56852f58a6b397101224ad" , "c01cb72df67ea48838fa94a3b2675a533ee768442767c268cb206c604e842335"), - ("6b96b8dec83d0a3e11e9534089d8dab439f567d9df56852f58a6b397101224ad" , "4373d910d712ebc595933642bbc799e3580929a511a6021eac016b134d7d5ae3"), - ("6ff45af05eeed74fcd8854727b67e81ed1848f8a5a805ac9439cb37c97b02cc9" , "94756545250a9d32cebfcce40691c1b8b7e0937a152fc09d8f71cd1922cfa285"), - ("6ff45af05eeed74fcd8854727b67e81ed1848f8a5a805ac9439cb37c97b02cc9" , "18ad8e29399a4f217b09a622d9216207d8fa6be1dda76c101b0330eb179e72ae"), - ("705ac28142d306c1c672dafb46cfa60e1f81ea8c64e9e8dae9aa7d8b66d0e889" , "70e40a6025f23d3dd79ffa72f593bb8b75593cf1a3cdfed7b68966941fa191d9"), - ("705ac28142d306c1c672dafb46cfa60e1f81ea8c64e9e8dae9aa7d8b66d0e889" , "a9111fbc960551cfcd697926043e58d6935028e096cfc98b623b3c1995f54152"), - ("740a2fe30b95d47bd53704884ee66fe88f386f51436b31434c53be061a90309f" , "fa8405603e53e4af19be7667fb9a2029bc18f256e67d1e4ca776b65250e4fbc0"), - ("740a2fe30b95d47bd53704884ee66fe88f386f51436b31434c53be061a90309f" , "fa8e0a29f10fa07bbdc398ebd1a78b4f572f501df8b8b4efed6a3335b82af6cd"), - ("777d8fbba990488bee5214092ef7eef9958190bc354cbe9111e3a7887b3fc03f" , "5208e2527f7b1149b761d1ad8d7e2755119fc6b6e661f45a68d1192179f79017"), - ("777d8fbba990488bee5214092ef7eef9958190bc354cbe9111e3a7887b3fc03f" , "4770d4cc0c3db78c3fc7f0deb092f0e1155902da482e82ecafd630e64a783824"), - ("798a5261fc7eeeec6970914b171a2f9d1415aaf5e8979cba8dc1773ea286dad8" , "ef229f116182295ce6bd2c31591cb087fd2acf2d951d887e3ebb99b0d6774fc5"), - ("798a5261fc7eeeec6970914b171a2f9d1415aaf5e8979cba8dc1773ea286dad8" , "a4eb8c9e8e6972c7c8df8e94925516b938f69f349f15be5d63bd4192aedf2183"), - ("7e7444fe28ee1795d516c1d7f6ec36f069d0c6d7a0b6f9b1c46d240aacde2009" , "def2f2258bb490220fcbd55aa94b5f432a3768b5556da565035e7da6fc879226"), - ("7e7444fe28ee1795d516c1d7f6ec36f069d0c6d7a0b6f9b1c46d240aacde2009" , "a5c7bc18a7fabecb92ac536668753b68ce150d5770334e3372791d0b5dc710b0"), - ("7e7444fe28ee1795d516c1d7f6ec36f069d0c6d7a0b6f9b1c46d240aacde2009" , "ba807c181b2f28e122ce8363a50c7637de2637dbbb28a4487f70ed55c81cc051"), - ("844f0176ef176e220e1e46aacb8d078c84caf00ffaaa5700cd4de801c6537f06" , "54a5a9c9a5e5501a34584db98488319fd713ed38d3f2126e64ef28ef1b1ac784"), - ("844f0176ef176e220e1e46aacb8d078c84caf00ffaaa5700cd4de801c6537f06" , "6388f47e93d4a63a576ae8019aa0769a00d4cb24dcf33e970fc563bcf49be259"), - ("846ff6835153efa63ff1b279f252bb8085e64eec742c8eb70d9d55e9aa07d3f6" , "e7560bba6d6cf0007e769b159aaed9ab52ab5873b91e2410977630003b694bc3"), - ("846ff6835153efa63ff1b279f252bb8085e64eec742c8eb70d9d55e9aa07d3f6" , "5732ed78db5797117024f3ae182ddd67c3a71ab2512d0e919e5ef3c16c508ebc"), - ("9701eff5653457a571384df686ff5617ff96176e1e9a43900f93e0338776d726" , "5e5a04b3c4078c2bb1c3403897de7cbb8200d2ea34e2e2e1afb8d4db35a92694"), - ("9701eff5653457a571384df686ff5617ff96176e1e9a43900f93e0338776d726" , "428d2866ab85ffb4b8d72f8f0fc9837324c0159fb787af1bc5c06d24cd850c9f"), - ("9a568c8cbcb633ba8a22ba60fb61cca0b1fd49dbb3fa6b9842d0bf1971e843c8" , "6410fd312871aa4eeb4ac157555c42d26461017e702cff2e73bdb608f8aaf0d0"), - ("9a568c8cbcb633ba8a22ba60fb61cca0b1fd49dbb3fa6b9842d0bf1971e843c8" , "cca48f712e5dd8e4dccf7cbf84bf689a0188bedd0b9e86b0f9b279a3281c908d"), - ("9adb181ddfba2c92cac5b973dab5832423db256497c1ebcb67c7a96cad09b17e" , "66da71179d71c4bc215f76375576165383dfa4d23aed39b57299349aaf577370"), - ("9adb181ddfba2c92cac5b973dab5832423db256497c1ebcb67c7a96cad09b17e" , "eb3b5a9766cca9d209d61709359d611fc7a5950e156940f36aea92601f48d341"), - ("a4d0f09bfa96b840f19bff7556ba740b1ead5f643c4bedce483722b270a33a8e" , "111f3e556a61a21d1fba83c304ba469f1166c88345ed2e051386065aee379282"), - ("a4d0f09bfa96b840f19bff7556ba740b1ead5f643c4bedce483722b270a33a8e" , "791934d83144aa3b05ff970a4012b200ae2c7c735e0a7e9b9f66101474762e75"), - ("a5e0cf3d431db8b5ca7b2c061ea98d0a147b880e880f11c93c3905ae631223d1" , "6a331f0b65703151dddb65e0bf15098a56537ea8b3bf39cc5a888ad77c93d1ef"), - ("a5e0cf3d431db8b5ca7b2c061ea98d0a147b880e880f11c93c3905ae631223d1" , "d20512306ee5ab6d0db5accb86ed4d652fe9ea0cd0325f56457da10266ab4ec7"), - ("aac136e7a9d14793453e6d1a12b3da0439cb65eb138ea1915917d60a740bfb69" , "ab9c5054bc11f8de2a998bf620941828ddf31522c7b2fac38986a5fd2320934b"), - ("aac136e7a9d14793453e6d1a12b3da0439cb65eb138ea1915917d60a740bfb69" , "a161247fc07c501a5545d390ed37ce5b407b734d4585f25fd2f5c6eab3af2196"), - ("b2528cadf1ea052ade46ea2c8a2ad3048723acfde9ec5d27824439ae9e1569a8" , "f7919c9733bf9bac645795d1fce209eadf12786139360afc6d7b269be3f6a9bb"), - ("b2528cadf1ea052ade46ea2c8a2ad3048723acfde9ec5d27824439ae9e1569a8" , "bd31a23955363c024b3ee78ddec5e14cb76a63b16a8572db374f6d8e7f7f7ca7"), - ("b4208b470766dd1add3aaf177a53809aa0fc802990bb44edf6fac9b485902f93" , "b0a83413d1d30f064b39d1ade0f527bb7da06069886759a701123833986f652d"), - ("b4208b470766dd1add3aaf177a53809aa0fc802990bb44edf6fac9b485902f93" , "1cc32f89936ccef869a4699569e4c16f9336b3a82f238ae07a72717df1dd576d"), - ("b5acd98e163b3f98b0a176b87468000b77192799234a0f1ebb45fa74ddf601fc" , "842f8a3be62f8468472b44929b918b4f3b595c1a736092ff310f9c424b841a0b"), - ("b5acd98e163b3f98b0a176b87468000b77192799234a0f1ebb45fa74ddf601fc" , "6076f677198824f1ca7bdd433a0a39b90a0ffcdde8869569e4c17f97226de64f"), - ("b61edfe55212a2bd6e8027a575f76cb5e5d3d994a823abd941cad41622a3e61d" , "124cafbb0a42a71de3545795325c0b9500c755a97e76a1735a1a4bd715945897"), - ("b61edfe55212a2bd6e8027a575f76cb5e5d3d994a823abd941cad41622a3e61d" , "41bbaea99b54b9a1c8caf13e375ca6d58872ca17dec98285a055ececebcf5acb"), - ("b7d56366b4ae4fb321168c7945fa33323e50f8262cf385234cceb693b6b2a76d" , "65b5463c32f27e7f2916660b5fb94be1373e8a3747733f02e8fbea10511683da"), - ("b7d56366b4ae4fb321168c7945fa33323e50f8262cf385234cceb693b6b2a76d" , "adff00eb8297d905e4a17d47bcf47d489c5fc2fcc77654e6ac982e9d81b4b6e1"), - ("b81fd6b2aaf7553f9b7f93e774dd3972083b072bb53c004df4cc04f36a0d8523" , "c7b5452cd57068ffa67e2d0c05fab679b364820fba0bc6e774f4bdf1b62e0e89"), - ("b81fd6b2aaf7553f9b7f93e774dd3972083b072bb53c004df4cc04f36a0d8523" , "e396f9c25772a6e3f2b6b9ee4ac5ad975c874c3ffa62bcf0e58c9ac05f521900"), - ("bcc442c4aaf3e935bf1a0052a355e797ac27b8bd81cb4fa0f8abe6c41ae8a86d" , "1eff53b8db77a442eadb2570a38199367c48cd97b68387d848c51527aac31c0e"), - ("bcc442c4aaf3e935bf1a0052a355e797ac27b8bd81cb4fa0f8abe6c41ae8a86d" , "dc10457c0101a5f517a17997d2e92fa4fa052714fe78b2450a88a8540d8ac611"), - ("c22c4fc34582d13e3211a67fa6baa0b4d9d6495934301e2d5c28146b1a479bef" , "5a967c74a3ba5feaf3cde1369ce28c7faae2e1361447a25f29faf9f85e93c96e"), - ("c22c4fc34582d13e3211a67fa6baa0b4d9d6495934301e2d5c28146b1a479bef" , "178394ea8ab5e1d9d1bbb78544d32dcaf25baef728b2c82b695e5e20e963725d"), - ("c8efeb6a87b1346438316bdc323a98cda2eb3ae810fb7ce8b3e53b640d59d1c1" , "dd1690a70e5ac9c25b9ccaf9716a4db28a0f2f95f1e16f6ed8dfcf7b4e7023f6"), - ("c8efeb6a87b1346438316bdc323a98cda2eb3ae810fb7ce8b3e53b640d59d1c1" , "eaf7f613bef4b8bde170d15905d2b4193b01d1402d206b06a4426165d12661d8"), - ("c9069c6641c22ecca70a03f4479e43fd9a972cfc57f6235e9291aa64c8e0f0e8" , "124bc878c8421f9cc7edfffda88a68fc530873716a41f11d623b8b7b143b95cb"), - ("c9069c6641c22ecca70a03f4479e43fd9a972cfc57f6235e9291aa64c8e0f0e8" , "23d48e114f678fe1d1ee1b191dcc622f15e5ff3881f82343eea008549df213e1"), - ("cc8f31fc183925d7c7977c49ab343f1a09c7f0bbce845b5a78ecab521bb99a21" , "9794509b96f692a91aa1e3c385047a4f143e9a13660c7581b42569149cbf10f6"), - ("cc8f31fc183925d7c7977c49ab343f1a09c7f0bbce845b5a78ecab521bb99a21" , "b42f2c4b8b751af0af5ba9780dfc3f0139a77df14d1894ff0a7ae573abe2e8d9"), - ("d8ca721844b8ae7a767768696b8c5e3f5d3f8b19d069dec9f0c7d91158098487" , "20eb5a4d1b0824603374703b1c26ea4b01bbf19cdabe3dc48e5a29d00bfb6ce5"), - ("d8ca721844b8ae7a767768696b8c5e3f5d3f8b19d069dec9f0c7d91158098487" , "2b152ee50975c339175a54ce52d4b7d34c0d84ed6a13f6a5951d48fc6ed1bcd6"), - ("d8ca721844b8ae7a767768696b8c5e3f5d3f8b19d069dec9f0c7d91158098487" , "c4c071498b01ea6ba5cd608db79a823b281b921cc5ef54005dbef15a423042b8"), - ("daa34cf0fb5bc39112b2494a301b170ccd87832d10efc9243b1fb8c817c81044" , "2f5233d45c4da97ae018240bd29a3ef1f8406f7458a6e3cf520b054b6bcb4f7c"), - ("daa34cf0fb5bc39112b2494a301b170ccd87832d10efc9243b1fb8c817c81044" , "ac0d4dda19032e8b78c88fbebede308b6a847ea54dccb320306322a7741d3c6a"), - ("daa34cf0fb5bc39112b2494a301b170ccd87832d10efc9243b1fb8c817c81044" , "adcb4e7158ed853afe5b8f8b9b4c1c42e29a39af9b0bf98e182d15f0373b72bb"), - ("daa34cf0fb5bc39112b2494a301b170ccd87832d10efc9243b1fb8c817c81044" , "69421d277e28070408bfbe9d4b8cd1e85a44dd4ef2f490630b2bf15d50adfadf"), - ("daa34cf0fb5bc39112b2494a301b170ccd87832d10efc9243b1fb8c817c81044" , "a8e537178fe74fbeda0c9d5f5fb52536f09facc4ba99a87dc7cf761b9b5e9d29"), - ("dbca47b75bbc00d0f8e4b6168416a9c1cdfd1519a7d9b6e1dcee4b72f9b85da2" , "b4a1df8d362fdab21b6cbb2e8aba105c7dc884ced82fc74f353e69a4e99b13e4"), - ("dbca47b75bbc00d0f8e4b6168416a9c1cdfd1519a7d9b6e1dcee4b72f9b85da2" , "8b9f9eede8a1311183ffe12de383472ebc0b28785f87d90996fc29a732c78a0a"), - ("dc24fc48e7dc473a8531d9033d61666175c2fa8f47ebbe06c8ee4e3c835503be" , "61adec85dc1782f5718663e347a36e7d953337113a9b7b599af6cc5d27384699"), - ("dc24fc48e7dc473a8531d9033d61666175c2fa8f47ebbe06c8ee4e3c835503be" , "033234e75fd174df903f2caed46741f20048057b9c0de7fefc2a64f8d3f3f5b7"), - ("e0276f6188f705c611b362950d8dbdf6763a83c3aa81e95ff1450486301ddd8a" , "4edd0eff698314492c2ac3730d16c3662b3738fa53d71eaa610e4fae2bc4767f"), - ("e0276f6188f705c611b362950d8dbdf6763a83c3aa81e95ff1450486301ddd8a" , "dac50924df85a3d94bb01bcc1f258065c4e74d1f38c267ace4496316e76c6f18"), - ("e3d835dc468032a4450b596138cbff37d3888f95c1c9868202a1b4da75698062" , "72c6e3a4b8793ac095dc03551380507e0341ed86f2a9fc4b5547defbfe98cae7"), - ("e3d835dc468032a4450b596138cbff37d3888f95c1c9868202a1b4da75698062" , "04033fa4cc59feaa3e187343d42484ef6053ea0a4fc6ac74103e9e26c44d4c6d"), - ("e4c8ef61fa3fd2732d6587ac6c67c17f887f0a0372318e5fa05e6421627f0b3e" , "baf955cc116b3bb3e6fd77dcf1264fa690bd2a3f83526574f5eeb4f6100e2fd6"), - ("e4c8ef61fa3fd2732d6587ac6c67c17f887f0a0372318e5fa05e6421627f0b3e" , "9c825788eb889d6c8e39057d2253b23b350c27a94130b112b8d500e48e758689"), - ("e4ee744a0efb8818b821536252656863387b6c564b8153a6339c0f77570105b5" , "351515f07f58d7e6b1e1f64996be80b91ca76d592d5053a467025a1ccfdea0d6"), - ("e4ee744a0efb8818b821536252656863387b6c564b8153a6339c0f77570105b5" , "014618bec2565496cfbbc3cda3ad4cd8740b08c8cac11a6e2f6775a1e50f0175"), - ("e4ee744a0efb8818b821536252656863387b6c564b8153a6339c0f77570105b5" , "515837eb87234faaf62b85e997f00d9fd6cf47fc4491e5883c6cdfe8f8c90a7a"), - ("e93063adbf76004c7512d2a790ec17800809e73c3450f188fae2583d6526b493" , "1f9fc1a291c71a5bbd91a411ff7f7a8bee0676b62aea64b994ae72955fa34586"), - ("e93063adbf76004c7512d2a790ec17800809e73c3450f188fae2583d6526b493" , "49ad862d912b00a88d45ca591a0545b247b913373568120ed505a08cc586c3ba"), - ("ffd4043495ce946112233b94b4cca5f330be6cea693e1933dc032abfe0ab628d" , "ad86c7c280ddc9a7af6bfcb83b230d9c8d810ccb90d01c60a8341b5f7ea1bedb"), - ("ffd4043495ce946112233b94b4cca5f330be6cea693e1933dc032abfe0ab628d" , "94506b8b0c29e8cbd9f7702e0593b002fb99c900d55216ba76cd15bc71c4a1ab"), + ( + "03979691af1a615984ee0a19d51a10618983edbaaf10ff0961a2cac55611c761", + "58120477776250c7ad6d4b1f2a37a32ab41294dd5e1f649b1b7cb1bfbb603a3d", + ), + ( + "03979691af1a615984ee0a19d51a10618983edbaaf10ff0961a2cac55611c761", + "6c339b52275535ad0ef3de8dfd084a1c11d5d6d02a88c684d16105cca02d8457", + ), + ( + "03979691af1a615984ee0a19d51a10618983edbaaf10ff0961a2cac55611c761", + "c78f92983ba1513b6a39bafa945749acb5afa0d2202c92606ca77f66030aaba8", + ), + ( + "06c58c71b42286d170282041d2b8c2980385408d430ae48581fdf8bdd31bac97", + "ff781852670cca017913f608826b905d6832d533c6ec14abe1e1bcd87619ead9", + ), + ( + "06c58c71b42286d170282041d2b8c2980385408d430ae48581fdf8bdd31bac97", + "5c3737281fa287ac5f08990be764ab552b0d4413d3f4343091c42ef2861f9ad6", + ), + ( + "0ba3dfbfa11fade09ef80a4ca37c65eff1d845b9ea075c2f9b5c6c3cdc3cdff2", + "c67554ff62e0ec3c3c6741313d13d498121ca1ed48920534879a9288c30b15c1", + ), + ( + "0ba3dfbfa11fade09ef80a4ca37c65eff1d845b9ea075c2f9b5c6c3cdc3cdff2", + "87055ffada2486990af665916bdc26f2ed0f486959799c3c327518dee092736b", + ), + ( + "0c4f987b932d4766f2e02b1caf33e1c6f0758f8d5610f265f39edd06d90a1bde", + "91b58380f87c7f3372bbbbd07a85dbec1f09cdef118a094505c9f9b789f98036", + ), + ( + "0c4f987b932d4766f2e02b1caf33e1c6f0758f8d5610f265f39edd06d90a1bde", + "c179f3270563372ef79237da9ef489ee20e983a8a1d40505ef2a4dcfe2aad8a2", + ), + ( + "0ffbbae98fe857cdc1ff50b6d58e8c804263cdb320285e3d6778297833a4ccd8", + "f0821b73de313853652085c329c6c60e805b8758b4a6f4d8b6d4ee0299300f18", + ), + ( + "0ffbbae98fe857cdc1ff50b6d58e8c804263cdb320285e3d6778297833a4ccd8", + "bf1a46b5d33143d45a73980df7a8688051443baaaabf4351a15e5a271cc3a1e9", + ), + ( + "11e173d85e4f580f7ed611a6f45ce20fd5f21d526ce3ba73fac0114e9031480b", + "7ffdc5a5265e12d2d4bc9981d2b577a87945e934b17813995bb1636a669a16df", + ), + ( + "11e173d85e4f580f7ed611a6f45ce20fd5f21d526ce3ba73fac0114e9031480b", + "244311b247ba303d07ac2dcb13b93b777978e4b92dd5b810a72f455bf6f18b5f", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "bec0d4fd6edb94d8402a14d1e7d84af92b0f8df0c4787f8f538e6430a9545e2f", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "19e7ffcc5a0b1868c70f3ee6d000cd6d0c397967e3e1cb9f870e127ecb92bd0b", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "ad9e9aac1781335ae13e37ad8f7754f876509bc0bbd53f31d71cea73b4acda23", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "daea845eef06101bf421f358328deb633d0f91bd529ac0b100e71ca1e8fc10fe", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "f3fe7d771eb9cef54d99538f612231a0a219340a88653f536e7f57b497df176d", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "699e9cc7d871e875046c9bf291eeff1191fd035dd1aeb3928126c8b8842aa54b", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "bf8fcfa796bba3ca8da5e2e2ff5ae6556081765508d56de4e0603fc16a0de527", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "8e937cf0b14ded0c56a58997e91be99d6452a73d2023c0343aa11a5e09eadd6c", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "bee58ed2068441cf1376f2fd7c59892ee62c426aeff170d84f676f38210bb607", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "d1e2b9b470af6b52d54627a01605a2309b823108b90387260b1eee8a158af5ec", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "dad396c559740ecfe762d54b6d8d27da7cb20b5d25d1ef17a0e4d435f91aeb3b", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "5b71c45d0fee078c3ef8a0c83a5ca089084d498cd35ce673783deff7dd0a4080", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "5476fb6652c64569e796062087cd712abc35b220a987d16649f1a14700111b8e", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "bcaa2f4aae1ffa43a37ff8dea324bc27b0989295573868e1c2ffe6e303c52a54", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "c88987c698807dc3f868fb12710bdd959af8ca9bc40e9a7d5bce711e047c82c9", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "77a0c25c66832a444cfd893cec81a23fa7623f04fa173174264c61bf17d9deb7", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "f28196e5af1bccdd289c40e7800119e490167c10451f5d80bd0e83b35331c6c0", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "a4869a08f052be93017d8a41478832a736c5fdd40b7b1e661a4a877c7401a152", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "ab0b1b589837634e07206c13ee59cf796ed7788a3868991967ce2c3d5418fd90", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "ff699ed6779eb5f05575e4f3fcc9293fbcc56e58b47c9e01d224f13c868fab33", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "a15ef3b26817cceba98078367ecc858c8e387f5c40bde033bdeac328d6a93508", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "68cc4b48d15d2948d68360a408678a710282d119f8636bd4f24476434ac77a8d", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "0d1fd17d9afda993877567307d88ace3b3af6931c50222b5678f1544766608ad", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "ca50065b949ee5e772a2c8c727d5794d42bd6b383793afe5173d190a5ff00339", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "4dcc1e737b361df01a0d15316643de9f4df4e5fa157fd7927645e7ea03438280", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "f8eafe1e3efff8cd729c05ecd8d071c41a0749c9ff460bf02cf1e59da776740d", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "204855dc22a62a9f484e8af72a5d3738667e492b1fa67c2bbf4e47aa9c7ca191", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "cbd48b5a9d71681d14ececc8234a5abbbe3dba41d8bd8160b3a1b432ecec1068", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "aa911ae6a623737dce11d867cb7011abb8d9ac6dae79fdce73deac6e2a8b52db", + ), + ( + "1fee0e932af5e0af68cc1e19ad6bea1247e08b9d10b97d60dbf7f3f40282e169", + "53c54ddd2f5176cad94c5e581e6196c8f54806ffeba17b28a1ffa9c901d00c5e", + ), + ( + "25c4f73e26e35e683deee9d2dac6cf551b95a8f5038a8c5e34be1edea0074c02", + "1e5e4dfa76ec170b6c100b5c4db859cefda711cbf2a36372c0bf23c797aa4296", + ), + ( + "25c4f73e26e35e683deee9d2dac6cf551b95a8f5038a8c5e34be1edea0074c02", + "66a1785af96941a84cf47f4603436c778906e6f17bc8236e0051814e973bba88", + ), + ( + "2ceb336eb39fdc18e3cb305ced128739394d4300be8cf974409813686528f1dc", + "0fdba12a64e014adce95fc7f0f26949bf89cc4d4522e195e2785b4745f796286", + ), + ( + "2ceb336eb39fdc18e3cb305ced128739394d4300be8cf974409813686528f1dc", + "c91d94956870181b951465119c40f29835894dd68c27a89145aa127c1b59ff12", + ), + ( + "301554b304deeb1a6b8036d874435cff7100c0a00a232dc70c3843062efeec08", + "20c11f81dbcd97fd1507ee7bc50a2d96e195b83ad5c00326c97f243c238923b9", + ), + ( + "301554b304deeb1a6b8036d874435cff7100c0a00a232dc70c3843062efeec08", + "a84416dd5aa511e87f65b45d6c3d750d1b31e9195673c160ca6944d2a344484e", + ), + ( + "36578daefeddb91f040d46cb7793127c34cfa79858695f8ce7ac13d55ebe1c8d", + "9287147c5765bbd4fc3dfe9adf857493ce30700dc31ae144e14e51ab1766db14", + ), + ( + "36578daefeddb91f040d46cb7793127c34cfa79858695f8ce7ac13d55ebe1c8d", + "208a68893b55cb122c08a0c72b2972717fbb13a463bda2c42280150db0365cbd", + ), + ( + "399b6b152a20c5bd2ef4e921db50bf13ab3807744c99fe971e5e41e41782698f", + "7bc37b445f569792a34f6ecbe81ba95cf5ff31a0aaa9730702a03aad602304c4", + ), + ( + "399b6b152a20c5bd2ef4e921db50bf13ab3807744c99fe971e5e41e41782698f", + "c943047ac52aebcf6e592699c9d480f6e7f71683962efaa5f2b109cb9e636947", + ), + ( + "399b6b152a20c5bd2ef4e921db50bf13ab3807744c99fe971e5e41e41782698f", + "bd50a9893274f8403d12c3b5868cd0aac7e90e5dc37896a970c121345ff5f4e7", + ), + ( + "3fa18ccf37a343292ee81d06b1216943f55c8b452ae5bb1498a3a30dafc8e823", + "c51ad9b72cf1fcc1a7b9ddafbd6cd05dd413ec2a1e12e563a0e73f1231e5156f", + ), + ( + "3fa18ccf37a343292ee81d06b1216943f55c8b452ae5bb1498a3a30dafc8e823", + "c8fffe85aa626c29f5f4e8c9837fbe0296828319058a0588c46bbb555272e515", + ), + ( + "6036697990cab852888319acc39b2891fdac4c54666031c68d8a75163fc980e9", + "746a46d24478a1da5867daa34ba11c1b23d5b688abd15e2a8ac910af06f00d53", + ), + ( + "6036697990cab852888319acc39b2891fdac4c54666031c68d8a75163fc980e9", + "31c1d2b95d4dfeb27ca611a60ddf35388d81fe89b973e08d8dd68867f916b01d", + ), + ( + "6422a976822b1250c0302d71748cbb91103f1fcb027e30cbd0fe8a83604d8da7", + "ac3705916f68bccbd7ee33fcf8431ba3a35fa10c18f78fe5f13df8ea30b94ee4", + ), + ( + "6422a976822b1250c0302d71748cbb91103f1fcb027e30cbd0fe8a83604d8da7", + "a617e370a112350309ae0b1efa8612b08e18c6163b6f41b2eb3e62436ec26e5c", + ), + ( + "6866d471f59022829d5ff56e78e5f8425401ed9d4ec2b79eb5092f88883f7675", + "984792affc14dcbce1b7610dbd8a19019b99d26b4f0bcd702228cea9835e7306", + ), + ( + "6866d471f59022829d5ff56e78e5f8425401ed9d4ec2b79eb5092f88883f7675", + "a50770438931db99140dce451784dae73f2a6ebc94b41734d269db7c8eeeadd5", + ), + ( + "6b96b8dec83d0a3e11e9534089d8dab439f567d9df56852f58a6b397101224ad", + "c01cb72df67ea48838fa94a3b2675a533ee768442767c268cb206c604e842335", + ), + ( + "6b96b8dec83d0a3e11e9534089d8dab439f567d9df56852f58a6b397101224ad", + "4373d910d712ebc595933642bbc799e3580929a511a6021eac016b134d7d5ae3", + ), + ( + "6ff45af05eeed74fcd8854727b67e81ed1848f8a5a805ac9439cb37c97b02cc9", + "94756545250a9d32cebfcce40691c1b8b7e0937a152fc09d8f71cd1922cfa285", + ), + ( + "6ff45af05eeed74fcd8854727b67e81ed1848f8a5a805ac9439cb37c97b02cc9", + "18ad8e29399a4f217b09a622d9216207d8fa6be1dda76c101b0330eb179e72ae", + ), + ( + "705ac28142d306c1c672dafb46cfa60e1f81ea8c64e9e8dae9aa7d8b66d0e889", + "70e40a6025f23d3dd79ffa72f593bb8b75593cf1a3cdfed7b68966941fa191d9", + ), + ( + "705ac28142d306c1c672dafb46cfa60e1f81ea8c64e9e8dae9aa7d8b66d0e889", + "a9111fbc960551cfcd697926043e58d6935028e096cfc98b623b3c1995f54152", + ), + ( + "740a2fe30b95d47bd53704884ee66fe88f386f51436b31434c53be061a90309f", + "fa8405603e53e4af19be7667fb9a2029bc18f256e67d1e4ca776b65250e4fbc0", + ), + ( + "740a2fe30b95d47bd53704884ee66fe88f386f51436b31434c53be061a90309f", + "fa8e0a29f10fa07bbdc398ebd1a78b4f572f501df8b8b4efed6a3335b82af6cd", + ), + ( + "777d8fbba990488bee5214092ef7eef9958190bc354cbe9111e3a7887b3fc03f", + "5208e2527f7b1149b761d1ad8d7e2755119fc6b6e661f45a68d1192179f79017", + ), + ( + "777d8fbba990488bee5214092ef7eef9958190bc354cbe9111e3a7887b3fc03f", + "4770d4cc0c3db78c3fc7f0deb092f0e1155902da482e82ecafd630e64a783824", + ), + ( + "798a5261fc7eeeec6970914b171a2f9d1415aaf5e8979cba8dc1773ea286dad8", + "ef229f116182295ce6bd2c31591cb087fd2acf2d951d887e3ebb99b0d6774fc5", + ), + ( + "798a5261fc7eeeec6970914b171a2f9d1415aaf5e8979cba8dc1773ea286dad8", + "a4eb8c9e8e6972c7c8df8e94925516b938f69f349f15be5d63bd4192aedf2183", + ), + ( + "7e7444fe28ee1795d516c1d7f6ec36f069d0c6d7a0b6f9b1c46d240aacde2009", + "def2f2258bb490220fcbd55aa94b5f432a3768b5556da565035e7da6fc879226", + ), + ( + "7e7444fe28ee1795d516c1d7f6ec36f069d0c6d7a0b6f9b1c46d240aacde2009", + "a5c7bc18a7fabecb92ac536668753b68ce150d5770334e3372791d0b5dc710b0", + ), + ( + "7e7444fe28ee1795d516c1d7f6ec36f069d0c6d7a0b6f9b1c46d240aacde2009", + "ba807c181b2f28e122ce8363a50c7637de2637dbbb28a4487f70ed55c81cc051", + ), + ( + "844f0176ef176e220e1e46aacb8d078c84caf00ffaaa5700cd4de801c6537f06", + "54a5a9c9a5e5501a34584db98488319fd713ed38d3f2126e64ef28ef1b1ac784", + ), + ( + "844f0176ef176e220e1e46aacb8d078c84caf00ffaaa5700cd4de801c6537f06", + "6388f47e93d4a63a576ae8019aa0769a00d4cb24dcf33e970fc563bcf49be259", + ), + ( + "846ff6835153efa63ff1b279f252bb8085e64eec742c8eb70d9d55e9aa07d3f6", + "e7560bba6d6cf0007e769b159aaed9ab52ab5873b91e2410977630003b694bc3", + ), + ( + "846ff6835153efa63ff1b279f252bb8085e64eec742c8eb70d9d55e9aa07d3f6", + "5732ed78db5797117024f3ae182ddd67c3a71ab2512d0e919e5ef3c16c508ebc", + ), + ( + "9701eff5653457a571384df686ff5617ff96176e1e9a43900f93e0338776d726", + "5e5a04b3c4078c2bb1c3403897de7cbb8200d2ea34e2e2e1afb8d4db35a92694", + ), + ( + "9701eff5653457a571384df686ff5617ff96176e1e9a43900f93e0338776d726", + "428d2866ab85ffb4b8d72f8f0fc9837324c0159fb787af1bc5c06d24cd850c9f", + ), + ( + "9a568c8cbcb633ba8a22ba60fb61cca0b1fd49dbb3fa6b9842d0bf1971e843c8", + "6410fd312871aa4eeb4ac157555c42d26461017e702cff2e73bdb608f8aaf0d0", + ), + ( + "9a568c8cbcb633ba8a22ba60fb61cca0b1fd49dbb3fa6b9842d0bf1971e843c8", + "cca48f712e5dd8e4dccf7cbf84bf689a0188bedd0b9e86b0f9b279a3281c908d", + ), + ( + "9adb181ddfba2c92cac5b973dab5832423db256497c1ebcb67c7a96cad09b17e", + "66da71179d71c4bc215f76375576165383dfa4d23aed39b57299349aaf577370", + ), + ( + "9adb181ddfba2c92cac5b973dab5832423db256497c1ebcb67c7a96cad09b17e", + "eb3b5a9766cca9d209d61709359d611fc7a5950e156940f36aea92601f48d341", + ), + ( + "a4d0f09bfa96b840f19bff7556ba740b1ead5f643c4bedce483722b270a33a8e", + "111f3e556a61a21d1fba83c304ba469f1166c88345ed2e051386065aee379282", + ), + ( + "a4d0f09bfa96b840f19bff7556ba740b1ead5f643c4bedce483722b270a33a8e", + "791934d83144aa3b05ff970a4012b200ae2c7c735e0a7e9b9f66101474762e75", + ), + ( + "a5e0cf3d431db8b5ca7b2c061ea98d0a147b880e880f11c93c3905ae631223d1", + "6a331f0b65703151dddb65e0bf15098a56537ea8b3bf39cc5a888ad77c93d1ef", + ), + ( + "a5e0cf3d431db8b5ca7b2c061ea98d0a147b880e880f11c93c3905ae631223d1", + "d20512306ee5ab6d0db5accb86ed4d652fe9ea0cd0325f56457da10266ab4ec7", + ), + ( + "aac136e7a9d14793453e6d1a12b3da0439cb65eb138ea1915917d60a740bfb69", + "ab9c5054bc11f8de2a998bf620941828ddf31522c7b2fac38986a5fd2320934b", + ), + ( + "aac136e7a9d14793453e6d1a12b3da0439cb65eb138ea1915917d60a740bfb69", + "a161247fc07c501a5545d390ed37ce5b407b734d4585f25fd2f5c6eab3af2196", + ), + ( + "b2528cadf1ea052ade46ea2c8a2ad3048723acfde9ec5d27824439ae9e1569a8", + "f7919c9733bf9bac645795d1fce209eadf12786139360afc6d7b269be3f6a9bb", + ), + ( + "b2528cadf1ea052ade46ea2c8a2ad3048723acfde9ec5d27824439ae9e1569a8", + "bd31a23955363c024b3ee78ddec5e14cb76a63b16a8572db374f6d8e7f7f7ca7", + ), + ( + "b4208b470766dd1add3aaf177a53809aa0fc802990bb44edf6fac9b485902f93", + "b0a83413d1d30f064b39d1ade0f527bb7da06069886759a701123833986f652d", + ), + ( + "b4208b470766dd1add3aaf177a53809aa0fc802990bb44edf6fac9b485902f93", + "1cc32f89936ccef869a4699569e4c16f9336b3a82f238ae07a72717df1dd576d", + ), + ( + "b5acd98e163b3f98b0a176b87468000b77192799234a0f1ebb45fa74ddf601fc", + "842f8a3be62f8468472b44929b918b4f3b595c1a736092ff310f9c424b841a0b", + ), + ( + "b5acd98e163b3f98b0a176b87468000b77192799234a0f1ebb45fa74ddf601fc", + "6076f677198824f1ca7bdd433a0a39b90a0ffcdde8869569e4c17f97226de64f", + ), + ( + "b61edfe55212a2bd6e8027a575f76cb5e5d3d994a823abd941cad41622a3e61d", + "124cafbb0a42a71de3545795325c0b9500c755a97e76a1735a1a4bd715945897", + ), + ( + "b61edfe55212a2bd6e8027a575f76cb5e5d3d994a823abd941cad41622a3e61d", + "41bbaea99b54b9a1c8caf13e375ca6d58872ca17dec98285a055ececebcf5acb", + ), + ( + "b7d56366b4ae4fb321168c7945fa33323e50f8262cf385234cceb693b6b2a76d", + "65b5463c32f27e7f2916660b5fb94be1373e8a3747733f02e8fbea10511683da", + ), + ( + "b7d56366b4ae4fb321168c7945fa33323e50f8262cf385234cceb693b6b2a76d", + "adff00eb8297d905e4a17d47bcf47d489c5fc2fcc77654e6ac982e9d81b4b6e1", + ), + ( + "b81fd6b2aaf7553f9b7f93e774dd3972083b072bb53c004df4cc04f36a0d8523", + "c7b5452cd57068ffa67e2d0c05fab679b364820fba0bc6e774f4bdf1b62e0e89", + ), + ( + "b81fd6b2aaf7553f9b7f93e774dd3972083b072bb53c004df4cc04f36a0d8523", + "e396f9c25772a6e3f2b6b9ee4ac5ad975c874c3ffa62bcf0e58c9ac05f521900", + ), + ( + "bcc442c4aaf3e935bf1a0052a355e797ac27b8bd81cb4fa0f8abe6c41ae8a86d", + "1eff53b8db77a442eadb2570a38199367c48cd97b68387d848c51527aac31c0e", + ), + ( + "bcc442c4aaf3e935bf1a0052a355e797ac27b8bd81cb4fa0f8abe6c41ae8a86d", + "dc10457c0101a5f517a17997d2e92fa4fa052714fe78b2450a88a8540d8ac611", + ), + ( + "c22c4fc34582d13e3211a67fa6baa0b4d9d6495934301e2d5c28146b1a479bef", + "5a967c74a3ba5feaf3cde1369ce28c7faae2e1361447a25f29faf9f85e93c96e", + ), + ( + "c22c4fc34582d13e3211a67fa6baa0b4d9d6495934301e2d5c28146b1a479bef", + "178394ea8ab5e1d9d1bbb78544d32dcaf25baef728b2c82b695e5e20e963725d", + ), + ( + "c8efeb6a87b1346438316bdc323a98cda2eb3ae810fb7ce8b3e53b640d59d1c1", + "dd1690a70e5ac9c25b9ccaf9716a4db28a0f2f95f1e16f6ed8dfcf7b4e7023f6", + ), + ( + "c8efeb6a87b1346438316bdc323a98cda2eb3ae810fb7ce8b3e53b640d59d1c1", + "eaf7f613bef4b8bde170d15905d2b4193b01d1402d206b06a4426165d12661d8", + ), + ( + "c9069c6641c22ecca70a03f4479e43fd9a972cfc57f6235e9291aa64c8e0f0e8", + "124bc878c8421f9cc7edfffda88a68fc530873716a41f11d623b8b7b143b95cb", + ), + ( + "c9069c6641c22ecca70a03f4479e43fd9a972cfc57f6235e9291aa64c8e0f0e8", + "23d48e114f678fe1d1ee1b191dcc622f15e5ff3881f82343eea008549df213e1", + ), + ( + "cc8f31fc183925d7c7977c49ab343f1a09c7f0bbce845b5a78ecab521bb99a21", + "9794509b96f692a91aa1e3c385047a4f143e9a13660c7581b42569149cbf10f6", + ), + ( + "cc8f31fc183925d7c7977c49ab343f1a09c7f0bbce845b5a78ecab521bb99a21", + "b42f2c4b8b751af0af5ba9780dfc3f0139a77df14d1894ff0a7ae573abe2e8d9", + ), + ( + "d8ca721844b8ae7a767768696b8c5e3f5d3f8b19d069dec9f0c7d91158098487", + "20eb5a4d1b0824603374703b1c26ea4b01bbf19cdabe3dc48e5a29d00bfb6ce5", + ), + ( + "d8ca721844b8ae7a767768696b8c5e3f5d3f8b19d069dec9f0c7d91158098487", + "2b152ee50975c339175a54ce52d4b7d34c0d84ed6a13f6a5951d48fc6ed1bcd6", + ), + ( + "d8ca721844b8ae7a767768696b8c5e3f5d3f8b19d069dec9f0c7d91158098487", + "c4c071498b01ea6ba5cd608db79a823b281b921cc5ef54005dbef15a423042b8", + ), + ( + "daa34cf0fb5bc39112b2494a301b170ccd87832d10efc9243b1fb8c817c81044", + "2f5233d45c4da97ae018240bd29a3ef1f8406f7458a6e3cf520b054b6bcb4f7c", + ), + ( + "daa34cf0fb5bc39112b2494a301b170ccd87832d10efc9243b1fb8c817c81044", + "ac0d4dda19032e8b78c88fbebede308b6a847ea54dccb320306322a7741d3c6a", + ), + ( + "daa34cf0fb5bc39112b2494a301b170ccd87832d10efc9243b1fb8c817c81044", + "adcb4e7158ed853afe5b8f8b9b4c1c42e29a39af9b0bf98e182d15f0373b72bb", + ), + ( + "daa34cf0fb5bc39112b2494a301b170ccd87832d10efc9243b1fb8c817c81044", + "69421d277e28070408bfbe9d4b8cd1e85a44dd4ef2f490630b2bf15d50adfadf", + ), + ( + "daa34cf0fb5bc39112b2494a301b170ccd87832d10efc9243b1fb8c817c81044", + "a8e537178fe74fbeda0c9d5f5fb52536f09facc4ba99a87dc7cf761b9b5e9d29", + ), + ( + "dbca47b75bbc00d0f8e4b6168416a9c1cdfd1519a7d9b6e1dcee4b72f9b85da2", + "b4a1df8d362fdab21b6cbb2e8aba105c7dc884ced82fc74f353e69a4e99b13e4", + ), + ( + "dbca47b75bbc00d0f8e4b6168416a9c1cdfd1519a7d9b6e1dcee4b72f9b85da2", + "8b9f9eede8a1311183ffe12de383472ebc0b28785f87d90996fc29a732c78a0a", + ), + ( + "dc24fc48e7dc473a8531d9033d61666175c2fa8f47ebbe06c8ee4e3c835503be", + "61adec85dc1782f5718663e347a36e7d953337113a9b7b599af6cc5d27384699", + ), + ( + "dc24fc48e7dc473a8531d9033d61666175c2fa8f47ebbe06c8ee4e3c835503be", + "033234e75fd174df903f2caed46741f20048057b9c0de7fefc2a64f8d3f3f5b7", + ), + ( + "e0276f6188f705c611b362950d8dbdf6763a83c3aa81e95ff1450486301ddd8a", + "4edd0eff698314492c2ac3730d16c3662b3738fa53d71eaa610e4fae2bc4767f", + ), + ( + "e0276f6188f705c611b362950d8dbdf6763a83c3aa81e95ff1450486301ddd8a", + "dac50924df85a3d94bb01bcc1f258065c4e74d1f38c267ace4496316e76c6f18", + ), + ( + "e3d835dc468032a4450b596138cbff37d3888f95c1c9868202a1b4da75698062", + "72c6e3a4b8793ac095dc03551380507e0341ed86f2a9fc4b5547defbfe98cae7", + ), + ( + "e3d835dc468032a4450b596138cbff37d3888f95c1c9868202a1b4da75698062", + "04033fa4cc59feaa3e187343d42484ef6053ea0a4fc6ac74103e9e26c44d4c6d", + ), + ( + "e4c8ef61fa3fd2732d6587ac6c67c17f887f0a0372318e5fa05e6421627f0b3e", + "baf955cc116b3bb3e6fd77dcf1264fa690bd2a3f83526574f5eeb4f6100e2fd6", + ), + ( + "e4c8ef61fa3fd2732d6587ac6c67c17f887f0a0372318e5fa05e6421627f0b3e", + "9c825788eb889d6c8e39057d2253b23b350c27a94130b112b8d500e48e758689", + ), + ( + "e4ee744a0efb8818b821536252656863387b6c564b8153a6339c0f77570105b5", + "351515f07f58d7e6b1e1f64996be80b91ca76d592d5053a467025a1ccfdea0d6", + ), + ( + "e4ee744a0efb8818b821536252656863387b6c564b8153a6339c0f77570105b5", + "014618bec2565496cfbbc3cda3ad4cd8740b08c8cac11a6e2f6775a1e50f0175", + ), + ( + "e4ee744a0efb8818b821536252656863387b6c564b8153a6339c0f77570105b5", + "515837eb87234faaf62b85e997f00d9fd6cf47fc4491e5883c6cdfe8f8c90a7a", + ), + ( + "e93063adbf76004c7512d2a790ec17800809e73c3450f188fae2583d6526b493", + "1f9fc1a291c71a5bbd91a411ff7f7a8bee0676b62aea64b994ae72955fa34586", + ), + ( + "e93063adbf76004c7512d2a790ec17800809e73c3450f188fae2583d6526b493", + "49ad862d912b00a88d45ca591a0545b247b913373568120ed505a08cc586c3ba", + ), + ( + "ffd4043495ce946112233b94b4cca5f330be6cea693e1933dc032abfe0ab628d", + "ad86c7c280ddc9a7af6bfcb83b230d9c8d810ccb90d01c60a8341b5f7ea1bedb", + ), + ( + "ffd4043495ce946112233b94b4cca5f330be6cea693e1933dc032abfe0ab628d", + "94506b8b0c29e8cbd9f7702e0593b002fb99c900d55216ba76cd15bc71c4a1ab", + ), ]; diff --git a/src/main.rs b/src/main.rs index 58eec56..10dd299 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,17 @@ -use bitcoin_network::{ - block::Block, get_blocks::GetBlocks, get_data::GetData, message::Message, -}; +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; +pub mod configs; pub mod database; pub mod duplicate; pub mod networks; pub mod peer; pub mod utils; -pub mod configs; -use crate::database::{save_blocks, finish}; +use crate::database::{finish, save_blocks}; use crate::peer::Peer; #[macro_use] @@ -41,8 +39,11 @@ fn main() { ********************/ let mut message_rcv: Message; - let mut peer = Peer::new(format!("{},{}", config.peer.ip, config.peer.port), network.magic_bytes); - let mut current_height: u32 = 0; + let mut peer = Peer::new( + format!("{},{}", config.peer.ip, config.peer.port), + network.magic_bytes, + ); + let mut current_height: u32 = 0; let mut hash = network.genesis_hash.to_vec(); hash.reverse(); @@ -62,25 +63,29 @@ fn main() { config.database.dbname ); - let mut postgres_client = postgres::Client::connect( - &database_params, - postgres::NoTls, - ) - .unwrap(); + let mut postgres_client = postgres::Client::connect(&database_params, postgres::NoTls).unwrap(); // create the tables if they don't exist database::create_tables(&network_arg, &mut postgres_client); - let result = postgres_client.query(format!("SELECT * FROM {0}.blocks a JOIN (SELECT MAX(height) as h FROM {0}.blocks) b ON a.height = b.h;", network_arg).as_str(), &[]).unwrap(); + let result = postgres_client.query(format!("SELECT * FROM {0}.blocks a JOIN (SELECT MAX(height) as h FROM {0}.blocks) b ON a.height = b.h;", network_arg).as_str(), &[]).unwrap(); if result.len() > 0 { let row = &result[0]; current_height = row.get(0); hash = row.get(1); - info!("We have found a new hash {} and height {}", hex::encode(&hash), current_height); + info!( + "We have found a new hash {} and height {}", + hex::encode(&hash), + current_height + ); } - info!("We have found a new hash {} and height {}", hex::encode(&hash), current_height); + info!( + "We have found a new hash {} and height {}", + hex::encode(&hash), + current_height + ); /******************** * @@ -96,17 +101,19 @@ fn main() { let mut process_current_height = current_height; // Connect to database - let mut postgres_client = postgres::Client::connect( - &database_params, - postgres::NoTls, - ) - .unwrap(); + let mut postgres_client = + postgres::Client::connect(&database_params, postgres::NoTls).unwrap(); // while recv save blocks in database loop { let blocks: Vec = rx.recv().unwrap(); - save_blocks(blocks, &network_arg, &mut postgres_client, &mut process_current_height); + save_blocks( + blocks, + &network_arg, + &mut postgres_client, + &mut process_current_height, + ); // We are synced if process_current_height > height { @@ -143,8 +150,10 @@ fn main() { // Verify if inventory is what we asked for let verified = utils::verify_inv_identifier(blocks_inv.inventory); - if !verified { warn!("One of the inventory record is not a block message. We might have a problem.") } - + if !verified { + warn!("One of the inventory record is not a block message. We might have a problem.") + } + if blocks_inv.count > 1 { // For now ignore notify inv message break; @@ -191,7 +200,6 @@ fn main() { let blocks = utils::check_chain(&mut blocks, hash.clone()).unwrap(); // Looks into the database for the previous hash. If the previous hash exist we have a fork - current_height += blocks.len() as u32; info!("Progress {}/{}", current_height, height); diff --git a/src/networks.rs b/src/networks.rs index f4c1bd9..bc47ecb 100644 --- a/src/networks.rs +++ b/src/networks.rs @@ -13,7 +13,10 @@ impl Network { testnet: false, magic_bytes: MagicBytes::BITCOIN_MAINNET, aux_pow: false, - genesis_hash: [0,0,0,0,0,25,214,104,156,8,90,225,101,131,30,147,79,247,99,174,70,162,166,193,114,179,241,182,10,140,226,111], + genesis_hash: [ + 0, 0, 0, 0, 0, 25, 214, 104, 156, 8, 90, 225, 101, 131, 30, 147, 79, 247, 99, 174, 70, + 162, 166, 193, 114, 179, 241, 182, 10, 140, 226, 111, + ], }; pub const BITCOIN_TESTNET: Network = Network { @@ -21,42 +24,60 @@ impl Network { // FIXME: incorrect bitcoin testnet/regtest value in magic byte lib magic_bytes: MagicBytes::BITCOIN_REGTEST, aux_pow: false, - genesis_hash: [0, 0, 0, 0, 9, 51, 234, 1, 173, 14, 233, 132, 32, 151, 121, 186, 174, 195, 206, 217, 15, 163, 244, 8, 113, 149, 38, 248, 215, 127, 73, 67], + genesis_hash: [ + 0, 0, 0, 0, 9, 51, 234, 1, 173, 14, 233, 132, 32, 151, 121, 186, 174, 195, 206, 217, + 15, 163, 244, 8, 113, 149, 38, 248, 215, 127, 73, 67, + ], }; pub const LITECOIN_MAINNET: Network = Network { testnet: false, magic_bytes: MagicBytes::LITECOIN_MAINNET, aux_pow: false, - genesis_hash: [18, 167, 101, 227, 31, 253, 64, 89, 186, 218, 30, 37, 25, 15, 110, 152, 201, 157, 151, 20, 211, 52, 239, 164, 26, 25, 90, 126, 126, 4, 191, 226], + genesis_hash: [ + 18, 167, 101, 227, 31, 253, 64, 89, 186, 218, 30, 37, 25, 15, 110, 152, 201, 157, 151, + 20, 211, 52, 239, 164, 26, 25, 90, 126, 126, 4, 191, 226, + ], }; pub const LITECOIN_TESTNET: Network = Network { testnet: true, magic_bytes: MagicBytes::LITECOIN_TESTNET, aux_pow: false, - genesis_hash: [73, 102, 98, 90, 75, 40, 81, 217, 253, 238, 19, 158, 86, 33, 26, 13, 136, 87, 95, 89, 237, 129, 111, 245, 230, 166, 61, 235, 78, 62, 41, 160], + genesis_hash: [ + 73, 102, 98, 90, 75, 40, 81, 217, 253, 238, 19, 158, 86, 33, 26, 13, 136, 87, 95, 89, + 237, 129, 111, 245, 230, 166, 61, 235, 78, 62, 41, 160, + ], }; pub const NAMECOIN_MAINNET: Network = Network { testnet: false, magic_bytes: MagicBytes::NAMECOIN_MAINNET, aux_pow: true, - genesis_hash: [0, 0, 0, 0, 0, 98, 183, 44, 94, 44, 235, 69, 251, 200, 88, 126, 128, 124, 21, 91, 13, 167, 53, 230, 72, 61, 251, 162, 240, 169, 199, 112], + genesis_hash: [ + 0, 0, 0, 0, 0, 98, 183, 44, 94, 44, 235, 69, 251, 200, 88, 126, 128, 124, 21, 91, 13, + 167, 53, 230, 72, 61, 251, 162, 240, 169, 199, 112, + ], }; pub const DOGECOIN_MAINNET: Network = Network { testnet: false, magic_bytes: MagicBytes::DOGECOIN_MAINNET, aux_pow: true, - genesis_hash: [26, 145, 227, 218, 206, 54, 226, 190, 59, 240, 48, 166, 86, 121, 254, 130, 26, 161, 214, 239, 146, 231, 201, 144, 46, 179, 24, 24, 44, 53, 86, 145], + genesis_hash: [ + 26, 145, 227, 218, 206, 54, 226, 190, 59, 240, 48, 166, 86, 121, 254, 130, 26, 161, + 214, 239, 146, 231, 201, 144, 46, 179, 24, 24, 44, 53, 86, 145, + ], }; pub const DOGECOIN_TESTNET: Network = Network { testnet: true, magic_bytes: MagicBytes::DOGECOIN_TESTNET, aux_pow: true, - genesis_hash: [187, 10, 120, 38, 70, 55, 64, 107, 99, 96, 170, 217, 38, 40, 77, 84, 77, 112, 73, 244, 81, 137, 219, 86, 100, 243, 196, 208, 115, 80, 85, 158], + genesis_hash: [ + 187, 10, 120, 38, 70, 55, 64, 107, 99, 96, 170, 217, 38, 40, 77, 84, 77, 112, 73, 244, + 81, 137, 219, 86, 100, 243, 196, 208, 115, 80, 85, 158, + ], }; pub fn find(network: &str) -> Result> { @@ -71,4 +92,4 @@ impl Network { _ => Err("not matching available networks.".into()), } } -} \ No newline at end of file +} diff --git a/src/peer.rs b/src/peer.rs index 4e8e239..1df3f39 100644 --- a/src/peer.rs +++ b/src/peer.rs @@ -1,11 +1,11 @@ use bitcoin_network::message::Message; use bitcoin_network::version::Version; -use std::net::TcpStream; -use std::sync::mpsc::channel; use std::error::Error; use std::io::prelude::*; -use std::thread; +use std::net::TcpStream; +use std::sync::mpsc::channel; use std::sync::mpsc::{Receiver, Sender}; +use std::thread; use std::time::Duration; use crate::utils; @@ -35,7 +35,6 @@ impl Peer { peer.start_thread(); return peer; - } pub fn start_thread(&mut self) -> thread::JoinHandle<()> { @@ -51,18 +50,20 @@ impl Peer { let mut buf = [0; 24]; // we have this loop to be sure we have received at least 24 bytes - while stream.peek(&mut buf).unwrap() != 24 { thread::sleep(Duration::from_millis(50)); } + while stream.peek(&mut buf).unwrap() != 24 { + thread::sleep(Duration::from_millis(50)); + } stream.read_exact(&mut buf).unwrap(); data.extend(&buf); - + let payload_size = u32::from_le_bytes(buf[16..20].try_into().unwrap()) as usize; let _command = String::from_utf8(buf[4..16].to_vec()).unwrap(); // Message payload - let mut payload : Vec = vec![]; + let mut payload: Vec = vec![]; // we have this loop to be sure we have received the complete payload - while payload.len() < payload_size { + while payload.len() < payload_size { let mut buf: Vec = vec![0; payload_size - payload.len()]; let l = stream.read(&mut buf).unwrap(); @@ -70,24 +71,19 @@ impl Peer { thread::sleep(Duration::from_millis(500)); } data.extend(payload); - + let message = Message::deserialize(&data).unwrap(); // answer ping message and don't add it to the queue if message.command.starts_with("ping") { trace!("'ping' received"); - let pong = Message::new( - magic_bytes, - "pong".to_owned(), - message.payload.clone(), - ); - stream - .write(&pong.serialize()) - .unwrap(); + let pong = + Message::new(magic_bytes, "pong".to_owned(), message.payload.clone()); + stream.write(&pong.serialize()).unwrap(); stream.flush().unwrap(); trace!("'pong' sent"); - + continue; } @@ -108,8 +104,7 @@ impl Peer { info!("'{}' received", message_rcv.command); // We need to know the node current block height - let version = Version::deserialize(&message_rcv.payload) - .unwrap(); + let version = Version::deserialize(&message_rcv.payload).unwrap(); info!("Current block height: {:?}", version.start_height); // verack diff --git a/src/utils.rs b/src/utils.rs index b39b8a2..d44429a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,17 +1,19 @@ +use bitcoin_network::{address::Address, version::Version}; use bitcoin_network::{block::Block, inventory::Inventory}; use std::error::Error; -use bitcoin_network::{address::Address, version::Version}; use std::time::{SystemTime, UNIX_EPOCH}; pub fn check_chain(blocks: &mut Vec, hash: Vec) -> Result, Box> { - let mut sorted_blocks : Vec = Vec::new(); + let mut sorted_blocks: Vec = Vec::new(); match blocks.iter().position(|b| hash == b.previous_hash) { Some(block_index) => { let block = blocks.remove(block_index); sorted_blocks.push(block); - }, - None => { return Ok(sorted_blocks); } + } + None => { + return Ok(sorted_blocks); + } } while blocks.len() > 0 { @@ -20,8 +22,8 @@ pub fn check_chain(blocks: &mut Vec, hash: Vec) -> Result, Some(block_index) => { let block = blocks.remove(block_index); sorted_blocks.push(block); - }, - None => break, + } + None => break, } } Ok(sorted_blocks) @@ -33,8 +35,25 @@ pub fn create_version(host: &str) -> Version { let _port: u16 = split[1].parse().unwrap(); // FIXME: Only support ipv4 - let ip = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, ip_string[0].parse::().unwrap(), ip_string[1].parse::().unwrap(), ip_string[2].parse::().unwrap(), ip_string[3].parse::().unwrap()]; - + let ip = [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 255, + 255, + ip_string[0].parse::().unwrap(), + ip_string[1].parse::().unwrap(), + ip_string[2].parse::().unwrap(), + ip_string[3].parse::().unwrap(), + ]; + Version { version: 70012, services: 4, @@ -46,7 +65,7 @@ pub fn create_version(host: &str) -> Version { services: 1, // FIXME: should be receiver address ip, - port:0, + port: 0, }, addr_trans: Address { services: 4, @@ -62,7 +81,11 @@ pub fn create_version(host: &str) -> Version { pub fn verify_inv_identifier(inventories: Vec) -> bool { let mut result = true; - inventories.iter().for_each(|inv| { if inv.identifier != 2 { result = false; }}); + inventories.iter().for_each(|inv| { + if inv.identifier != 2 { + result = false; + } + }); return result; -} \ No newline at end of file +}