Implement the mining software

This commit is contained in:
2024-03-24 13:09:29 +01:00
parent 2f3216a0c4
commit 825b07bc11
7 changed files with 608 additions and 7 deletions

View File

@@ -158,7 +158,7 @@ A "block transfer" message is sent back in response to a "block request" message
| protocol version = 0 (BE) | 2 | | protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 | | capable version = 0 (BE) | 2 |
| type = 7 (BE) | 1 | | type = 7 (BE) | 1 |
| padding (nullbytes) | 220 | | padding (nullbytes) | 252 |
The node should answer to a "Mining task request" with a "Mining task response" The node should answer to a "Mining task request" with a "Mining task response"
@@ -173,6 +173,7 @@ The node should answer to a "Mining task request" with a "Mining task response"
| previous hash | 32 | | previous hash | 32 |
| timestamp (unix time in seconds, BE) | 8 | | timestamp (unix time in seconds, BE) | 8 |
| difficulty sum (BE) | 32 | | difficulty sum (BE) | 32 |
| threshold | 32 |
The node tells the miner "timestamp", "previous hash", "difficulty sum" and "transaction" for the new block. The node tells the miner "timestamp", "previous hash", "difficulty sum" and "transaction" for the new block.

View File

@@ -23,7 +23,7 @@ def main():
if len(block) != 292: if len(block) != 292:
break break
timestamp = int.from_bytes(block[244:252], "big") timestamp = int.from_bytes(block[244:252], "big")
time_info = time.strftime("%d.%m.%Y %H:%M.%S", time.localtime(timestamp)) time_info = time.strftime("%d.%m.%Y %H:%M:%S", time.localtime(timestamp))
message = prepare_message(block[148:180]) message = prepare_message(block[148:180])
print(f"[{time_info}] {message}") print(f"[{time_info}] {message}")
except FileNotFoundError: except FileNotFoundError:

1
miner/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

385
miner/Cargo.lock generated Normal file
View File

