Placeholder storage contract and replicator client (#1286)
* Add hooks for executing the storage contract * Add store_ledger stage Similar to replicate_stage but no voting/banking stuff, just convert blobs to entries and write the ledger out * Add storage_addr to tests and add new NodeInfo constructor to reduce duplication...
This commit is contained in:
parent
3dcee9f79e
commit
a9355c33b2
|
@ -33,6 +33,10 @@ path = "src/bin/bench-tps.rs"
|
||||||
name = "solana-drone"
|
name = "solana-drone"
|
||||||
path = "src/bin/drone.rs"
|
path = "src/bin/drone.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "solana-replicator"
|
||||||
|
path = "src/bin/replicator.rs"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "solana-fullnode"
|
name = "solana-fullnode"
|
||||||
path = "src/bin/fullnode.rs"
|
path = "src/bin/fullnode.rs"
|
||||||
|
|
|
@ -21,6 +21,7 @@ use std::result;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
use storage_program::StorageProgram;
|
||||||
use system_program::SystemProgram;
|
use system_program::SystemProgram;
|
||||||
use timing::{duration_as_us, timestamp};
|
use timing::{duration_as_us, timestamp};
|
||||||
use transaction::Transaction;
|
use transaction::Transaction;
|
||||||
|
@ -359,6 +360,8 @@ impl Bank {
|
||||||
// TODO: the runtime should be checking read/write access to memory
|
// TODO: the runtime should be checking read/write access to memory
|
||||||
// we are trusting the hard coded contracts not to clobber or allocate
|
// we are trusting the hard coded contracts not to clobber or allocate
|
||||||
BudgetState::process_transaction(&tx, accounts)
|
BudgetState::process_transaction(&tx, accounts)
|
||||||
|
} else if StorageProgram::check_id(&tx.program_id) {
|
||||||
|
StorageProgram::process_transaction(&tx, accounts)
|
||||||
} else {
|
} else {
|
||||||
return Err(BankError::UnknownContractId(tx.program_id));
|
return Err(BankError::UnknownContractId(tx.program_id));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
extern crate getopts;
|
||||||
|
extern crate serde_json;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate solana;
|
||||||
|
|
||||||
|
use clap::{App, Arg};
|
||||||
|
use solana::crdt::Node;
|
||||||
|
use solana::fullnode::Config;
|
||||||
|
use solana::logger;
|
||||||
|
use solana::replicator::Replicator;
|
||||||
|
use solana::signature::{Keypair, KeypairUtil};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::net::{Ipv4Addr, SocketAddr};
|
||||||
|
use std::process::exit;
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
logger::setup();
|
||||||
|
let matches = App::new("replicator")
|
||||||
|
.version(crate_version!())
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("identity")
|
||||||
|
.short("i")
|
||||||
|
.long("identity")
|
||||||
|
.value_name("PATH")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Run with the identity found in FILE"),
|
||||||
|
).arg(
|
||||||
|
Arg::with_name("network")
|
||||||
|
.short("n")
|
||||||
|
.long("network")
|
||||||
|
.value_name("HOST:PORT")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Rendezvous with the network at this gossip entry point"),
|
||||||
|
).arg(
|
||||||
|
Arg::with_name("ledger")
|
||||||
|
.short("l")
|
||||||
|
.long("ledger")
|
||||||
|
.value_name("DIR")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("use DIR as persistent ledger location"),
|
||||||
|
).get_matches();
|
||||||
|
|
||||||
|
let ledger_path = matches.value_of("ledger");
|
||||||
|
|
||||||
|
let (keypair, ncp) = if let Some(i) = matches.value_of("identity") {
|
||||||
|
let path = i.to_string();
|
||||||
|
if let Ok(file) = File::open(path.clone()) {
|
||||||
|
let parse: serde_json::Result<Config> = serde_json::from_reader(file);
|
||||||
|
if let Ok(data) = parse {
|
||||||
|
(data.keypair(), data.node_info.contact_info.ncp)
|
||||||
|
} else {
|
||||||
|
eprintln!("failed to parse {}", path);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eprintln!("failed to read {}", path);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(Keypair::new(), socketaddr!([127, 0, 0, 1], 8700))
|
||||||
|
};
|
||||||
|
|
||||||
|
let node = Node::new_with_external_ip(keypair.pubkey(), &ncp);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"replicating the data with keypair: {:?} ncp:{:?}",
|
||||||
|
keypair.pubkey(),
|
||||||
|
ncp
|
||||||
|
);
|
||||||
|
println!("my node: {:?}", node);
|
||||||
|
|
||||||
|
let exit = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
|
let network_addr = matches
|
||||||
|
.value_of("network")
|
||||||
|
.map(|network| network.parse().expect("failed to parse network address"));
|
||||||
|
|
||||||
|
// TODO: ask network what slice we should store
|
||||||
|
let entry_height = 0;
|
||||||
|
|
||||||
|
let replicator = Replicator::new(entry_height, &exit, ledger_path, node, network_addr);
|
||||||
|
|
||||||
|
replicator.join();
|
||||||
|
}
|
99
src/crdt.rs
99
src/crdt.rs
|
@ -82,6 +82,8 @@ pub struct ContactInfo {
|
||||||
pub rpu: SocketAddr,
|
pub rpu: SocketAddr,
|
||||||
/// transactions address
|
/// transactions address
|
||||||
pub tpu: SocketAddr,
|
pub tpu: SocketAddr,
|
||||||
|
/// storage data address
|
||||||
|
pub storage_addr: SocketAddr,
|
||||||
/// if this struture changes update this value as well
|
/// if this struture changes update this value as well
|
||||||
/// Always update `NodeInfo` version too
|
/// Always update `NodeInfo` version too
|
||||||
/// This separate version for addresses allows us to use the `Vote`
|
/// This separate version for addresses allows us to use the `Vote`
|
||||||
|
@ -116,6 +118,7 @@ impl NodeInfo {
|
||||||
tvu: SocketAddr,
|
tvu: SocketAddr,
|
||||||
rpu: SocketAddr,
|
rpu: SocketAddr,
|
||||||
tpu: SocketAddr,
|
tpu: SocketAddr,
|
||||||
|
storage_addr: SocketAddr,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
NodeInfo {
|
NodeInfo {
|
||||||
id,
|
id,
|
||||||
|
@ -125,6 +128,7 @@ impl NodeInfo {
|
||||||
tvu,
|
tvu,
|
||||||
rpu,
|
rpu,
|
||||||
tpu,
|
tpu,
|
||||||
|
storage_addr,
|
||||||
version: 0,
|
version: 0,
|
||||||
},
|
},
|
||||||
leader_id: Pubkey::default(),
|
leader_id: Pubkey::default(),
|
||||||
|
@ -134,19 +138,30 @@ impl NodeInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_localhost(id: Pubkey) -> Self {
|
||||||
|
Self::new(
|
||||||
|
id,
|
||||||
|
socketaddr!("127.0.0.1:1234"),
|
||||||
|
socketaddr!("127.0.0.1:1235"),
|
||||||
|
socketaddr!("127.0.0.1:1236"),
|
||||||
|
socketaddr!("127.0.0.1:1237"),
|
||||||
|
socketaddr!("127.0.0.1:1238"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
/// NodeInfo with unspecified addresses for adversarial testing.
|
/// NodeInfo with unspecified addresses for adversarial testing.
|
||||||
pub fn new_unspecified() -> Self {
|
pub fn new_unspecified() -> Self {
|
||||||
let addr = socketaddr!(0, 0);
|
let addr = socketaddr!(0, 0);
|
||||||
assert!(addr.ip().is_unspecified());
|
assert!(addr.ip().is_unspecified());
|
||||||
Self::new(Keypair::new().pubkey(), addr, addr, addr, addr)
|
Self::new(Keypair::new().pubkey(), addr, addr, addr, addr, addr)
|
||||||
}
|
}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
/// NodeInfo with multicast addresses for adversarial testing.
|
/// NodeInfo with multicast addresses for adversarial testing.
|
||||||
pub fn new_multicast() -> Self {
|
pub fn new_multicast() -> Self {
|
||||||
let addr = socketaddr!("224.0.1.255:1000");
|
let addr = socketaddr!("224.0.1.255:1000");
|
||||||
assert!(addr.ip().is_multicast());
|
assert!(addr.ip().is_multicast());
|
||||||
Self::new(Keypair::new().pubkey(), addr, addr, addr, addr)
|
Self::new(Keypair::new().pubkey(), addr, addr, addr, addr, addr)
|
||||||
}
|
}
|
||||||
fn next_port(addr: &SocketAddr, nxt: u16) -> SocketAddr {
|
fn next_port(addr: &SocketAddr, nxt: u16) -> SocketAddr {
|
||||||
let mut nxt_addr = *addr;
|
let mut nxt_addr = *addr;
|
||||||
|
@ -164,6 +179,7 @@ impl NodeInfo {
|
||||||
replicate_addr,
|
replicate_addr,
|
||||||
requests_addr,
|
requests_addr,
|
||||||
transactions_addr,
|
transactions_addr,
|
||||||
|
"0.0.0.0:0".parse().unwrap(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
pub fn new_with_socketaddr(bind_addr: &SocketAddr) -> Self {
|
pub fn new_with_socketaddr(bind_addr: &SocketAddr) -> Self {
|
||||||
|
@ -173,7 +189,7 @@ impl NodeInfo {
|
||||||
//
|
//
|
||||||
pub fn new_entry_point(gossip_addr: &SocketAddr) -> Self {
|
pub fn new_entry_point(gossip_addr: &SocketAddr) -> Self {
|
||||||
let daddr: SocketAddr = socketaddr!("0.0.0.0:0");
|
let daddr: SocketAddr = socketaddr!("0.0.0.0:0");
|
||||||
NodeInfo::new(Pubkey::default(), *gossip_addr, daddr, daddr, daddr)
|
NodeInfo::new(Pubkey::default(), *gossip_addr, daddr, daddr, daddr, daddr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1238,11 +1254,12 @@ impl Crdt {
|
||||||
let pubkey = Keypair::new().pubkey();
|
let pubkey = Keypair::new().pubkey();
|
||||||
let daddr = socketaddr_any!();
|
let daddr = socketaddr_any!();
|
||||||
|
|
||||||
let node = NodeInfo::new(pubkey, daddr, daddr, daddr, daddr);
|
let node = NodeInfo::new(pubkey, daddr, daddr, daddr, daddr, daddr);
|
||||||
(node, gossip_socket)
|
(node, gossip_socket)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Sockets {
|
pub struct Sockets {
|
||||||
pub gossip: UdpSocket,
|
pub gossip: UdpSocket,
|
||||||
pub requests: UdpSocket,
|
pub requests: UdpSocket,
|
||||||
|
@ -1254,6 +1271,7 @@ pub struct Sockets {
|
||||||
pub retransmit: UdpSocket,
|
pub retransmit: UdpSocket,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
pub info: NodeInfo,
|
pub info: NodeInfo,
|
||||||
pub sockets: Sockets,
|
pub sockets: Sockets,
|
||||||
|
@ -1274,12 +1292,14 @@ impl Node {
|
||||||
let respond = UdpSocket::bind("0.0.0.0:0").unwrap();
|
let respond = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||||
let broadcast = UdpSocket::bind("0.0.0.0:0").unwrap();
|
let broadcast = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||||
let retransmit = UdpSocket::bind("0.0.0.0:0").unwrap();
|
let retransmit = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||||
|
let storage = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||||
let info = NodeInfo::new(
|
let info = NodeInfo::new(
|
||||||
pubkey,
|
pubkey,
|
||||||
gossip.local_addr().unwrap(),
|
gossip.local_addr().unwrap(),
|
||||||
replicate.local_addr().unwrap(),
|
replicate.local_addr().unwrap(),
|
||||||
requests.local_addr().unwrap(),
|
requests.local_addr().unwrap(),
|
||||||
transaction.local_addr().unwrap(),
|
transaction.local_addr().unwrap(),
|
||||||
|
storage.local_addr().unwrap(),
|
||||||
);
|
);
|
||||||
Node {
|
Node {
|
||||||
info,
|
info,
|
||||||
|
@ -1317,6 +1337,7 @@ impl Node {
|
||||||
let (_, repair) = bind();
|
let (_, repair) = bind();
|
||||||
let (_, broadcast) = bind();
|
let (_, broadcast) = bind();
|
||||||
let (_, retransmit) = bind();
|
let (_, retransmit) = bind();
|
||||||
|
let (storage_port, _) = bind();
|
||||||
|
|
||||||
// Responses are sent from the same Udp port as requests are received
|
// Responses are sent from the same Udp port as requests are received
|
||||||
// from, in hopes that a NAT sitting in the middle will route the
|
// from, in hopes that a NAT sitting in the middle will route the
|
||||||
|
@ -1329,6 +1350,7 @@ impl Node {
|
||||||
SocketAddr::new(ncp.ip(), replicate_port),
|
SocketAddr::new(ncp.ip(), replicate_port),
|
||||||
SocketAddr::new(ncp.ip(), requests_port),
|
SocketAddr::new(ncp.ip(), requests_port),
|
||||||
SocketAddr::new(ncp.ip(), transaction_port),
|
SocketAddr::new(ncp.ip(), transaction_port),
|
||||||
|
SocketAddr::new(ncp.ip(), storage_port),
|
||||||
);
|
);
|
||||||
trace!("new NodeInfo: {:?}", info);
|
trace!("new NodeInfo: {:?}", info);
|
||||||
|
|
||||||
|
@ -1380,13 +1402,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn insert_test() {
|
fn insert_test() {
|
||||||
let mut d = NodeInfo::new(
|
let mut d = NodeInfo::new_localhost(Keypair::new().pubkey());
|
||||||
Keypair::new().pubkey(),
|
|
||||||
socketaddr!("127.0.0.1:1234"),
|
|
||||||
socketaddr!("127.0.0.1:1235"),
|
|
||||||
socketaddr!("127.0.0.1:1236"),
|
|
||||||
socketaddr!("127.0.0.1:1237"),
|
|
||||||
);
|
|
||||||
assert_eq!(d.version, 0);
|
assert_eq!(d.version, 0);
|
||||||
let mut crdt = Crdt::new(d.clone()).unwrap();
|
let mut crdt = Crdt::new(d.clone()).unwrap();
|
||||||
assert_eq!(crdt.table[&d.id].version, 0);
|
assert_eq!(crdt.table[&d.id].version, 0);
|
||||||
|
@ -1486,27 +1502,9 @@ mod tests {
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn update_test() {
|
fn update_test() {
|
||||||
let d1 = NodeInfo::new(
|
let d1 = NodeInfo::new_localhost(Keypair::new().pubkey());
|
||||||
Keypair::new().pubkey(),
|
let d2 = NodeInfo::new_localhost(Keypair::new().pubkey());
|
||||||
socketaddr!("127.0.0.1:1234"),
|
let d3 = NodeInfo::new_localhost(Keypair::new().pubkey());
|
||||||
socketaddr!("127.0.0.1:1235"),
|
|
||||||
socketaddr!("127.0.0.1:1236"),
|
|
||||||
socketaddr!("127.0.0.1:1237"),
|
|
||||||
);
|
|
||||||
let d2 = NodeInfo::new(
|
|
||||||
Keypair::new().pubkey(),
|
|
||||||
socketaddr!("127.0.0.1:1234"),
|
|
||||||
socketaddr!("127.0.0.1:1235"),
|
|
||||||
socketaddr!("127.0.0.1:1236"),
|
|
||||||
socketaddr!("127.0.0.1:1237"),
|
|
||||||
);
|
|
||||||
let d3 = NodeInfo::new(
|
|
||||||
Keypair::new().pubkey(),
|
|
||||||
socketaddr!("127.0.0.1:1234"),
|
|
||||||
socketaddr!("127.0.0.1:1235"),
|
|
||||||
socketaddr!("127.0.0.1:1236"),
|
|
||||||
socketaddr!("127.0.0.1:1237"),
|
|
||||||
);
|
|
||||||
let mut crdt = Crdt::new(d1.clone()).expect("Crdt::new");
|
let mut crdt = Crdt::new(d1.clone()).expect("Crdt::new");
|
||||||
let (key, ix, ups) = crdt.get_updates_since(0);
|
let (key, ix, ups) = crdt.get_updates_since(0);
|
||||||
assert_eq!(key, d1.id);
|
assert_eq!(key, d1.id);
|
||||||
|
@ -1542,13 +1540,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn window_index_request() {
|
fn window_index_request() {
|
||||||
let me = NodeInfo::new(
|
let me = NodeInfo::new_localhost(Keypair::new().pubkey());
|
||||||
Keypair::new().pubkey(),
|
|
||||||
socketaddr!([127, 0, 0, 1], 1234),
|
|
||||||
socketaddr!([127, 0, 0, 1], 1235),
|
|
||||||
socketaddr!([127, 0, 0, 1], 1236),
|
|
||||||
socketaddr!([127, 0, 0, 1], 1237),
|
|
||||||
);
|
|
||||||
let mut crdt = Crdt::new(me).expect("Crdt::new");
|
let mut crdt = Crdt::new(me).expect("Crdt::new");
|
||||||
let rv = crdt.window_index_request(0);
|
let rv = crdt.window_index_request(0);
|
||||||
assert_matches!(rv, Err(Error::CrdtError(CrdtError::NoPeers)));
|
assert_matches!(rv, Err(Error::CrdtError(CrdtError::NoPeers)));
|
||||||
|
@ -1560,6 +1552,7 @@ mod tests {
|
||||||
socketaddr!([127, 0, 0, 1], 1235),
|
socketaddr!([127, 0, 0, 1], 1235),
|
||||||
socketaddr!([127, 0, 0, 1], 1236),
|
socketaddr!([127, 0, 0, 1], 1236),
|
||||||
socketaddr!([127, 0, 0, 1], 1237),
|
socketaddr!([127, 0, 0, 1], 1237),
|
||||||
|
socketaddr!([127, 0, 0, 1], 1238),
|
||||||
);
|
);
|
||||||
crdt.insert(&nxt);
|
crdt.insert(&nxt);
|
||||||
let rv = crdt.window_index_request(0).unwrap();
|
let rv = crdt.window_index_request(0).unwrap();
|
||||||
|
@ -1573,6 +1566,7 @@ mod tests {
|
||||||
socketaddr!([127, 0, 0, 1], 1235),
|
socketaddr!([127, 0, 0, 1], 1235),
|
||||||
socketaddr!([127, 0, 0, 1], 1236),
|
socketaddr!([127, 0, 0, 1], 1236),
|
||||||
socketaddr!([127, 0, 0, 1], 1237),
|
socketaddr!([127, 0, 0, 1], 1237),
|
||||||
|
socketaddr!([127, 0, 0, 1], 1238),
|
||||||
);
|
);
|
||||||
crdt.insert(&nxt);
|
crdt.insert(&nxt);
|
||||||
let mut one = false;
|
let mut one = false;
|
||||||
|
@ -1598,6 +1592,7 @@ mod tests {
|
||||||
socketaddr!("127.0.0.1:127"),
|
socketaddr!("127.0.0.1:127"),
|
||||||
socketaddr!("127.0.0.1:127"),
|
socketaddr!("127.0.0.1:127"),
|
||||||
socketaddr!("127.0.0.1:127"),
|
socketaddr!("127.0.0.1:127"),
|
||||||
|
socketaddr!("127.0.0.1:127"),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut crdt = Crdt::new(me).expect("Crdt::new");
|
let mut crdt = Crdt::new(me).expect("Crdt::new");
|
||||||
|
@ -1616,23 +1611,11 @@ mod tests {
|
||||||
/// test that gossip requests are eventually generated for all nodes
|
/// test that gossip requests are eventually generated for all nodes
|
||||||
#[test]
|
#[test]
|
||||||
fn gossip_request() {
|
fn gossip_request() {
|
||||||
let me = NodeInfo::new(
|
let me = NodeInfo::new_localhost(Keypair::new().pubkey());
|
||||||
Keypair::new().pubkey(),
|
|
||||||
socketaddr!("127.0.0.1:1234"),
|
|
||||||
socketaddr!("127.0.0.1:1235"),
|
|
||||||
socketaddr!("127.0.0.1:1236"),
|
|
||||||
socketaddr!("127.0.0.1:1237"),
|
|
||||||
);
|
|
||||||
let mut crdt = Crdt::new(me.clone()).expect("Crdt::new");
|
let mut crdt = Crdt::new(me.clone()).expect("Crdt::new");
|
||||||
let rv = crdt.gossip_request();
|
let rv = crdt.gossip_request();
|
||||||
assert_matches!(rv, Err(Error::CrdtError(CrdtError::NoPeers)));
|
assert_matches!(rv, Err(Error::CrdtError(CrdtError::NoPeers)));
|
||||||
let nxt1 = NodeInfo::new(
|
let nxt1 = NodeInfo::new_localhost(Keypair::new().pubkey());
|
||||||
Keypair::new().pubkey(),
|
|
||||||
socketaddr!("127.0.0.2:1234"),
|
|
||||||
socketaddr!("127.0.0.1:1235"),
|
|
||||||
socketaddr!("127.0.0.1:1236"),
|
|
||||||
socketaddr!("127.0.0.1:1237"),
|
|
||||||
);
|
|
||||||
|
|
||||||
crdt.insert(&nxt1);
|
crdt.insert(&nxt1);
|
||||||
|
|
||||||
|
@ -1754,6 +1737,7 @@ mod tests {
|
||||||
socketaddr!("127.0.0.1:1235"),
|
socketaddr!("127.0.0.1:1235"),
|
||||||
socketaddr!("127.0.0.1:1236"),
|
socketaddr!("127.0.0.1:1236"),
|
||||||
socketaddr!("127.0.0.1:1237"),
|
socketaddr!("127.0.0.1:1237"),
|
||||||
|
socketaddr!("127.0.0.1:1238"),
|
||||||
);
|
);
|
||||||
let recycler = BlobRecycler::default();
|
let recycler = BlobRecycler::default();
|
||||||
let rv = Crdt::run_window_request(
|
let rv = Crdt::run_window_request(
|
||||||
|
@ -1922,6 +1906,7 @@ mod tests {
|
||||||
socketaddr_any!(),
|
socketaddr_any!(),
|
||||||
socketaddr!("127.0.0.1:1236"),
|
socketaddr!("127.0.0.1:1236"),
|
||||||
socketaddr_any!(),
|
socketaddr_any!(),
|
||||||
|
socketaddr_any!(),
|
||||||
);
|
);
|
||||||
leader3.ledger_state.last_id = hash(b"3");
|
leader3.ledger_state.last_id = hash(b"3");
|
||||||
let mut crdt = Crdt::new(leader0.clone()).expect("Crdt::new");
|
let mut crdt = Crdt::new(leader0.clone()).expect("Crdt::new");
|
||||||
|
@ -2011,13 +1996,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_default_leader() {
|
fn test_default_leader() {
|
||||||
logger::setup();
|
logger::setup();
|
||||||
let node_info = NodeInfo::new(
|
let node_info = NodeInfo::new_localhost(Keypair::new().pubkey());
|
||||||
Keypair::new().pubkey(),
|
|
||||||
socketaddr!("127.0.0.1:1234"),
|
|
||||||
socketaddr!("127.0.0.1:1235"),
|
|
||||||
socketaddr!("127.0.0.1:1236"),
|
|
||||||
socketaddr!("127.0.0.1:1237"),
|
|
||||||
);
|
|
||||||
let mut crdt = Crdt::new(node_info).unwrap();
|
let mut crdt = Crdt::new(node_info).unwrap();
|
||||||
let network_entry_point = NodeInfo::new_entry_point(&socketaddr!("127.0.0.1:1239"));
|
let network_entry_point = NodeInfo::new_entry_point(&socketaddr!("127.0.0.1:1239"));
|
||||||
crdt.insert(&network_entry_point);
|
crdt.insert(&network_entry_point);
|
||||||
|
|
|
@ -737,13 +737,7 @@ mod test {
|
||||||
blobs.push(b_);
|
blobs.push(b_);
|
||||||
}
|
}
|
||||||
|
|
||||||
let d = crdt::NodeInfo::new(
|
let d = crdt::NodeInfo::new_localhost(Keypair::new().pubkey());
|
||||||
Keypair::new().pubkey(),
|
|
||||||
"127.0.0.1:1234".parse().unwrap(),
|
|
||||||
"127.0.0.1:1235".parse().unwrap(),
|
|
||||||
"127.0.0.1:1236".parse().unwrap(),
|
|
||||||
"127.0.0.1:1237".parse().unwrap(),
|
|
||||||
);
|
|
||||||
assert!(index_blobs(&d, &blobs, &mut (offset as u64)).is_ok());
|
assert!(index_blobs(&d, &blobs, &mut (offset as u64)).is_ok());
|
||||||
for b in blobs {
|
for b in blobs {
|
||||||
let idx = b.read().get_index().unwrap() as usize % WINDOW_SIZE;
|
let idx = b.read().get_index().unwrap() as usize % WINDOW_SIZE;
|
||||||
|
|
|
@ -43,6 +43,7 @@ pub mod recorder;
|
||||||
pub mod recvmmsg;
|
pub mod recvmmsg;
|
||||||
pub mod recycler;
|
pub mod recycler;
|
||||||
pub mod replicate_stage;
|
pub mod replicate_stage;
|
||||||
|
pub mod replicator;
|
||||||
pub mod request;
|
pub mod request;
|
||||||
pub mod request_processor;
|
pub mod request_processor;
|
||||||
pub mod request_stage;
|
pub mod request_stage;
|
||||||
|
@ -54,6 +55,8 @@ pub mod service;
|
||||||
pub mod signature;
|
pub mod signature;
|
||||||
pub mod sigverify;
|
pub mod sigverify;
|
||||||
pub mod sigverify_stage;
|
pub mod sigverify_stage;
|
||||||
|
pub mod storage_program;
|
||||||
|
pub mod store_ledger_stage;
|
||||||
pub mod streamer;
|
pub mod streamer;
|
||||||
pub mod system_program;
|
pub mod system_program;
|
||||||
pub mod thin_client;
|
pub mod thin_client;
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
use blob_fetch_stage::BlobFetchStage;
|
||||||
|
use crdt::{Crdt, Node, NodeInfo};
|
||||||
|
use ncp::Ncp;
|
||||||
|
use service::Service;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::net::UdpSocket;
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::mpsc::channel;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
use std::thread::JoinHandle;
|
||||||
|
use store_ledger_stage::StoreLedgerStage;
|
||||||
|
use streamer::BlobReceiver;
|
||||||
|
use window;
|
||||||
|
use window_service::window_service;
|
||||||
|
|
||||||
|
pub struct Replicator {
|
||||||
|
ncp: Ncp,
|
||||||
|
fetch_stage: BlobFetchStage,
|
||||||
|
store_ledger_stage: StoreLedgerStage,
|
||||||
|
t_window: JoinHandle<()>,
|
||||||
|
pub retransmit_receiver: BlobReceiver,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Replicator {
|
||||||
|
pub fn new(
|
||||||
|
entry_height: u64,
|
||||||
|
exit: &Arc<AtomicBool>,
|
||||||
|
ledger_path: Option<&str>,
|
||||||
|
node: Node,
|
||||||
|
network_addr: Option<SocketAddr>,
|
||||||
|
) -> Replicator {
|
||||||
|
let window = window::new_window_from_entries(&[], entry_height, &node.info);
|
||||||
|
let shared_window = Arc::new(RwLock::new(window));
|
||||||
|
|
||||||
|
let crdt = Arc::new(RwLock::new(Crdt::new(node.info).expect("Crdt::new")));
|
||||||
|
|
||||||
|
let leader_info = network_addr.map(|i| NodeInfo::new_entry_point(&i));
|
||||||
|
|
||||||
|
if let Some(leader_info) = leader_info.as_ref() {
|
||||||
|
crdt.write().unwrap().insert(leader_info);
|
||||||
|
} else {
|
||||||
|
panic!("No leader info!");
|
||||||
|
}
|
||||||
|
|
||||||
|
let repair_socket = Arc::new(node.sockets.repair);
|
||||||
|
let mut blob_sockets: Vec<Arc<UdpSocket>> =
|
||||||
|
node.sockets.replicate.into_iter().map(Arc::new).collect();
|
||||||
|
blob_sockets.push(repair_socket.clone());
|
||||||
|
let (fetch_stage, blob_fetch_receiver) =
|
||||||
|
BlobFetchStage::new_multi_socket(blob_sockets, exit.clone());
|
||||||
|
|
||||||
|
let (blob_window_sender, blob_window_receiver) = channel();
|
||||||
|
// todo: pull blobs off the retransmit_receiver and recycle them?
|
||||||
|
let (retransmit_sender, retransmit_receiver) = channel();
|
||||||
|
let t_window = window_service(
|
||||||
|
crdt.clone(),
|
||||||
|
shared_window.clone(),
|
||||||
|
entry_height,
|
||||||
|
blob_fetch_receiver,
|
||||||
|
blob_window_sender,
|
||||||
|
retransmit_sender,
|
||||||
|
repair_socket,
|
||||||
|
);
|
||||||
|
|
||||||
|
let store_ledger_stage = StoreLedgerStage::new(blob_window_receiver, ledger_path);
|
||||||
|
|
||||||
|
let ncp = Ncp::new(
|
||||||
|
&crdt,
|
||||||
|
shared_window.clone(),
|
||||||
|
ledger_path,
|
||||||
|
node.sockets.gossip,
|
||||||
|
exit.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Replicator {
|
||||||
|
ncp,
|
||||||
|
fetch_stage,
|
||||||
|
store_ledger_stage,
|
||||||
|
t_window,
|
||||||
|
retransmit_receiver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn join(self) {
|
||||||
|
self.ncp.join().unwrap();
|
||||||
|
self.t_window.join().unwrap();
|
||||||
|
self.fetch_stage.join().unwrap();
|
||||||
|
self.store_ledger_stage.join().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use client::mk_client;
|
||||||
|
use crdt::Node;
|
||||||
|
use fullnode::Fullnode;
|
||||||
|
use ledger::{genesis, read_ledger};
|
||||||
|
use logger;
|
||||||
|
use replicator::Replicator;
|
||||||
|
use signature::{Keypair, KeypairUtil};
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread::sleep;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_replicator_startup() {
|
||||||
|
logger::setup();
|
||||||
|
info!("starting replicator test");
|
||||||
|
let entry_height = 0;
|
||||||
|
let replicator_ledger_path = "replicator_test_replicator_ledger";
|
||||||
|
|
||||||
|
let exit = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
|
let leader_ledger_path = "replicator_test_leader_ledger";
|
||||||
|
let (mint, leader_ledger_path) = genesis(leader_ledger_path, 100);
|
||||||
|
|
||||||
|
info!("starting leader node");
|
||||||
|
let leader_keypair = Keypair::new();
|
||||||
|
let leader_node = Node::new_localhost_with_pubkey(leader_keypair.pubkey());
|
||||||
|
let network_addr = leader_node.sockets.gossip.local_addr().unwrap();
|
||||||
|
let leader_info = leader_node.info.clone();
|
||||||
|
let leader_rotation_interval = 20;
|
||||||
|
let leader = Fullnode::new(
|
||||||
|
leader_node,
|
||||||
|
&leader_ledger_path,
|
||||||
|
leader_keypair,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
Some(leader_rotation_interval),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut leader_client = mk_client(&leader_info);
|
||||||
|
|
||||||
|
let bob = Keypair::new();
|
||||||
|
|
||||||
|
let last_id = leader_client.get_last_id();
|
||||||
|
leader_client
|
||||||
|
.transfer(1, &mint.keypair(), bob.pubkey(), &last_id)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let replicator_keypair = Keypair::new();
|
||||||
|
|
||||||
|
info!("starting replicator node");
|
||||||
|
let replicator_node = Node::new_localhost_with_pubkey(replicator_keypair.pubkey());
|
||||||
|
let replicator = Replicator::new(
|
||||||
|
entry_height,
|
||||||
|
&exit,
|
||||||
|
Some(replicator_ledger_path),
|
||||||
|
replicator_node,
|
||||||
|
Some(network_addr),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut num_entries = 0;
|
||||||
|
for _ in 0..10 {
|
||||||
|
match read_ledger(replicator_ledger_path, true) {
|
||||||
|
Ok(entries) => {
|
||||||
|
for _ in entries {
|
||||||
|
num_entries += 1;
|
||||||
|
}
|
||||||
|
info!("{} entries", num_entries);
|
||||||
|
if num_entries > 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
info!("error reading ledger: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sleep(Duration::new(1, 0));
|
||||||
|
let last_id = leader_client.get_last_id();
|
||||||
|
leader_client
|
||||||
|
.transfer(1, &mint.keypair(), bob.pubkey(), &last_id)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
assert!(num_entries > 0);
|
||||||
|
exit.store(true, Ordering::Relaxed);
|
||||||
|
replicator.join();
|
||||||
|
leader.exit();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
//! storage program
|
||||||
|
//! Receive mining proofs from miners, validate the answers
|
||||||
|
//! and give reward for good proofs.
|
||||||
|
|
||||||
|
use bank::Account;
|
||||||
|
use bincode::deserialize;
|
||||||
|
use signature::Pubkey;
|
||||||
|
use transaction::Transaction;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub enum StorageProgram {
|
||||||
|
SubmitMiningProof { sha_state: [u8; 32] },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const STORAGE_PROGRAM_ID: [u8; 32] = [1u8; 32];
|
||||||
|
|
||||||
|
impl StorageProgram {
|
||||||
|
pub fn check_id(program_id: &Pubkey) -> bool {
|
||||||
|
program_id.as_ref() == STORAGE_PROGRAM_ID
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id() -> Pubkey {
|
||||||
|
Pubkey::new(&STORAGE_PROGRAM_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_balance(account: &Account) -> i64 {
|
||||||
|
account.tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_transaction(tx: &Transaction, _accounts: &mut [Account]) {
|
||||||
|
let syscall: StorageProgram = deserialize(&tx.userdata).unwrap();
|
||||||
|
match syscall {
|
||||||
|
StorageProgram::SubmitMiningProof { sha_state } => {
|
||||||
|
info!("Mining proof submitted with state {}", sha_state[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {}
|
|
@ -0,0 +1,73 @@
|
||||||
|
//! The `store_ledger` stores the ledger from received blobs for storage nodes
|
||||||
|
|
||||||
|
use counter::Counter;
|
||||||
|
use ledger::{reconstruct_entries_from_blobs, LedgerWriter};
|
||||||
|
use log::Level;
|
||||||
|
use result::{Error, Result};
|
||||||
|
use service::Service;
|
||||||
|
use std::sync::atomic::AtomicUsize;
|
||||||
|
use std::sync::mpsc::RecvTimeoutError;
|
||||||
|
use std::thread::{self, Builder, JoinHandle};
|
||||||
|
use std::time::Duration;
|
||||||
|
use streamer::BlobReceiver;
|
||||||
|
|
||||||
|
pub struct StoreLedgerStage {
|
||||||
|
thread_hdls: Vec<JoinHandle<()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StoreLedgerStage {
|
||||||
|
/// Process entry blobs, already in order
|
||||||
|
fn store_requests(
|
||||||
|
window_receiver: &BlobReceiver,
|
||||||
|
ledger_writer: Option<&mut LedgerWriter>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let timer = Duration::new(1, 0);
|
||||||
|
let mut blobs = window_receiver.recv_timeout(timer)?;
|
||||||
|
while let Ok(mut more) = window_receiver.try_recv() {
|
||||||
|
blobs.append(&mut more);
|
||||||
|
}
|
||||||
|
let entries = reconstruct_entries_from_blobs(blobs.clone())?;
|
||||||
|
|
||||||
|
inc_new_counter_info!(
|
||||||
|
"store-transactions",
|
||||||
|
entries.iter().map(|x| x.transactions.len()).sum()
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(ledger_writer) = ledger_writer {
|
||||||
|
ledger_writer.write_entries(entries)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(window_receiver: BlobReceiver, ledger_path: Option<&str>) -> Self {
|
||||||
|
let mut ledger_writer = ledger_path.map(|p| LedgerWriter::open(p, true).unwrap());
|
||||||
|
|
||||||
|
let t_store_requests = Builder::new()
|
||||||
|
.name("solana-store-ledger-stage".to_string())
|
||||||
|
.spawn(move || loop {
|
||||||
|
if let Err(e) = Self::store_requests(&window_receiver, ledger_writer.as_mut()) {
|
||||||
|
match e {
|
||||||
|
Error::RecvTimeoutError(RecvTimeoutError::Disconnected) => break,
|
||||||
|
Error::RecvTimeoutError(RecvTimeoutError::Timeout) => (),
|
||||||
|
_ => error!("{:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
|
let thread_hdls = vec![t_store_requests];
|
||||||
|
|
||||||
|
StoreLedgerStage { thread_hdls }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Service for StoreLedgerStage {
|
||||||
|
type JoinReturnType = ();
|
||||||
|
|
||||||
|
fn join(self) -> thread::Result<()> {
|
||||||
|
for thread_hdl in self.thread_hdls {
|
||||||
|
thread_hdl.join()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -133,7 +133,7 @@ impl WriteStage {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
for _ in 0..ventries.len() {
|
for _ in 0..ventries.len() {
|
||||||
let entries = ventries.pop().unwrap();
|
let entries = ventries.pop().unwrap();
|
||||||
for e in entries.iter() {
|
for e in &entries {
|
||||||
num_txs += e.transactions.len();
|
num_txs += e.transactions.len();
|
||||||
}
|
}
|
||||||
let crdt_votes_start = Instant::now();
|
let crdt_votes_start = Instant::now();
|
||||||
|
|
Loading…
Reference in New Issue