Merge branch 'master' into chain-builder
This commit is contained in:
commit
a972bbaf7a
|
@ -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)",
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
31
pbtc/main.rs
31
pbtc/main.rs
|
@ -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());
|
||||
|
|
|
@ -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(()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())) {
|
||||
|
|
Loading…
Reference in New Issue