@@ -0,0 +1,385 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anstream"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
[[package]]
name = "anstyle-parse"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "base64"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "carrotcoin_miner"
version = "0.1.0"
dependencies = [
"base64",
"clap",
"num_cpus",
"rand",
"sha2",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "cpufeatures"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "libc"
version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "strsim"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
[[package]]
name = "syn"
version = "2.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
[[package]]
name = "windows_i686_gnu"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
[[package]]
name = "windows_i686_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"

13
miner/Cargo.toml Normal file
View File

@@ -0,0 +1,13 @@
[package]
name = "carrotcoin_miner"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
base64 = "0.22.0"
clap = { version = "4.5.3", features = ["derive"] }
num_cpus = "1.16.0"
rand = "0.8.5"
sha2 = "0.10.8"

198
miner/src/main.rs Normal file
View File

@@ -0,0 +1,198 @@
use base64::prelude::*;
use clap::Parser;
use rand::Rng;
use sha2::Digest;
use sha2::Sha256;
use std::io::ErrorKind;
use std::net::Ipv6Addr;
use std::net::UdpSocket;
use std::sync::mpsc;
use std::sync::mpsc::TryRecvError;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use std::time::Duration;
use std::time::Instant;
/// Carrotcoin miner
#[derive(Parser)]
struct Args {
/// Wallet address for the mining reward
#[arg(short, long)]
wallet: String,
/// Message to encode in each block (maximum 32 bytes)
#[arg(short, long)]
message: Option<String>,
/// Number of threads to start
#[arg(short, long)]
thread_count: Option<usize>,
}
struct Task {
prefix: Vec<u8>,
threshold: [u8; 32],
work_counter: usize,
}
fn run_mining_worker(task: Arc<Mutex<Task>>, results: mpsc::Sender<Vec<u8>>) {
let mut rng = rand::thread_rng();
loop {
let mut hasher = Sha256::new();
let mut lock_guard = task.lock().unwrap();
let prefix = lock_guard.prefix.clone();
hasher.update(&prefix);
let threshold = lock_guard.threshold.clone();
lock_guard.work_counter += 1;
drop(lock_guard);
let start = rng.gen_range(0..((1u64 << 50) - 1)) << 14;
for nonce in start..(start + (1 << 14)) {
let mut hasher_cloned = hasher.clone();
let nonce_bytes = nonce.to_be_bytes();
hasher_cloned.update(nonce_bytes);
let result_hash: [u8; 32] = hasher_cloned.finalize().into();
if result_hash[0] == 0 && result_hash <= threshold {
let mut block = Vec::with_capacity(292);
block.extend_from_slice(&prefix);
block.extend_from_slice(&nonce_bytes);
results.send(block).unwrap();
}
}
}
}
fn start_threads(task: &Arc<Mutex<Task>>, results: &mpsc::Sender<Vec<u8>>, thread_count: usize) {
for _ in 0..thread_count {
let task_cloned = task.clone();
let results_cloned = results.clone();
thread::spawn(|| run_mining_worker(task_cloned, results_cloned));
}
}
fn build_message(message_string: &str) -> Option<Vec<u8>> {
let message_bytes = message_string.as_bytes();
if message_bytes.len() > 32 {
return None;
}
let mut message_buffer = Vec::with_capacity(32);
message_buffer.extend_from_slice(&message_bytes);
message_buffer.resize(32, 0);
Some(message_buffer)
}
fn show_statistics(duration: Duration, work_counter: usize, threshold: &[u8]) {
let duration_seconds = duration.as_secs_f64();
let mut hash_numeric = 0f64;
for byte_value in threshold {
hash_numeric = hash_numeric * 256.0 + f64::from(*byte_value);
}
let difficulty = 2.0f64.powi(256) / hash_numeric;
let relative_difficulty = difficulty / 2.0f64.powi(28);
let hashrate = (work_counter as f64) / duration_seconds * 2.0f64.powi(14);
let hashrate_mhs = hashrate / 1e6;
let expected_reward = hashrate * 3600f64 / difficulty;
println!("difficulty: {relative_difficulty:.02}; hashrate: {hashrate_mhs:.02} Mh/s; expected reward: {expected_reward:.04} cc / hour");
}
fn main() {
let args = Args::parse();
let public_key = match BASE64_STANDARD.decode(args.wallet) {
Ok(key_bytes) if key_bytes.len() == 32 => key_bytes,
Ok(_) => {
eprintln!("The wallet address is not exactly 32 bytes, so it is invalid.");
return;
}
Err(_) => {
eprintln!("Failed to base64-decode the wallet address.");
return;
}
};
let message = match build_message(args.message.as_deref().unwrap_or("")) {
Some(message) => message,
None => {
eprintln!("The provided block message ist too long. Must be at most 32 bytes.");
return;
}
};
let thread_count = args.thread_count.unwrap_or_else(num_cpus::get);
let mut last_statistic_time = Instant::now();
let mut statistic_output_delay = Duration::from_secs(5);
let socket = UdpSocket::bind((Ipv6Addr::UNSPECIFIED, 0)).unwrap();
let mut threads_started = false;
socket.connect((Ipv6Addr::LOCALHOST, 62039)).unwrap();
socket.set_nonblocking(true).unwrap();
let task = Arc::new(Mutex::new(Task {
prefix: Vec::new(),
threshold: [0u8; 32],
work_counter: 0,
}));
let (results_tx, results_rx) = mpsc::channel();
let mut request = vec![0u8; 257];
request[4] = 7;
loop {
let mut recv_buffer = vec![0u8; 512];
loop {
match socket.recv(&mut recv_buffer) {
Ok(size) => {
if size == 257 && &recv_buffer[0..5] == &[0, 0, 0, 0, 8] {
let transaction = &recv_buffer[5..153];
let previous_hash = &recv_buffer[153..185];
let timestamp = &recv_buffer[185..193];
let difficulty_sum = &recv_buffer[193..225];
let threshold = &recv_buffer[225..257];
let mut prefix = Vec::with_capacity(292);
prefix.extend_from_slice(transaction);
prefix.extend_from_slice(&message);
prefix.extend_from_slice(&public_key);
prefix.extend_from_slice(previous_hash);
prefix.extend_from_slice(timestamp);
prefix.extend_from_slice(difficulty_sum);
let mut lock = task.lock().unwrap();
lock.prefix = prefix;
lock.threshold = threshold.try_into().unwrap();
let current_time = Instant::now();
let duration = current_time - last_statistic_time;
if duration >= statistic_output_delay {
show_statistics(duration, lock.work_counter, threshold);
last_statistic_time = current_time;
lock.work_counter = 0;
statistic_output_delay = Duration::from_secs(60);
}
if !threads_started {
start_threads(&task, &results_tx, thread_count);
threads_started = true;
}
}
}
Err(e) => {
if e.kind() == ErrorKind::WouldBlock {
break;
} else if e.kind() == ErrorKind::ConnectionRefused {
eprintln!("Node is not running locally!");
return;
} else {
panic!("{e}");
}
}
}
}
socket.send(&request).unwrap();
std::thread::sleep(std::time::Duration::from_secs(1));
let mined_block = match results_rx.try_recv() {
Ok(block) => Some(block),
Err(e) if e == TryRecvError::Empty => None,
Err(e) => panic!("{e}"),
};
if let Some(block) = mined_block {
let mut block_transfer = Vec::with_capacity(297);
block_transfer.extend_from_slice(b"\0\0\0\0\x02");
block_transfer.extend_from_slice(&block);
socket.send(&block_transfer).unwrap();
println!("I found a carrot! \u{1f955}");
}
}
}

13
node.py
View File

@@ -362,8 +362,8 @@ def receiver(node, b):
receive_observer.publish(identifier, event_obj) receive_observer.publish(identifier, event_obj)
elif msg_type == 7: elif msg_type == 7:
# mining task request # mining task request
if msg_len != 225: if msg_len != 257:
log(f"Got a mining task request of wrong length ({msg_len} bytes from {sender}, but expected 255 bytes)") log(f"Got a mining task request of wrong length ({msg_len} bytes from {sender}, but expected 257 bytes)")
continue continue
transaction = b.open_transactions.get_transaction(0) transaction = b.open_transactions.get_transaction(0)
if transaction is not None: if transaction is not None:
@@ -374,8 +374,8 @@ def receiver(node, b):
timestamp_raw = t.to_bytes(8, "big") timestamp_raw = t.to_bytes(8, "big")
latest_block = b.get_latest_block() latest_block = b.get_latest_block()
if latest_block is not None: if latest_block is not None:
B_1_difficulty_sum, _ = latest_block.get_difficulty_info(0, blockchain) B_1_difficulty_sum, _ = latest_block.get_difficulty_info(0, b)
B_10_difficulty_sum, B_10_timestamp = latest_block.get_difficulty_info(9, blockchain) B_10_difficulty_sum, B_10_timestamp = latest_block.get_difficulty_info(9, b)
D = B_1_difficulty_sum - B_10_difficulty_sum D = B_1_difficulty_sum - B_10_difficulty_sum
T = t - B_10_timestamp T = t - B_10_timestamp
calculated_difficulty = D * 3000 // 9 // T calculated_difficulty = D * 3000 // 9 // T
@@ -383,13 +383,16 @@ def receiver(node, b):
difficulty_sum = B_1_difficulty_sum + block_difficulty difficulty_sum = B_1_difficulty_sum + block_difficulty
previous_hash = latest_block.own_hash previous_hash = latest_block.own_hash
else: else:
block_difficulty = 2**28
difficulty_sum = 2**29 difficulty_sum = 2**29
previous_hash = 32 * b"\0" previous_hash = 32 * b"\0"
threshold = (2**256 - 1) // block_difficulty
response = b"\0\0\0\0\x08" + \ response = b"\0\0\0\0\x08" + \
transaction_raw + \ transaction_raw + \
previous_hash + \ previous_hash + \
timestamp_raw + \ timestamp_raw + \
difficulty_sum.to_bytes(32, "big") difficulty_sum.to_bytes(32, "big") + \
threshold.to_bytes(32, "big")
node.node_socket.sendto(response, addr) node.node_socket.sendto(response, addr)
elif msg_type == 9: elif msg_type == 9:
# payment request # payment request