Merge branch 'master' into chain-builder

This commit is contained in:
NikVolf 2016-10-28 01:17:13 +03:00
commit a972bbaf7a
12 changed files with 126 additions and 60 deletions

1
Cargo.lock generated
View File

@ -2,6 +2,7 @@
name = "pbtc"
version = "0.1.0"
dependencies = [
"chain 0.1.0",
"clap 2.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"db 0.1.0",
"env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -8,6 +8,7 @@ description = "Parity bitcoin client."
[dependencies]
env_logger = "0.3"
clap = { version = "2", features = ["yaml"] }
chain = { path = "chain" }
keys = { path = "keys" }
message = { path = "message" }
miner = { path = "miner" }

View File

@ -35,7 +35,7 @@ impl Public {
pub fn verify(&self, message: &Message, signature: &Signature) -> Result<bool, Error> {
let context = &SECP256K1;
let public = try!(key::PublicKey::from_slice(context, self));
let mut signature = try!(SecpSignature::from_der(context, signature));
let mut signature = try!(SecpSignature::from_der_lax(context, signature));
signature.normalize_s(context);
let message = try!(SecpMessage::from_slice(&**message));
match context.verify(&message, &signature, &public) {

View File

@ -1,37 +1,23 @@
use std::{net, io};
use std::time::Duration;
use futures::{Future, Poll};
use futures::stream::Stream;
use tokio_core::reactor::Handle;
use tokio_core::net::{TcpStream, TcpListener};
use tokio_core::io::IoStream;
use tokio_core::net::TcpStream;
use message::{MessageResult, Magic};
use io::{accept_handshake, AcceptHandshake};
use io::{accept_handshake, AcceptHandshake, Deadline, deadline};
use net::{Config, Connection};
pub fn listen(handle: &Handle, config: Config) -> Result<Listen, io::Error> {
let listener = try!(TcpListener::bind(&config.local_address, handle));
let listen = Listen {
inner: listener.incoming()
.and_then(move |(stream, address)| accept_connection(stream, &config, address))
.boxed(),
};
Ok(listen)
}
pub struct Listen {
inner: IoStream<MessageResult<Connection>>,
}
fn accept_connection(stream: TcpStream, config: &Config, address: net::SocketAddr) -> AcceptConnection {
AcceptConnection {
pub fn accept_connection(stream: TcpStream, handle: &Handle, config: &Config, address: net::SocketAddr) -> Deadline<AcceptConnection> {
let accept = AcceptConnection {
handshake: accept_handshake(stream, config.magic, config.version(&address)),
magic: config.magic,
address: address,
}
};
deadline(Duration::new(5, 0), handle, accept).expect("Failed to create timeout")
}
struct AcceptConnection {
pub struct AcceptConnection {
handshake: AcceptHandshake<TcpStream>,
magic: Magic,
address: net::SocketAddr,
@ -57,12 +43,3 @@ impl Future for AcceptConnection {
Ok(Ok(connection).into())
}
}
impl Stream for Listen {
type Item = MessageResult<Connection>;
type Error = io::Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
self.inner.poll()
}
}

View File

@ -1,4 +1,5 @@
use std::io;
use std::time::Duration;
use std::net::SocketAddr;
use futures::{Future, Poll, Async};
use tokio_core::reactor::Handle;
@ -6,18 +7,20 @@ use tokio_core::net::{TcpStream, TcpStreamNew};
use message::Error;
use message::common::Magic;
use message::types::Version;
use io::{handshake, Handshake};
use io::{handshake, Handshake, Deadline, deadline};
use net::{Config, Connection};
pub fn connect(address: &SocketAddr, handle: &Handle, config: &Config) -> Connect {
Connect {
pub fn connect(address: &SocketAddr, handle: &Handle, config: &Config) -> Deadline<Connect> {
let connect = Connect {
state: ConnectState::TcpConnect {
future: TcpStream::connect(address, handle),
version: Some(config.version(address)),
},
magic: config.magic,
address: *address,
}
};
deadline(Duration::new(5, 0), handle, connect).expect("Failed to create timeout")
}
enum ConnectState {

View File

@ -1,13 +1,13 @@
mod accept_connection;
mod channel;
mod config;
mod connect;
mod connection;
mod connections;
mod listen;
pub use self::accept_connection::{AcceptConnection, accept_connection};
pub use self::channel::Channel;
pub use self::config::Config;
pub use self::connect::{Connect, connect};
pub use self::connection::Connection;
pub use self::connections::Connections;
pub use self::listen::{Listen, listen};

View File

@ -5,16 +5,18 @@ use futures::{Future, finished, failed, BoxFuture};
use futures::stream::Stream;
use futures_cpupool::CpuPool;
use tokio_core::io::IoFuture;
use tokio_core::net::{TcpListener, TcpStream};
use tokio_core::reactor::{Handle, Remote, Timeout};
use abstract_ns::Resolver;
use ns_dns_tokio::DnsResolver;
use message::{Payload, MessageResult};
use protocol::Direction;
use net::{connect, listen, Connections, Channel, Config as NetConfig};
use net::{connect, Connections, Channel, Config as NetConfig, accept_connection};
use util::{NodeTable, Node};
use session::{SessionFactory, SeednodeSessionFactory, NormalSessionFactory};
use {Config, PeerId};
use protocol::{LocalSyncNodeRef, InboundSyncConnectionRef, OutboundSyncConnectionRef};
use io::DeadlineStatus;
pub type BoxedEmptyFuture = BoxFuture<(), ()>;
@ -84,7 +86,7 @@ impl Context {
let connection = connect(&socket, handle, config);
connection.then(move |result| {
match result {
Ok(Ok(connection)) => {
Ok(DeadlineStatus::Meet(Ok(connection))) => {
// successfull hanshake
trace!("Connected to {}", connection.address);
context.node_table.write().insert(connection.address, connection.services);
@ -94,12 +96,18 @@ impl Context {
channel.session().initialize(channel.clone(), Direction::Outbound);
Context::on_message(context, channel)
},
Ok(Err(err)) => {
Ok(DeadlineStatus::Meet(Err(err))) => {
// protocol error
trace!("Handshake with {} failed", socket);
// TODO: close socket
finished(Err(err)).boxed()
},
Ok(DeadlineStatus::Timeout) => {
// connection time out
trace!("Handshake with {} timedout", socket);
// TODO: close socket
finished(Ok(())).boxed()
},
Err(err) => {
// network error
trace!("Unable to connect to {}", socket);
@ -118,13 +126,10 @@ impl Context {
})
}
/// Starts tcp server and listens for incomming connections.
pub fn listen(context: Arc<Context>, handle: &Handle, config: NetConfig) -> Result<BoxedEmptyFuture, io::Error> {
trace!("Starting tcp server");
let listen = try!(listen(&handle, config));
let server = listen.then(move |result| {
pub fn accept_connection_future(context: Arc<Context>, stream: TcpStream, socket: net::SocketAddr, handle: &Handle, config: NetConfig) -> BoxedEmptyFuture {
accept_connection(stream, handle, &config, socket).then(move |result| {
match result {
Ok(Ok(connection)) => {
Ok(DeadlineStatus::Meet(Ok(connection))) => {
// successfull hanshake
trace!("Accepted connection from {}", connection.address);
context.node_table.write().insert(connection.address, connection.services);
@ -134,20 +139,45 @@ impl Context {
channel.session().initialize(channel.clone(), Direction::Inbound);
Context::on_message(context.clone(), channel)
},
Ok(Err(err)) => {
Ok(DeadlineStatus::Meet(Err(err))) => {
// protocol error
// TODO: close socket
finished(Err(err)).boxed()
},
Ok(DeadlineStatus::Timeout) => {
// connection time out
trace!("Handshake with {} timedout", socket);
// TODO: close socket
finished(Ok(())).boxed()
},
Err(err) => {
// network error
failed(err).boxed()
}
}
})
.for_each(|_| Ok(()))
.then(|_| finished(()))
.boxed();
.boxed()
}
pub fn accept_connection(context: Arc<Context>, stream: TcpStream, socket: net::SocketAddr, config: NetConfig) {
context.remote.clone().spawn(move |handle| {
context.pool.clone().spawn(Context::accept_connection_future(context, stream, socket, handle, config))
})
}
/// Starts tcp server and listens for incomming connections.
pub fn listen(context: Arc<Context>, handle: &Handle, config: NetConfig) -> Result<BoxedEmptyFuture, io::Error> {
trace!("Starting tcp server");
let server = try!(TcpListener::bind(&config.local_address, handle));
let server = server.incoming()
.and_then(move |(stream, socket)| {
Context::accept_connection(context.clone(), stream, socket, config.clone());
Ok(())
})
.for_each(|_| Ok(()))
.then(|_| finished(()))
.boxed();
Ok(server)
}
@ -324,8 +354,7 @@ impl P2P {
fn listen(&self) -> Result<(), Box<error::Error>> {
let server = try!(Context::listen(self.context.clone(), &self.event_loop_handle, self.config.connection.clone()));
let pool_work = self.pool.spawn(server);
self.event_loop_handle.spawn(pool_work);
self.event_loop_handle.spawn(server);
Ok(())
}
}

View File

@ -26,4 +26,7 @@ args:
- printtoconsole:
long: printtoconsole
help: Send trace/debug info to console instead of debug.log file
- diskdb:
long: diskdb
help: Use disk storage instead of in-memory one

View File

@ -8,10 +8,12 @@ pub struct Config {
pub connect: Option<net::IpAddr>,
pub seednode: Option<String>,
pub print_to_console: bool,
pub use_disk_database: bool,
}
pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> {
let print_to_console = matches.is_present("printtoconsole");
let use_disk_database = matches.is_present("diskdb");
let magic = match matches.is_present("testnet") {
true => Magic::Testnet,
false => Magic::Mainnet,
@ -38,6 +40,7 @@ pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> {
port: port,
connect: connect,
seednode: seednode,
use_disk_database: use_disk_database,
};
Ok(config)

View File

@ -5,6 +5,7 @@ extern crate clap;
extern crate db;
extern crate env_logger;
extern crate chain;
extern crate keys;
extern crate script;
extern crate message;
@ -13,11 +14,13 @@ extern crate sync;
mod config;
use std::env;
use std::sync::Arc;
use std::net::SocketAddr;
use p2p::{P2P, event_loop, forever, NetConfig};
use sync::local_node::LocalNode;
use sync::inbound_connection_factory::InboundConnectionFactory;
use chain::Block;
fn main() {
env_logger::init().unwrap();
@ -27,6 +30,30 @@ fn main() {
}
}
fn open_db(use_disk_database: bool) -> Arc<db::Store> {
match use_disk_database {
true => {
let mut db_path = env::home_dir().expect("Failed to get home dir");
db_path.push(".pbtc");
db_path.push("db");
Arc::new(db::Storage::new(db_path).expect("Failed to open database"))
},
false => {
Arc::new(db::TestStorage::default())
}
}
}
fn init_db(db: &Arc<db::Store>) {
// insert genesis block if db is empty
if db.best_block_number().is_none() {
// TODO: move to config
let genesis_block: Block = "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".into();
db.insert_block(&genesis_block)
.expect("Failed to insert genesis block to the database");
}
}
fn run() -> Result<(), String> {
let yaml = load_yaml!("cli.yml");
let matches = clap::App::from_yaml(yaml).get_matches();
@ -52,8 +79,8 @@ fn run() -> Result<(), String> {
seeds: cfg.seednode.map_or_else(|| vec![], |x| vec![x]),
};
// TODO: in-memory database is used
let db = Arc::new(db::TestStorage::with_genesis_block());
let db = open_db(cfg.use_disk_database);
init_db(&db);
let local_sync_node = LocalNode::new(db);
let local_sync_factory = InboundConnectionFactory::with_local_node(local_sync_node.clone());

View File

@ -1862,4 +1862,20 @@ mod tests {
.verify_p2sh(true);
assert_eq!(verify_script(&input, &output, &flags, &checker), Ok(()));
}
// https://blockchain.info/rawtx/fb0a1d8d34fa5537e461ac384bac761125e1bfa7fec286fa72511240fa66864d
#[test]
fn test_transaction_from_124276() {
let tx: Transaction = "01000000012316aac445c13ff31af5f3d1e2cebcada83e54ba10d15e01f49ec28bddc285aa000000008e4b3048022200002b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378db4b14e736602220000334a96676e58b1bb01784cb7c556dd8ce1c220171904da22e18fe1e7d1510db5014104d0fe07ff74c9ef5b00fed1104fad43ecf72dbab9e60733e4f56eacf24b20cf3b8cd945bcabcc73ba0158bf9ce769d43e94bd58c5c7e331a188922b3fe9ca1f5affffffff01c0c62d00000000001976a9147a2a3b481ca80c4ba7939c54d9278e50189d94f988ac00000000".into();
let signer: TransactionInputSigner = tx.into();
let checker = TransactionSignatureChecker {
signer: signer,
input_index: 0,
};
let input: Script = "4b3048022200002b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378db4b14e736602220000334a96676e58b1bb01784cb7c556dd8ce1c220171904da22e18fe1e7d1510db5014104d0fe07ff74c9ef5b00fed1104fad43ecf72dbab9e60733e4f56eacf24b20cf3b8cd945bcabcc73ba0158bf9ce769d43e94bd58c5c7e331a188922b3fe9ca1f5a".into();
let output: Script = "76a9147a2a3b481ca80c4ba7939c54d9278e50189d94f988ac".into();
let flags = VerificationFlags::default()
.verify_p2sh(true);
assert_eq!(verify_script(&input, &output, &flags, &checker), Ok(()));
}
}

View File

@ -18,7 +18,7 @@ impl ChainVerifier {
ChainVerifier { store: store }
}
fn verify_transaction(&self, transaction: &chain::Transaction) -> Result<(), TransactionError> {
fn verify_transaction(&self, block: &chain::Block, transaction: &chain::Transaction) -> Result<(), TransactionError> {
use script::{
TransactionInputSigner,
TransactionSignatureChecker,
@ -28,9 +28,15 @@ impl ChainVerifier {
};
for (input_index, input) in transaction.inputs().iter().enumerate() {
let parent_transaction = match self.store.transaction(&input.previous_output.hash) {
Some(tx) => tx,
None => { return Err(TransactionError::Input(input_index)); }
let store_parent_transaction = self.store.transaction(&input.previous_output.hash);
let parent_transaction = match store_parent_transaction {
Some(ref tx) => tx,
None => {
match block.transactions.iter().filter(|t| t.hash() == input.previous_output.hash).nth(0) {
Some(ref tx) => tx,
None => { return Err(TransactionError::Input(input_index)); },
}
},
};
if parent_transaction.outputs.len() <= input.previous_output.index as usize {
return Err(TransactionError::Input(input_index));
@ -89,7 +95,7 @@ impl Verify for ChainVerifier {
// verify transactions (except coinbase)
for (idx, transaction) in block.transactions().iter().skip(1).enumerate() {
try!(self.verify_transaction(transaction).map_err(|e| Error::Transaction(idx, e)));
try!(self.verify_transaction(block, transaction).map_err(|e| Error::Transaction(idx, e)));
}
let _parent = match self.store.block(BlockRef::Hash(block.header().previous_header_hash.clone())) {