Commit 8d286eff authored by wgr523's avatar wgr523
Browse files

add code

parent 72a5c3bc
/target
**/*.rs.bk
.idea
.DS_Store
This diff is collapsed.
[package]
name = "bitcoin"
version = "0.1.0"
authors = []
edition = "2018"
[dependencies]
ring = "0.16"
bincode = "1.2"
serde = { version = "1.0", features = ["derive"] }
hex = "0.4"
log = "0.4"
stderrlog = "0.4"
mio = "0.6"
slab = "0.4"
mio-extras = "2.0"
serde_json = "1.0"
tiny_http = "0.6"
url = "2.1"
crossbeam = "0.7"
rand = "0.6"
hex-literal = "0.2"
clap = { version = "2.33", features = ["wrap_help"]}
[features]
default = []
test-utilities = []
MIT License
Copyright (c) 2019 Prism Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
......@@ -8,6 +8,7 @@ We use Piazza for discussion.
## Assignments
- [Assignment 1](Assignment1). Due date 12:30PM, Jan 28, 2020.
- [Assignment 2](Assignment2). Due date 12:30PM, Feb 6, 2020.
## Midterm Project
......
use serde::Serialize;
use crate::miner::Handle as MinerHandle;
use crate::network::server::Handle as NetworkServerHandle;
use crate::network::message::Message;
use log::info;
use std::collections::HashMap;
use std::thread;
use tiny_http::Header;
use tiny_http::Response;
use tiny_http::Server as HTTPServer;
use url::Url;
pub struct Server {
handle: HTTPServer,
miner: MinerHandle,
network: NetworkServerHandle,
}
#[derive(Serialize)]
struct ApiResponse {
success: bool,
message: String,
}
macro_rules! respond_result {
( $req:expr, $success:expr, $message:expr ) => {{
let content_type = "Content-Type: application/json".parse::<Header>().unwrap();
let payload = ApiResponse {
success: $success,
message: $message.to_string(),
};
let resp = Response::from_string(serde_json::to_string_pretty(&payload).unwrap())
.with_header(content_type);
$req.respond(resp).unwrap();
}};
}
impl Server {
pub fn start(
addr: std::net::SocketAddr,
miner: &MinerHandle,
network: &NetworkServerHandle,
) {
let handle = HTTPServer::http(&addr).unwrap();
let server = Self {
handle,
miner: miner.clone(),
network: network.clone(),
};
thread::spawn(move || {
for req in server.handle.incoming_requests() {
let miner = server.miner.clone();
let network = server.network.clone();
thread::spawn(move || {
// a valid url requires a base
let base_url = Url::parse(&format!("http://{}/", &addr)).unwrap();
let url = match base_url.join(req.url()) {
Ok(u) => u,
Err(e) => {
respond_result!(req, false, format!("error parsing url: {}", e));
return;
}
};
match url.path() {
"/miner/start" => {
let params = url.query_pairs();
let params: HashMap<_, _> = params.into_owned().collect();
let lambda = match params.get("lambda") {
Some(v) => v,
None => {
respond_result!(req, false, "missing lambda");
return;
}
};
let lambda = match lambda.parse::<u64>() {
Ok(v) => v,
Err(e) => {
respond_result!(
req,
false,
format!("error parsing lambda: {}", e)
);
return;
}
};
miner.start(lambda);
respond_result!(req, true, "ok");
}
"/network/ping" => {
network.broadcast(Message::Ping(String::from("Test ping")));
respond_result!(req, true, "ok");
}
_ => {
let content_type =
"Content-Type: application/json".parse::<Header>().unwrap();
let payload = ApiResponse {
success: false,
message: "endpoint not found".to_string(),
};
let resp = Response::from_string(
serde_json::to_string_pretty(&payload).unwrap(),
)
.with_header(content_type)
.with_status_code(404);
req.respond(resp).unwrap();
}
}
});
}
});
info!("API server listening at {}", &addr);
}
}
use serde::{Serialize, Deserialize};
use crate::crypto::hash::{H256, Hashable};
#[derive(Serialize, Deserialize, Debug)]
pub struct Block {
}
impl Hashable for Block {
fn hash(&self) -> H256 {
unimplemented!()
}
}
#[cfg(any(test, test_utilities))]
pub mod test {
use super::*;
use crate::crypto::hash::H256;
pub fn generate_random_block(parent: &H256) -> Block {
unimplemented!()
}
}
use crate::block::Block;
use crate::crypto::hash::H256;
pub struct Blockchain {
}
impl Blockchain {
/// Create a new blockchain, only containing the genesis block
pub fn new() -> Self {
unimplemented!()
}
/// Insert a block into blockchain
pub fn insert(&mut self, block: &Block) {
unimplemented!()
}
/// Get the last block's hash of the longest chain
pub fn tip(&self) -> H256 {
unimplemented!()
}
/// Get the last block's hash of the longest chain
#[cfg(any(test, test_utilities))]
pub fn all_blocks_in_longest_chain(&self) -> Vec<H256> {
unimplemented!()
}
}
#[cfg(any(test, test_utilities))]
mod tests {
use super::*;
use crate::block::test::generate_random_block;
use crate::crypto::hash::Hashable;
#[test]
fn insert_one() {
let mut blockchain = Blockchain::new();
let genesis_hash = blockchain.tip();
let block = generate_random_block(&genesis_hash);
blockchain.insert(&block);
assert_eq!(blockchain.tip(), block.hash());
}
}
use serde::{Serialize, Deserialize};
use std::convert::TryInto;
/// An object that can be meaningfully hashed.
pub trait Hashable {
/// Hash the object using SHA256.
fn hash(&self) -> H256;
}
/// A SHA256 hash.
#[derive(Eq, PartialEq, Serialize, Deserialize, Clone, Hash, Default, Copy)]
pub struct H256([u8; 32]); // big endian u256
impl Hashable for H256 {
fn hash(&self) -> H256 {
ring::digest::digest(&ring::digest::SHA256, &self.0).into()
}
}
impl std::fmt::Display for H256 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let start = if let Some(precision) = f.precision() {
if precision >= 64 {
0
} else {
32 - precision / 2
}
} else {
0
};
for byte_idx in start..32 {
write!(f, "{:>02x}", &self.0[byte_idx])?;
}
Ok(())
}
}
impl std::fmt::Debug for H256 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{:>02x}{:>02x}..{:>02x}{:>02x}",
&self.0[0], &self.0[1], &self.0[30], &self.0[31]
)
}
}
impl std::convert::AsRef<[u8]> for H256 {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl std::convert::From<&[u8; 32]> for H256 {
fn from(input: &[u8; 32]) -> H256 {
let mut buffer: [u8; 32] = [0; 32];
buffer[..].copy_from_slice(input);
H256(buffer)
}
}
impl std::convert::From<&H256> for [u8; 32] {
fn from(input: &H256) -> [u8; 32] {
let mut buffer: [u8; 32] = [0; 32];
buffer[..].copy_from_slice(&input.0);
buffer
}
}
impl std::convert::From<[u8; 32]> for H256 {
fn from(input: [u8; 32]) -> H256 {
H256(input)
}
}
impl std::convert::From<H256> for [u8; 32] {
fn from(input: H256) -> [u8; 32] {
input.0
}
}
impl std::convert::From<ring::digest::Digest> for H256 {
fn from(input: ring::digest::Digest) -> H256 {
let mut raw_hash: [u8; 32] = [0; 32];
raw_hash[0..32].copy_from_slice(input.as_ref());
H256(raw_hash)
}
}
impl Ord for H256 {
fn cmp(&self, other: &H256) -> std::cmp::Ordering {
let self_higher = u128::from_be_bytes(self.0[0..16].try_into().unwrap());
let self_lower = u128::from_be_bytes(self.0[16..32].try_into().unwrap());
let other_higher = u128::from_be_bytes(other.0[0..16].try_into().unwrap());
let other_lower = u128::from_be_bytes(other.0[16..32].try_into().unwrap());
let higher = self_higher.cmp(&other_higher);
match higher {
std::cmp::Ordering::Equal => self_lower.cmp(&other_lower),
_ => higher,
}
}
}
impl PartialOrd for H256 {
fn partial_cmp(&self, other: &H256) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[cfg(any(test, test_utilities))]
pub mod tests {
use super::H256;
use rand::Rng;
pub fn generate_random_hash() -> H256 {
let mut rng = rand::thread_rng();
let random_bytes: Vec<u8> = (0..32).map(|_| rng.gen()).collect();
let mut raw_bytes = [0; 32];
raw_bytes.copy_from_slice(&random_bytes);
(&raw_bytes).into()
}
}
use ring::rand;
use ring::signature::Ed25519KeyPair;
/// Generate a random key pair.
pub fn random() -> Ed25519KeyPair {
let rng = rand::SystemRandom::new();
let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rng).unwrap();
Ed25519KeyPair::from_pkcs8(pkcs8_bytes.as_ref().into()).unwrap()
}
use super::hash::{Hashable, H256};
/// A Merkle tree.
#[derive(Debug, Default)]
pub struct MerkleTree {
}
impl MerkleTree {
pub fn new<T>(data: &[T]) -> Self where T: Hashable, {
unimplemented!()
}
pub fn root(&self) -> H256 {
unimplemented!()
}
/// Returns the Merkle Proof of data at index i
pub fn proof(&self, index: usize) -> Vec<H256> {
unimplemented!()
}
}
/// Verify that the datum hash with a vector of proofs will produce the Merkle root. Also need the
/// index of datum and `leaf_size`, the total number of leaves.
pub fn verify(root: &H256, datum: &H256, proof: &[H256], index: usize, leaf_size: usize) -> bool {
unimplemented!()
}
#[cfg(test)]
mod tests {
use crate::crypto::hash::H256;
use super::*;
macro_rules! gen_merkle_tree_data {
() => {{
vec![
(hex!("0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d")).into(),
(hex!("0101010101010101010101010101010101010101010101010101010101010202")).into(),
]
}};
}
#[test]
fn root() {
let input_data: Vec<H256> = gen_merkle_tree_data!();
let merkle_tree = MerkleTree::new(&input_data);
let root = merkle_tree.root();
assert_eq!(
root,
(hex!("6b787718210e0b3b608814e04e61fde06d0df794319a12162f287412df3ec920")).into()
);
// "b69566be6e1720872f73651d1851a0eae0060a132cf0f64a0ffaea248de6cba0" is the hash of
// "0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d"
// "965b093a75a75895a351786dd7a188515173f6928a8af8c9baa4dcff268a4f0f" is the hash of
// "0101010101010101010101010101010101010101010101010101010101010202"
// "6b787718210e0b3b608814e04e61fde06d0df794319a12162f287412df3ec920" is the hash of
// the concatenation of these two hashes "b69..." and "965..."
// notice that the order of these two matters
}
#[test]
fn proof() {
let input_data: Vec<H256> = gen_merkle_tree_data!();
let merkle_tree = MerkleTree::new(&input_data);
let proof = merkle_tree.proof(0);
assert_eq!(proof,
vec![hex!("965b093a75a75895a351786dd7a188515173f6928a8af8c9baa4dcff268a4f0f").into()]
);
// "965b093a75a75895a351786dd7a188515173f6928a8af8c9baa4dcff268a4f0f" is the hash of
// "0101010101010101010101010101010101010101010101010101010101010202"
}
#[test]
fn verifying() {
let input_data: Vec<H256> = gen_merkle_tree_data!();
let merkle_tree = MerkleTree::new(&input_data);
let proof = merkle_tree.proof(0);
assert!(verify(&merkle_tree.root(), &input_data[0].hash(), &proof, 0, input_data.len()));
}
}
pub mod hash;
pub mod merkle;
pub mod key_pair;
#[cfg(test)]
#[macro_use]
extern crate hex_literal;
pub mod api;
pub mod block;
pub mod blockchain;
pub mod crypto;
pub mod miner;
pub mod network;
pub mod transaction;
use clap::clap_app;
use crossbeam::channel;
use log::{error, info};
use api::Server as ApiServer;
use network::{server, worker};
use std::net;
use std::process;
use std::thread;
use std::time;
fn main() {
// parse command line arguments
let matches = clap_app!(Bitcoin =>
(version: "0.1")
(about: "Bitcoin client")
(@arg verbose: -v ... "Increases the verbosity of logging")
(@arg peer_addr: --p2p [ADDR] default_value("127.0.0.1:6000") "Sets the IP address and the port of the P2P server")
(@arg api_addr: --api [ADDR] default_value("127.0.0.1:7000") "Sets the IP address and the port of the API server")
(@arg known_peer: -c --connect ... [PEER] "Sets the peers to connect to at start")
(@arg p2p_workers: --("p2p-workers") [INT] default_value("4") "Sets the number of worker threads for P2P server")
)
.get_matches();
// init logger
let verbosity = matches.occurrences_of("verbose") as usize;
stderrlog::new().verbosity(verbosity).init().unwrap();
// parse p2p server address
let p2p_addr = matches
.value_of("peer_addr")
.unwrap()
.parse::<net::SocketAddr>()
.unwrap_or_else(|e| {
error!("Error parsing P2P server address: {}", e);
process::exit(1);
});
// parse api server address
let api_addr = matches
.value_of("api_addr")
.unwrap()
.parse::<net::SocketAddr>()
.unwrap_or_else(|e| {
error!("Error parsing API server address: {}", e);
process::exit(1);
});
// create channels between server and worker
let (msg_tx, msg_rx) = channel::unbounded();
// start the p2p server
let (server_ctx, server) = server::new(p2p_addr, msg_tx).unwrap();
server_ctx.start().unwrap();
// start the worker
let p2p_workers = matches
.value_of("p2p_workers")
.unwrap()
.parse::<usize>()
.unwrap_or_else(|e| {
error!("Error parsing P2P workers: {}", e);
process::exit(1);
});
let worker_ctx = worker::new(
p2p_workers,
msg_rx,
&server,
);
worker_ctx.start();
// start the miner
let (miner_ctx, miner) = miner::new(
&server,
);
miner_ctx.start();
// connect to known peers
if let Some(known_peers) = matches.values_of("known_peer") {
let known_peers: Vec<String> = known_peers.map(|x| x.to_owned()).collect();
let server = server.clone();
thread::spawn(move || {
for peer in known_peers {
loop {
let addr = match peer.parse::<net::SocketAddr>() {
Ok(x) => x,
Err(e) => {
error!("Error parsing peer address {}: {}", &peer, e);
break;
}
};
match server.connect(addr) {
Ok(_) => {
info!("Connected to outgoing peer {}", &addr);
break;
}
Err(e) => {
error!(
"Error connecting to peer {}, retrying in one second: {}",
addr, e
);
thread::sleep(time::Duration::from_millis(1000));
continue;
}
}
}
}
});
}
// start the API server
ApiServer::start(
api_addr,
&miner,
&server,
);
loop {
std::thread::park();
}
}
use crate::network::server::Handle as ServerHandle;
use log::info;
use crossbeam::channel::{unbounded, Receiver, Sender, TryRecvError};
use std::time;
use std::thread;
enum ControlSignal {
Start(u64), // the number controls the lambda of interval between block generation
Exit,