use k256::{PublicKey, ecdsa::{SigningKey, VerifyingKey}};
pub fn keys_from_str(priv_key: &str) -> (SigningKey, VerifyingKey) {
let signing_key = SigningKey::from_bytes(hex::decode(priv_key).unwrap().as_slice().into()).unwrap();
let verifying_key = VerifyingKey::from(signing_key.clone());
(signing_key, verifying_key)
}
use coin::block::*;
use coin::tx::*;
use coin::user::*;
use k256::ecdsa::Signature;
use std::time::{SystemTime, UNIX_EPOCH};
use coin::block::*;
use coin::tx::*;
use coin::user::*;
use std::fs;
#[cfg(test)]
mod tests {
use super::*;
fn genesis_keys() -> (SigningKey, VerifyingKey) {
let priv_key = fs::read_to_string("private_key.txt")
.expect("private_key.txt needed for testing");
keys_from_str(&priv_key.trim())
}
#[test]
fn genesis_block_initialization() {
let genesis = Block::genesis_block();
let state = State::with_genesis_block();
let (_, genesis_pub) = genesis_keys();
let genesis_pub = PublicKey::from(genesis_pub);
let genesis_out = &state.utxo_set.values().next().unwrap();
assert_eq!(genesis_out.recipient, genesis_pub);
}
#[test]
fn valid_transaction_flow() {
let mut state = State::with_genesis_block();
let (sender_priv, sender_pub) = genesis_keys();
let recipient = User::random();
let mut new_block = Block::new();
new_block.prev_hash = state.blocks[0].get_hash();
new_block
.transact(
&mut state.utxo_set,
&sender_priv,
&recipient.verifying,
50,
)
.expect("Failed to create transaction");
new_block.nonce = new_block.mine();
state.add_block_if_valid(new_block.clone())
.expect("Block should be valid");
let genesis_balance = state.get_balance(sender_pub.into());
let recipient_balance = state.get_balance(recipient.verifying.into());
assert_eq!(genesis_balance, Block::START_SUPPLY - 50);
assert_eq!(recipient_balance, 50);
}
#[test]
fn invalid_transaction_overspend() {
let mut state = State::with_genesis_block();
let (sender_priv, _) = genesis_keys();
let recipient = User::random();
let mut new_block = Block::new();
let result = new_block.transact(
&mut state.utxo_set,
&sender_priv,
&recipient.verifying,
Block::START_SUPPLY + 1,
);
assert!(result.is_err(), "Should reject overspending");
}
#[test]
fn utxo_set_update() {
let mut state = State::with_genesis_block();
let (sender_priv, _) = genesis_keys();
let genesis_outpoint = Outpoint(state.blocks[0].txs[0].txid, 0);
let mut new_block = Block::new();
new_block.prev_hash = state.blocks[0].get_hash();
new_block
.transact(&mut state.utxo_set, &sender_priv, &sender_priv.verifying_key(), 10)
.unwrap();
new_block.nonce = new_block.mine();
state.add_block_if_valid(new_block).unwrap();
assert!(!state.utxo_set.contains_key(&genesis_outpoint));
}
#[test]
fn block_mining_and_verification() {
let mut block = Block::new();
block.prev_hash = BLANK_BLOCK_HASH;
block.time_stamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
block.nonce = block.mine();
assert!(
block.verify_work(),
"Mined block should pass work verification"
);
}
#[test]
fn block_rejection_bad_nonce() {
let mut state = State::with_genesis_block();
let mut invalid_block = Block::new();
invalid_block.prev_hash = state.blocks[0].get_hash();
invalid_block.nonce = 12345;
assert!(
state.add_block_if_valid(invalid_block).is_err(),
"Block with invalid nonce should be rejected"
);
}
#[test]
fn block_timestamp_validation() {
let mut state = State::with_genesis_block();
let (sender_priv, _) = genesis_keys();
state.blocks[0].time_stamp = 1000;
let mut block1 = Block::new();
block1.prev_hash = state.blocks[0].get_hash();
block1.time_stamp = 2000;
block1.nonce = block1.mine();
state.add_block_if_valid(block1).unwrap();
let mut block2 = Block::new();
block2.prev_hash = state.blocks.last().unwrap().get_hash();
block2.time_stamp = 3000;
block2.nonce = block2.mine();
state.add_block_if_valid(block2).unwrap();
let min_timestamp = state.median_time_stamp(None); let mut max_timestamp = 3000 + 2*60*60;
let mut bad_block_low = Block::new();
bad_block_low.prev_hash = state.blocks.last().unwrap().get_hash();
bad_block_low.time_stamp = min_timestamp - 1;
bad_block_low.nonce = bad_block_low.mine();
assert!(
state.add_block_if_valid(bad_block_low).is_err(),
"Should reject block below median timestamp"
);
let mut good_block_min = Block::new();
good_block_min.prev_hash = state.blocks.last().unwrap().get_hash();
good_block_min.time_stamp = min_timestamp;
good_block_min.nonce = good_block_min.mine();
assert!(
state.add_block_if_valid(good_block_min).is_ok(),
"Should accept block at exact median"
);
max_timestamp = state.blocks.last().unwrap().time_stamp + 2*60*60;
}
}