Run RustFmt as part of the CI (#37)

* Run RustFmt as part of the CI

* Format repo

* Run RustFmt before the default Travis build step

Apparently if you override `script` you also need to make
sure to `build` and `test` the code yourself.

* Format repo
This commit is contained in:
Hernando Castano
2020-03-19 13:35:48 -04:00
committed by Bastian Köcher
parent d904a282c8
commit e5f998d7d9
21 changed files with 832 additions and 633 deletions
+35 -32
View File
@@ -30,18 +30,13 @@
// You should have received a copy of the GNU General Public License
// along with Parity-Bridge. If not, see <http://www.gnu.org/licenses/>.
use crate::ethereum_sync_loop::MaybeConnectionError;
use crate::ethereum_types::{Header, HeaderId, Receipt, H256, U64};
use jsonrpsee::common::Params;
use jsonrpsee::raw::{
RawClient,
RawClientError,
};
use jsonrpsee::transport::http::{
HttpTransportClient, RequestError
};
use jsonrpsee::raw::{RawClient, RawClientError};
use jsonrpsee::transport::http::{HttpTransportClient, RequestError};
use serde::de::DeserializeOwned;
use serde_json::{from_value, to_value};
use crate::ethereum_sync_loop::MaybeConnectionError;
use crate::ethereum_types::{H256, Header, HeaderId, Receipt, U64};
/// Proof of hash serialization success.
const HASH_SERIALIZATION_PROOF: &'static str = "hash serialization never fails; qed";
@@ -87,11 +82,7 @@ pub fn client(uri: &str) -> Client {
/// Retrieve best known block number from Ethereum node.
pub async fn best_block_number(client: Client) -> (Client, Result<u64, Error>) {
let (client, result) = call_rpc::<U64>(
client,
"eth_blockNumber",
Params::None,
).await;
let (client, result) = call_rpc::<U64>(client, "eth_blockNumber", Params::None).await;
(client, result.map(|x| x.as_u64()))
}
@@ -104,11 +95,17 @@ pub async fn header_by_number(client: Client, number: u64) -> (Client, Result<He
to_value(U64::from(number)).expect(INT_SERIALIZATION_PROOF),
to_value(false).expect(BOOL_SERIALIZATION_PROOF),
]),
).await;
(client, header.and_then(|header: Header| match header.number.is_some() && header.hash.is_some() {
true => Ok(header),
false => Err(Error::IncompleteHeader),
}))
)
.await;
(
client,
header.and_then(
|header: Header| match header.number.is_some() && header.hash.is_some() {
true => Ok(header),
false => Err(Error::IncompleteHeader),
},
),
)
}
/// Retrieve block header by its hash from Ethereum node.
@@ -120,11 +117,17 @@ pub async fn header_by_hash(client: Client, hash: H256) -> (Client, Result<Heade
to_value(hash).expect(HASH_SERIALIZATION_PROOF),
to_value(false).expect(BOOL_SERIALIZATION_PROOF),
]),
).await;
(client, header.and_then(|header: Header| match header.number.is_none() && header.hash.is_none() {
true => Ok(header),
false => Err(Error::IncompleteHeader),
}))
)
.await;
(
client,
header.and_then(
|header: Header| match header.number.is_none() && header.hash.is_none() {
true => Ok(header),
false => Err(Error::IncompleteHeader),
},
),
)
}
/// Retrieve transactions receipts for given block.
@@ -151,16 +154,16 @@ async fn transaction_receipt(client: Client, hash: H256) -> (Client, Result<Rece
let (client, receipt) = call_rpc::<Receipt>(
client,
"eth_getTransactionReceipt",
Params::Array(vec![
to_value(hash).expect(HASH_SERIALIZATION_PROOF),
]),
).await;
(client, receipt.and_then(|receipt| {
match receipt.gas_used.is_some() {
Params::Array(vec![to_value(hash).expect(HASH_SERIALIZATION_PROOF)]),
)
.await;
(
client,
receipt.and_then(|receipt| match receipt.gas_used.is_some() {
true => Ok(receipt),
false => Err(Error::IncompleteReceipt),
}
}))
}),
)
}
/// Calls RPC on Ethereum node.
+293 -70
View File
@@ -30,12 +30,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity-Bridge. If not, see <http://www.gnu.org/licenses/>.
use crate::ethereum_types::{Header, HeaderId, HeaderStatus, QueuedHeader, Receipt, H256};
use std::collections::{
BTreeMap, HashMap, HashSet,
btree_map::Entry as BTreeMapEntry,
hash_map::Entry as HashMapEntry,
btree_map::Entry as BTreeMapEntry, hash_map::Entry as HashMapEntry, BTreeMap, HashMap, HashSet,
};
use crate::ethereum_types::{H256, Header, HeaderId, HeaderStatus, QueuedHeader, Receipt};
type HeadersQueue = BTreeMap<u64, HashMap<H256, QueuedHeader>>;
type KnownHeaders = BTreeMap<u64, HashMap<H256, HeaderStatus>>;
@@ -79,9 +77,15 @@ impl QueuedHeaders {
pub fn headers_in_status(&self, status: HeaderStatus) -> usize {
match status {
HeaderStatus::Unknown | HeaderStatus::Synced => return 0,
HeaderStatus::MaybeOrphan => self.maybe_orphan.values().fold(0, |total, headers| total + headers.len()),
HeaderStatus::MaybeOrphan => self
.maybe_orphan
.values()
.fold(0, |total, headers| total + headers.len()),
HeaderStatus::Orphan => self.orphan.values().fold(0, |total, headers| total + headers.len()),
HeaderStatus::MaybeReceipts => self.maybe_receipts.values().fold(0, |total, headers| total + headers.len()),
HeaderStatus::MaybeReceipts => self
.maybe_receipts
.values()
.fold(0, |total, headers| total + headers.len()),
HeaderStatus::Receipts => self.receipts.values().fold(0, |total, headers| total + headers.len()),
HeaderStatus::Ready => self.ready.values().fold(0, |total, headers| total + headers.len()),
HeaderStatus::Submitted => self.submitted.values().fold(0, |total, headers| total + headers.len()),
@@ -90,9 +94,14 @@ impl QueuedHeaders {
/// Returns number of headers that are currently in the queue.
pub fn total_headers(&self) -> usize {
self.maybe_orphan.values().fold(0, |total, headers| total + headers.len())
self.maybe_orphan
.values()
.fold(0, |total, headers| total + headers.len())
+ self.orphan.values().fold(0, |total, headers| total + headers.len())
+ self.maybe_receipts.values().fold(0, |total, headers| total + headers.len())
+ self
.maybe_receipts
.values()
.fold(0, |total, headers| total + headers.len())
+ self.receipts.values().fold(0, |total, headers| total + headers.len())
+ self.ready.values().fold(0, |total, headers| total + headers.len())
}
@@ -171,16 +180,19 @@ impl QueuedHeaders {
HeaderStatus::Unknown | HeaderStatus::MaybeOrphan => {
insert_header(&mut self.maybe_orphan, id, header);
HeaderStatus::MaybeOrphan
},
}
HeaderStatus::Orphan => {
insert_header(&mut self.orphan, id, header);
HeaderStatus::Orphan
}
HeaderStatus::MaybeReceipts | HeaderStatus::Receipts | HeaderStatus::Ready
| HeaderStatus::Submitted | HeaderStatus::Synced => {
HeaderStatus::MaybeReceipts
| HeaderStatus::Receipts
| HeaderStatus::Ready
| HeaderStatus::Submitted
| HeaderStatus::Synced => {
insert_header(&mut self.maybe_receipts, id, header);
HeaderStatus::MaybeReceipts
},
}
};
self.known_headers.entry(id.0).or_default().insert(id.1, status);
@@ -202,10 +214,12 @@ impl QueuedHeaders {
HeaderStatus::Ready => remove_header(&mut self.ready, &current),
HeaderStatus::Submitted => remove_header(&mut self.submitted, &current),
HeaderStatus::Synced => break,
}.expect("header has a given status; given queue has the header; qed");
}
.expect("header has a given status; given queue has the header; qed");
log::debug!(target: "bridge", "Ethereum header {:?} is now {:?}", current, HeaderStatus::Synced);
*self.known_headers
*self
.known_headers
.entry(current.0)
.or_default()
.entry(current.1)
@@ -215,7 +229,8 @@ impl QueuedHeaders {
// remember that the header is synced
log::debug!(target: "bridge", "Ethereum header {:?} is now {:?}", id, HeaderStatus::Synced);
*self.known_headers
*self
.known_headers
.entry(id.0)
.or_default()
.entry(id.1)
@@ -303,7 +318,7 @@ impl QueuedHeaders {
if prune_border <= self.prune_border {
return;
}
prune_queue(&mut self.maybe_orphan, prune_border);
prune_queue(&mut self.orphan, prune_border);
prune_queue(&mut self.maybe_receipts, prune_border);
@@ -402,7 +417,10 @@ fn move_header_descendants(
if current_parents.contains(&entry.get().header().parent_hash) {
let header_to_move = entry.remove();
let header_to_move_id = header_to_move.id();
known_headers.entry(header_to_move_id.0).or_default().insert(header_to_move_id.1, destination_status);
known_headers
.entry(header_to_move_id.0)
.or_default()
.insert(header_to_move_id.1, destination_status);
headers_to_move.push((header_to_move_id, header_to_move));
log::debug!(
@@ -438,7 +456,8 @@ fn oldest_header(queue: &HeadersQueue) -> Option<&QueuedHeader> {
/// Return oldest headers from the queue until functor will return false.
fn oldest_headers(queue: &HeadersQueue, mut f: impl FnMut(&QueuedHeader) -> bool) -> Option<Vec<&QueuedHeader>> {
let result = queue.values()
let result = queue
.values()
.flat_map(|h| h.values())
.take_while(|h| f(h))
.collect::<Vec<_>>();
@@ -490,11 +509,27 @@ pub(crate) mod tests {
fn total_headers_works() {
// total headers just sums up number of headers in every queue
let mut queue = QueuedHeaders::default();
queue.maybe_orphan.entry(1).or_default().insert(hash(1), Default::default());
queue.maybe_orphan.entry(1).or_default().insert(hash(2), Default::default());
queue.maybe_orphan.entry(2).or_default().insert(hash(3), Default::default());
queue
.maybe_orphan
.entry(1)
.or_default()
.insert(hash(1), Default::default());
queue
.maybe_orphan
.entry(1)
.or_default()
.insert(hash(2), Default::default());
queue
.maybe_orphan
.entry(2)
.or_default()
.insert(hash(3), Default::default());
queue.orphan.entry(3).or_default().insert(hash(4), Default::default());
queue.maybe_receipts.entry(4).or_default().insert(hash(5), Default::default());
queue
.maybe_receipts
.entry(4)
.or_default()
.insert(hash(5), Default::default());
queue.ready.entry(5).or_default().insert(hash(6), Default::default());
assert_eq!(queue.total_headers(), 6);
}
@@ -503,21 +538,41 @@ pub(crate) mod tests {
fn best_queued_number_works() {
// initially there are headers in MaybeOrphan queue only
let mut queue = QueuedHeaders::default();
queue.maybe_orphan.entry(1).or_default().insert(hash(1), Default::default());
queue.maybe_orphan.entry(1).or_default().insert(hash(2), Default::default());
queue.maybe_orphan.entry(3).or_default().insert(hash(3), Default::default());
queue
.maybe_orphan
.entry(1)
.or_default()
.insert(hash(1), Default::default());
queue
.maybe_orphan
.entry(1)
.or_default()
.insert(hash(2), Default::default());
queue
.maybe_orphan
.entry(3)
.or_default()
.insert(hash(3), Default::default());
assert_eq!(queue.best_queued_number(), 3);
// and then there's better header in Orphan
queue.orphan.entry(10).or_default().insert(hash(10), Default::default());
assert_eq!(queue.best_queued_number(), 10);
// and then there's better header in MaybeReceipts
queue.maybe_receipts.entry(20).or_default().insert(hash(20), Default::default());
queue
.maybe_receipts
.entry(20)
.or_default()
.insert(hash(20), Default::default());
assert_eq!(queue.best_queued_number(), 20);
// and then there's better header in Ready
queue.ready.entry(30).or_default().insert(hash(30), Default::default());
assert_eq!(queue.best_queued_number(), 30);
// and then there's better header in MaybeOrphan again
queue.maybe_orphan.entry(40).or_default().insert(hash(40), Default::default());
queue
.maybe_orphan
.entry(40)
.or_default()
.insert(hash(40), Default::default());
assert_eq!(queue.best_queued_number(), 40);
}
@@ -527,7 +582,11 @@ pub(crate) mod tests {
let mut queue = QueuedHeaders::default();
assert_eq!(queue.status(&id(10)), HeaderStatus::Unknown);
// and status is read from the KnownHeaders
queue.known_headers.entry(10).or_default().insert(hash(10), HeaderStatus::Ready);
queue
.known_headers
.entry(10)
.or_default()
.insert(hash(10), HeaderStatus::Ready);
assert_eq!(queue.status(&id(10)), HeaderStatus::Ready);
}
@@ -536,50 +595,83 @@ pub(crate) mod tests {
// initially we have oldest header #10
let mut queue = QueuedHeaders::default();
queue.maybe_orphan.entry(10).or_default().insert(hash(1), header(100));
assert_eq!(queue.header(HeaderStatus::MaybeOrphan).unwrap().header().hash.unwrap(), hash(100));
assert_eq!(
queue.header(HeaderStatus::MaybeOrphan).unwrap().header().hash.unwrap(),
hash(100)
);
// inserting #20 changes nothing
queue.maybe_orphan.entry(20).or_default().insert(hash(1), header(101));
assert_eq!(queue.header(HeaderStatus::MaybeOrphan).unwrap().header().hash.unwrap(), hash(100));
assert_eq!(
queue.header(HeaderStatus::MaybeOrphan).unwrap().header().hash.unwrap(),
hash(100)
);
// inserting #5 makes it oldest
queue.maybe_orphan.entry(5).or_default().insert(hash(1), header(102));
assert_eq!(queue.header(HeaderStatus::MaybeOrphan).unwrap().header().hash.unwrap(), hash(102));
assert_eq!(
queue.header(HeaderStatus::MaybeOrphan).unwrap().header().hash.unwrap(),
hash(102)
);
}
#[test]
fn header_response_works() {
// when parent is Synced, we insert to MaybeReceipts
let mut queue = QueuedHeaders::default();
queue.known_headers.entry(100).or_default().insert(hash(100), HeaderStatus::Synced);
queue
.known_headers
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::Synced);
queue.header_response(header(101).header().clone());
assert_eq!(queue.status(&id(101)), HeaderStatus::MaybeReceipts);
// when parent is Ready, we insert to MaybeReceipts
let mut queue = QueuedHeaders::default();
queue.known_headers.entry(100).or_default().insert(hash(100), HeaderStatus::Ready);
queue
.known_headers
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::Ready);
queue.header_response(header(101).header().clone());
assert_eq!(queue.status(&id(101)), HeaderStatus::MaybeReceipts);
// when parent is Receipts, we insert to MaybeReceipts
let mut queue = QueuedHeaders::default();
queue.known_headers.entry(100).or_default().insert(hash(100), HeaderStatus::Receipts);
queue
.known_headers
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::Receipts);
queue.header_response(header(101).header().clone());
assert_eq!(queue.status(&id(101)), HeaderStatus::MaybeReceipts);
// when parent is MaybeReceipts, we insert to MaybeReceipts
let mut queue = QueuedHeaders::default();
queue.known_headers.entry(100).or_default().insert(hash(100), HeaderStatus::MaybeReceipts);
queue
.known_headers
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::MaybeReceipts);
queue.header_response(header(101).header().clone());
assert_eq!(queue.status(&id(101)), HeaderStatus::MaybeReceipts);
// when parent is Orphan, we insert to Orphan
let mut queue = QueuedHeaders::default();
queue.known_headers.entry(100).or_default().insert(hash(100), HeaderStatus::Orphan);
queue
.known_headers
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::Orphan);
queue.header_response(header(101).header().clone());
assert_eq!(queue.status(&id(101)), HeaderStatus::Orphan);
// when parent is MaybeOrphan, we insert to MaybeOrphan
let mut queue = QueuedHeaders::default();
queue.known_headers.entry(100).or_default().insert(hash(100), HeaderStatus::MaybeOrphan);
queue
.known_headers
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::MaybeOrphan);
queue.header_response(header(101).header().clone());
assert_eq!(queue.status(&id(101)), HeaderStatus::MaybeOrphan);
@@ -599,15 +691,39 @@ pub(crate) mod tests {
// #97 in Receipts
// #96 in Ready
let mut queue = QueuedHeaders::default();
queue.known_headers.entry(100).or_default().insert(hash(100), HeaderStatus::MaybeOrphan);
queue.maybe_orphan.entry(100).or_default().insert(hash(100), header(100));
queue.known_headers.entry(99).or_default().insert(hash(99), HeaderStatus::Orphan);
queue
.known_headers
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::MaybeOrphan);
queue
.maybe_orphan
.entry(100)
.or_default()
.insert(hash(100), header(100));
queue
.known_headers
.entry(99)
.or_default()
.insert(hash(99), HeaderStatus::Orphan);
queue.orphan.entry(99).or_default().insert(hash(99), header(99));
queue.known_headers.entry(98).or_default().insert(hash(98), HeaderStatus::MaybeReceipts);
queue
.known_headers
.entry(98)
.or_default()
.insert(hash(98), HeaderStatus::MaybeReceipts);
queue.maybe_receipts.entry(98).or_default().insert(hash(98), header(98));
queue.known_headers.entry(97).or_default().insert(hash(97), HeaderStatus::Receipts);
queue
.known_headers
.entry(97)
.or_default()
.insert(hash(97), HeaderStatus::Receipts);
queue.receipts.entry(97).or_default().insert(hash(97), header(97));
queue.known_headers.entry(96).or_default().insert(hash(96), HeaderStatus::Ready);
queue
.known_headers
.entry(96)
.or_default()
.insert(hash(96), HeaderStatus::Ready);
queue.ready.entry(96).or_default().insert(hash(96), header(96));
queue.substrate_best_header_response(&id(100));
@@ -618,7 +734,10 @@ pub(crate) mod tests {
assert!(queue.receipts.is_empty());
assert!(queue.ready.is_empty());
assert_eq!(queue.known_headers.len(), 5);
assert!(queue.known_headers.values().all(|s| s.values().all(|s| *s == HeaderStatus::Synced)));
assert!(queue
.known_headers
.values()
.all(|s| s.values().all(|s| *s == HeaderStatus::Synced)));
}
#[test]
@@ -629,11 +748,27 @@ pub(crate) mod tests {
// #102 in MaybeOrphan
// #103 in Orphan
let mut queue = QueuedHeaders::default();
queue.known_headers.entry(101).or_default().insert(hash(101), HeaderStatus::Orphan);
queue
.known_headers
.entry(101)
.or_default()
.insert(hash(101), HeaderStatus::Orphan);
queue.orphan.entry(101).or_default().insert(hash(101), header(101));
queue.known_headers.entry(102).or_default().insert(hash(102), HeaderStatus::MaybeOrphan);
queue.maybe_orphan.entry(102).or_default().insert(hash(102), header(102));
queue.known_headers.entry(103).or_default().insert(hash(103), HeaderStatus::Orphan);
queue
.known_headers
.entry(102)
.or_default()
.insert(hash(102), HeaderStatus::MaybeOrphan);
queue
.maybe_orphan
.entry(102)
.or_default()
.insert(hash(102), header(102));
queue
.known_headers
.entry(103)
.or_default()
.insert(hash(103), HeaderStatus::Orphan);
queue.orphan.entry(103).or_default().insert(hash(103), header(103));
queue.substrate_best_header_response(&id(100));
@@ -655,12 +790,32 @@ pub(crate) mod tests {
// and we have asked for MaybeOrphan status of #100.parent (i.e. #99)
// and the response is: YES, #99 is known to the Substrate runtime
let mut queue = QueuedHeaders::default();
queue.known_headers.entry(100).or_default().insert(hash(100), HeaderStatus::MaybeOrphan);
queue.maybe_orphan.entry(100).or_default().insert(hash(100), header(100));
queue.known_headers.entry(101).or_default().insert(hash(101), HeaderStatus::Orphan);
queue
.known_headers
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::MaybeOrphan);
queue
.maybe_orphan
.entry(100)
.or_default()
.insert(hash(100), header(100));
queue
.known_headers
.entry(101)
.or_default()
.insert(hash(101), HeaderStatus::Orphan);
queue.orphan.entry(101).or_default().insert(hash(101), header(101));
queue.known_headers.entry(102).or_default().insert(hash(102), HeaderStatus::MaybeOrphan);
queue.maybe_orphan.entry(102).or_default().insert(hash(102), header(102));
queue
.known_headers
.entry(102)
.or_default()
.insert(hash(102), HeaderStatus::MaybeOrphan);
queue
.maybe_orphan
.entry(102)
.or_default()
.insert(hash(102), header(102));
queue.maybe_orphan_response(&id(99), true);
// then all headers (#100..#103) are moved to the MaybeReceipts queue
@@ -680,10 +835,26 @@ pub(crate) mod tests {
// and we have asked for MaybeOrphan status of #100.parent (i.e. #99)
// and the response is: NO, #99 is NOT known to the Substrate runtime
let mut queue = QueuedHeaders::default();
queue.known_headers.entry(100).or_default().insert(hash(100), HeaderStatus::MaybeOrphan);
queue.maybe_orphan.entry(100).or_default().insert(hash(100), header(100));
queue.known_headers.entry(101).or_default().insert(hash(101), HeaderStatus::MaybeOrphan);
queue.maybe_orphan.entry(101).or_default().insert(hash(101), header(101));
queue
.known_headers
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::MaybeOrphan);
queue
.maybe_orphan
.entry(100)
.or_default()
.insert(hash(100), header(100));
queue
.known_headers
.entry(101)
.or_default()
.insert(hash(101), HeaderStatus::MaybeOrphan);
queue
.maybe_orphan
.entry(101)
.or_default()
.insert(hash(101), header(101));
queue.maybe_orphan_response(&id(99), false);
// then all headers (#100..#101) are moved to the Orphan queue
@@ -696,8 +867,16 @@ pub(crate) mod tests {
#[test]
fn positive_maybe_receipts_response_works() {
let mut queue = QueuedHeaders::default();
queue.known_headers.entry(100).or_default().insert(hash(100), HeaderStatus::MaybeReceipts);
queue.maybe_receipts.entry(100).or_default().insert(hash(100), header(100));
queue
.known_headers
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::MaybeReceipts);
queue
.maybe_receipts
.entry(100)
.or_default()
.insert(hash(100), header(100));
queue.maybe_receipts_response(&id(100), true);
assert!(queue.maybe_receipts.is_empty());
assert_eq!(queue.receipts.len(), 1);
@@ -707,8 +886,16 @@ pub(crate) mod tests {
#[test]
fn negative_maybe_receipts_response_works() {
let mut queue = QueuedHeaders::default();
queue.known_headers.entry(100).or_default().insert(hash(100), HeaderStatus::MaybeReceipts);
queue.maybe_receipts.entry(100).or_default().insert(hash(100), header(100));
queue
.known_headers
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::MaybeReceipts);
queue
.maybe_receipts
.entry(100)
.or_default()
.insert(hash(100), header(100));
queue.maybe_receipts_response(&id(100), false);
assert!(queue.maybe_receipts.is_empty());
assert_eq!(queue.ready.len(), 1);
@@ -718,7 +905,11 @@ pub(crate) mod tests {
#[test]
fn receipts_response_works() {
let mut queue = QueuedHeaders::default();
queue.known_headers.entry(100).or_default().insert(hash(100), HeaderStatus::Receipts);
queue
.known_headers
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::Receipts);
queue.receipts.entry(100).or_default().insert(hash(100), header(100));
queue.receipts_response(&id(100), Vec::new());
assert!(queue.receipts.is_empty());
@@ -729,7 +920,11 @@ pub(crate) mod tests {
#[test]
fn header_submitted_works() {
let mut queue = QueuedHeaders::default();
queue.known_headers.entry(100).or_default().insert(hash(100), HeaderStatus::Ready);
queue
.known_headers
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::Ready);
queue.ready.entry(100).or_default().insert(hash(100), header(100));
queue.headers_submitted(vec![id(100)]);
assert!(queue.ready.is_empty());
@@ -739,15 +934,43 @@ pub(crate) mod tests {
#[test]
fn prune_works() {
let mut queue = QueuedHeaders::default();
queue.known_headers.entry(104).or_default().insert(hash(104), HeaderStatus::MaybeOrphan);
queue.maybe_orphan.entry(104).or_default().insert(hash(104), header(104));
queue.known_headers.entry(103).or_default().insert(hash(103), HeaderStatus::Orphan);
queue
.known_headers
.entry(104)
.or_default()
.insert(hash(104), HeaderStatus::MaybeOrphan);
queue
.maybe_orphan
.entry(104)
.or_default()
.insert(hash(104), header(104));
queue
.known_headers
.entry(103)
.or_default()
.insert(hash(103), HeaderStatus::Orphan);
queue.orphan.entry(103).or_default().insert(hash(103), header(103));
queue.known_headers.entry(102).or_default().insert(hash(102), HeaderStatus::MaybeReceipts);
queue.maybe_receipts.entry(102).or_default().insert(hash(102), header(102));
queue.known_headers.entry(101).or_default().insert(hash(101), HeaderStatus::Receipts);
queue
.known_headers
.entry(102)
.or_default()
.insert(hash(102), HeaderStatus::MaybeReceipts);
queue
.maybe_receipts
.entry(102)
.or_default()
.insert(hash(102), header(102));
queue
.known_headers
.entry(101)
.or_default()
.insert(hash(101), HeaderStatus::Receipts);
queue.receipts.entry(101).or_default().insert(hash(101), header(101));
queue.known_headers.entry(100).or_default().insert(hash(100), HeaderStatus::Ready);
queue
.known_headers
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::Ready);
queue.ready.entry(100).or_default().insert(hash(100), header(100));
queue.prune(102);
+14 -12
View File
@@ -30,11 +30,11 @@
// You should have received a copy of the GNU General Public License
// along with Parity-Bridge. If not, see <http://www.gnu.org/licenses/>.
use codec::Encode;
use crate::ethereum_headers::QueuedHeaders;
use crate::ethereum_types::{HeaderId, HeaderStatus, QueuedHeader};
use crate::ethereum_sync_loop::EthereumSyncParams;
use crate::ethereum_types::{HeaderId, HeaderStatus, QueuedHeader};
use crate::substrate_types::{into_substrate_ethereum_header, into_substrate_ethereum_receipts};
use codec::Encode;
/// Ethereum headers synchronization context.
#[derive(Debug)]
@@ -63,7 +63,8 @@ impl HeadersSync {
/// Returns true if we have synced almost all known headers.
pub fn is_almost_synced(&self) -> bool {
match self.target_header_number {
Some(target_header_number) => self.best_header
Some(target_header_number) => self
.best_header
.map(|best| target_header_number.saturating_sub(best.0) < 4)
.unwrap_or(false),
None => true,
@@ -100,10 +101,7 @@ impl HeadersSync {
}
// we assume that there were no reorgs if we have already downloaded best header
let best_downloaded_number = std::cmp::max(
self.headers.best_queued_number(),
best_header.0,
);
let best_downloaded_number = std::cmp::max(self.headers.best_queued_number(), best_header.0);
if best_downloaded_number == target_header_number {
return None;
}
@@ -115,7 +113,9 @@ impl HeadersSync {
/// Select headers that need to be submitted to the Substrate node.
pub fn select_headers_to_submit(&self) -> Option<Vec<&QueuedHeader>> {
let headers_in_submit_status = self.headers.headers_in_status(HeaderStatus::Submitted);
let headers_to_submit_count = self.params.max_headers_in_submitted_status
let headers_to_submit_count = self
.params
.max_headers_in_submitted_status
.checked_sub(headers_in_submit_status)?;
let mut total_size = 0;
@@ -129,7 +129,8 @@ impl HeadersSync {
}
let encoded_size = into_substrate_ethereum_header(header.header()).encode().len()
+ into_substrate_ethereum_receipts(header.receipts()).map(|receipts| receipts.encode().len())
+ into_substrate_ethereum_receipts(header.receipts())
.map(|receipts| receipts.encode().len())
.unwrap_or(0);
if total_headers != 0 && total_size + encoded_size > self.params.max_headers_size_in_single_submit {
return false;
@@ -162,7 +163,8 @@ impl HeadersSync {
self.headers.substrate_best_header_response(&best_header);
// prune ancient headers
self.headers.prune(best_header.0.saturating_sub(self.params.prune_depth));
self.headers
.prune(best_header.0.saturating_sub(self.params.prune_depth));
// finally remember the best header itself
self.best_header = Some(best_header);
@@ -180,9 +182,9 @@ impl HeadersSync {
#[cfg(test)]
mod tests {
use crate::ethereum_headers::tests::{header, id};
use crate::ethereum_types::{H256, HeaderStatus};
use super::*;
use crate::ethereum_headers::tests::{header, id};
use crate::ethereum_types::{HeaderStatus, H256};
fn side_hash(number: u64) -> H256 {
H256::from_low_u64_le(1000 + number)
@@ -30,10 +30,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity-Bridge. If not, see <http://www.gnu.org/licenses/>.
use futures::{future::FutureExt, stream::StreamExt};
use crate::ethereum_client;
use crate::ethereum_types::HeaderStatus as EthereumHeaderStatus;
use crate::substrate_client;
use futures::{future::FutureExt, stream::StreamExt};
// TODO: when SharedClient will be available, switch to Substrate headers subscription
// (because we do not need old Substrate headers)
@@ -98,7 +98,10 @@ impl std::fmt::Debug for EthereumSyncParams {
.field("max_future_headers_to_download", &self.max_future_headers_to_download)
.field("max_headers_in_submitted_status", &self.max_headers_in_submitted_status)
.field("max_headers_in_single_submit", &self.max_headers_in_single_submit)
.field("max_headers_size_in_single_submit", &self.max_headers_size_in_single_submit)
.field(
"max_headers_size_in_single_submit",
&self.max_headers_size_in_single_submit,
)
.field("prune_depth", &self.prune_depth)
.finish()
}
@@ -136,9 +139,7 @@ pub fn run(params: EthereumSyncParams) {
let mut eth_maybe_client = None;
let mut eth_best_block_number_required = false;
let eth_best_block_number_future = ethereum_client::best_block_number(
ethereum_client::client(&eth_uri)
).fuse();
let eth_best_block_number_future = ethereum_client::best_block_number(ethereum_client::client(&eth_uri)).fuse();
let eth_new_header_future = futures::future::Fuse::terminated();
let eth_orphan_header_future = futures::future::Fuse::terminated();
let eth_receipts_future = futures::future::Fuse::terminated();
@@ -147,9 +148,8 @@ pub fn run(params: EthereumSyncParams) {
let mut sub_maybe_client = None;
let mut sub_best_block_required = false;
let sub_best_block_future = substrate_client::best_ethereum_block(
substrate_client::client(&sub_uri, sub_signer),
).fuse();
let sub_best_block_future =
substrate_client::best_ethereum_block(substrate_client::client(&sub_uri, sub_signer)).fuse();
let sub_receipts_check_future = futures::future::Fuse::terminated();
let sub_existence_status_future = futures::future::Fuse::terminated();
let sub_submit_header_future = futures::future::Fuse::terminated();
@@ -326,7 +326,7 @@ pub fn run(params: EthereumSyncParams) {
// 2) check transactions receipts - it stops us from downloading/submitting new blocks;
// 3) check existence - it stops us from submitting new blocks;
// 4) submit header
if sub_best_block_required {
log::debug!(target: "bridge", "Asking Substrate about best block");
sub_best_block_future.set(substrate_client::best_ethereum_block(sub_client).fuse());
@@ -338,9 +338,8 @@ pub fn run(params: EthereumSyncParams) {
);
let header = header.clone();
sub_receipts_check_future.set(
substrate_client::ethereum_receipts_required(sub_client, header).fuse()
);
sub_receipts_check_future
.set(substrate_client::ethereum_receipts_required(sub_client, header).fuse());
} else if let Some(header) = eth_sync.headers().header(EthereumHeaderStatus::MaybeOrphan) {
// for MaybeOrphan we actually ask for parent' header existence
let parent_id = header.parent_id();
@@ -351,9 +350,8 @@ pub fn run(params: EthereumSyncParams) {
parent_id,
);
sub_existence_status_future.set(
substrate_client::ethereum_header_known(sub_client, parent_id).fuse(),
);
sub_existence_status_future
.set(substrate_client::ethereum_header_known(sub_client, parent_id).fuse());
} else if let Some(headers) = eth_sync.select_headers_to_submit() {
let ids = match headers.len() {
1 => format!("{:?}", headers[0].id()),
@@ -368,9 +366,7 @@ pub fn run(params: EthereumSyncParams) {
);
let headers = headers.into_iter().cloned().collect();
sub_submit_header_future.set(
substrate_client::submit_ethereum_headers(sub_client, headers).fuse(),
);
sub_submit_header_future.set(substrate_client::submit_ethereum_headers(sub_client, headers).fuse());
// remember that we have submitted some headers
if stall_countdown.is_none() {
@@ -400,11 +396,8 @@ pub fn run(params: EthereumSyncParams) {
id,
);
eth_receipts_future.set(
ethereum_client::transactions_receipts(
eth_client,
id,
header.header().transactions.clone(),
).fuse()
ethereum_client::transactions_receipts(eth_client, id, header.header().transactions.clone())
.fuse(),
);
} else if let Some(header) = eth_sync.headers().header(EthereumHeaderStatus::Orphan) {
// for Orphan we actually ask for parent' header
@@ -416,9 +409,7 @@ pub fn run(params: EthereumSyncParams) {
parent_id,
);
eth_orphan_header_future.set(
ethereum_client::header_by_hash(eth_client, parent_id.1).fuse(),
);
eth_orphan_header_future.set(ethereum_client::header_by_hash(eth_client, parent_id.1).fuse());
} else if let Some(id) = eth_sync.select_new_header_to_download() {
log::debug!(
target: "bridge",
@@ -426,9 +417,7 @@ pub fn run(params: EthereumSyncParams) {
id,
);
eth_new_header_future.set(
ethereum_client::header_by_number(eth_client, id).fuse(),
);
eth_new_header_future.set(ethereum_client::header_by_number(eth_client, id).fuse());
} else {
eth_maybe_client = Some(eth_client);
}
@@ -469,7 +458,10 @@ async fn delay<T>(timeout_ms: u64, retval: T) -> T {
}
fn interval(timeout_ms: u64) -> impl futures::Stream<Item = ()> {
futures::stream::unfold((), move |_| async move { delay(timeout_ms, ()).await; Some(((), ())) })
futures::stream::unfold((), move |_| async move {
delay(timeout_ms, ()).await;
Some(((), ()))
})
}
fn process_future_result<TClient, TResult, TError, TGoOfflineFuture>(
@@ -488,7 +480,7 @@ fn process_future_result<TClient, TResult, TError, TGoOfflineFuture>(
Ok(result) => {
*maybe_client = Some(client);
on_success(result);
},
}
Err(error) => {
if error.is_connection_error() {
go_offline_future.set(go_offline(client).fuse());
@@ -497,6 +489,6 @@ fn process_future_result<TClient, TResult, TError, TGoOfflineFuture>(
}
log::error!(target: "bridge", "{}: {:?}", error_pattern, error);
},
}
}
}
@@ -52,7 +52,10 @@ pub struct HeaderId(pub u64, pub H256);
impl From<&Header> for HeaderId {
fn from(header: &Header) -> HeaderId {
HeaderId(header.number.expect(HEADER_ID_PROOF).as_u64(), header.hash.expect(HEADER_ID_PROOF))
HeaderId(
header.number.expect(HEADER_ID_PROOF).as_u64(),
header.hash.expect(HEADER_ID_PROOF),
)
}
}
+14 -12
View File
@@ -30,7 +30,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity-Bridge. If not, see <http://www.gnu.org/licenses/>.
#![recursion_limit="1024"]
#![recursion_limit = "1024"]
mod ethereum_client;
mod ethereum_headers;
@@ -40,8 +40,8 @@ mod ethereum_types;
mod substrate_client;
mod substrate_types;
use std::io::Write;
use sp_core::crypto::Pair;
use std::io::Write;
fn main() {
initialize();
@@ -51,7 +51,7 @@ fn main() {
Err(err) => {
log::error!(target: "bridge", "Error parsing parameters: {}", err);
return;
},
}
});
}
@@ -66,8 +66,8 @@ fn initialize() {
builder.parse_filters(&filters);
builder.format(move |buf, record| {
writeln!(buf, "{}", {
let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now())
.expect("Time is incorrectly formatted");
let timestamp =
time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).expect("Time is incorrectly formatted");
if cfg!(windows) {
format!("{} {} {} {}", timestamp, record.level(), record.target(), record.args())
} else {
@@ -79,11 +79,13 @@ fn initialize() {
log::Level::Debug => Color::Fixed(14).paint(record.level().to_string()),
log::Level::Trace => Color::Fixed(12).paint(record.level().to_string()),
};
format!("{} {} {} {}"
, Color::Fixed(8).bold().paint(timestamp)
, log_level
, Color::Fixed(8).paint(record.target())
, record.args())
format!(
"{} {} {} {}",
Color::Fixed(8).bold().paint(timestamp),
log_level,
Color::Fixed(8).paint(record.target()),
record.args()
)
}
})
});
@@ -110,8 +112,8 @@ fn ethereum_sync_params() -> Result<ethereum_sync_loop::EthereumSyncParams, Stri
}
if let Some(sub_signer) = matches.value_of("sub-signer") {
let sub_signer_password = matches.value_of("sub-signer-password");
eth_sync_params.sub_signer = sp_core::sr25519::Pair::from_string(sub_signer, sub_signer_password)
.map_err(|e| format!("{:?}", e))?;
eth_sync_params.sub_signer =
sp_core::sr25519::Pair::from_string(sub_signer, sub_signer_password).map_err(|e| format!("{:?}", e))?;
}
Ok(eth_sync_params)
+44 -82
View File
@@ -30,29 +30,16 @@
// You should have received a copy of the GNU General Public License
// along with Parity-Bridge. If not, see <http://www.gnu.org/licenses/>.
use codec::{Encode, Decode};
use crate::ethereum_sync_loop::MaybeConnectionError;
use crate::ethereum_types::{Bytes, HeaderId as EthereumHeaderId, QueuedHeader as QueuedEthereumHeader, H256};
use crate::substrate_types::{into_substrate_ethereum_header, into_substrate_ethereum_receipts, TransactionHash};
use codec::{Decode, Encode};
use jsonrpsee::common::Params;
use jsonrpsee::raw::{
RawClient, RawClientError
};
use jsonrpsee::transport::http::{
HttpTransportClient, RequestError
};
use jsonrpsee::raw::{RawClient, RawClientError};
use jsonrpsee::transport::http::{HttpTransportClient, RequestError};
use serde_json::{from_value, to_value};
use sp_core::crypto::Pair;
use sp_runtime::traits::IdentifyAccount;
use crate::ethereum_sync_loop::MaybeConnectionError;
use crate::ethereum_types::{
Bytes,
H256,
HeaderId as EthereumHeaderId,
QueuedHeader as QueuedEthereumHeader,
};
use crate::substrate_types::{
TransactionHash,
into_substrate_ethereum_header,
into_substrate_ethereum_receipts,
};
/// Substrate client type.
pub struct Client {
@@ -105,7 +92,8 @@ pub async fn best_ethereum_block(client: Client) -> (Client, Result<EthereumHead
to_value("EthereumHeadersApi_best_block").unwrap(),
to_value("0x").unwrap(),
]),
).await;
)
.await;
(client, result.map(|(num, hash)| EthereumHeaderId(num, hash)))
}
@@ -124,8 +112,12 @@ pub async fn ethereum_receipts_required(
to_value("EthereumHeadersApi_is_import_requires_receipts").unwrap(),
to_value(Bytes(encoded_header)).unwrap(),
]),
).await;
(client, receipts_required.map(|receipts_required| (id, receipts_required)))
)
.await;
(
client,
receipts_required.map(|receipts_required| (id, receipts_required)),
)
}
/// Returns true if Ethereum header is known to Substrate runtime.
@@ -147,7 +139,8 @@ pub async fn ethereum_header_known(
to_value("EthereumHeadersApi_is_known_block").unwrap(),
to_value(Bytes(encoded_id)).unwrap(),
]),
).await;
)
.await;
(client, is_known_block.map(|is_known_block| (id, is_known_block)))
}
@@ -167,7 +160,7 @@ pub async fn submit_ethereum_headers(
};
client.genesis_hash = Some(genesis_hash);
(client, genesis_hash)
},
}
};
let account_id = client.signer.public().as_array_ref().clone().into();
let (client, nonce) = next_account_index(client, account_id).await;
@@ -175,20 +168,14 @@ pub async fn submit_ethereum_headers(
Ok(nonce) => nonce,
Err(err) => return (client, Err(err)),
};
let transaction = create_submit_transaction(
headers,
&client.signer,
nonce,
genesis_hash,
);
let transaction = create_submit_transaction(headers, &client.signer, nonce, genesis_hash);
let encoded_transaction = transaction.encode();
let (client, transaction_hash) = call_rpc(
client,
"author_submitExtrinsic",
Params::Array(vec![
to_value(Bytes(encoded_transaction)).unwrap(),
]),
).await;
Params::Array(vec![to_value(Bytes(encoded_transaction)).unwrap()]),
)
.await;
(client, transaction_hash.map(|transaction_hash| (transaction_hash, ids)))
}
@@ -197,10 +184,9 @@ async fn block_hash_by_number(client: Client, number: u64) -> (Client, Result<H2
call_rpc(
client,
"chain_getBlockHash",
Params::Array(vec![
to_value(number).unwrap(),
]),
).await
Params::Array(vec![to_value(number).unwrap()]),
)
.await
}
/// Get substrate account nonce.
@@ -213,24 +199,15 @@ async fn next_account_index(
let (client, index) = call_rpc_u64(
client,
"system_accountNextIndex",
Params::Array(vec![
to_value(account.to_ss58check()).unwrap(),
]),
).await;
Params::Array(vec![to_value(account.to_ss58check()).unwrap()]),
)
.await;
(client, index.map(|index| index as _))
}
/// Calls RPC on Substrate node that returns Bytes.
async fn call_rpc<T: Decode>(
mut client: Client,
method: &'static str,
params: Params,
) -> (Client, Result<T, Error>) {
async fn do_call_rpc<T: Decode>(
client: &mut Client,
method: &'static str,
params: Params,
) -> Result<T, Error> {
async fn call_rpc<T: Decode>(mut client: Client, method: &'static str, params: Params) -> (Client, Result<T, Error>) {
async fn do_call_rpc<T: Decode>(client: &mut Client, method: &'static str, params: Params) -> Result<T, Error> {
let request_id = client
.rpc_client
.start_request(method, params)
@@ -253,16 +230,8 @@ async fn call_rpc<T: Decode>(
}
/// Calls RPC on Substrate node that returns u64.
async fn call_rpc_u64(
mut client: Client,
method: &'static str,
params: Params,
) -> (Client, Result<u64, Error>) {
async fn do_call_rpc(
client: &mut Client,
method: &'static str,
params: Params,
) -> Result<u64, Error> {
async fn call_rpc_u64(mut client: Client, method: &'static str, params: Params) -> (Client, Result<u64, Error>) {
async fn do_call_rpc(client: &mut Client, method: &'static str, params: Params) -> Result<u64, Error> {
let request_id = client
.rpc_client
.start_request(method, params)
@@ -290,20 +259,18 @@ fn create_submit_transaction(
index: node_primitives::Index,
genesis_hash: H256,
) -> bridge_node_runtime::UncheckedExtrinsic {
let function = bridge_node_runtime::Call::BridgeEthPoA(
bridge_node_runtime::BridgeEthPoACall::import_headers(
headers
.into_iter()
.map(|header| {
let (header, receipts) = header.extract();
(
into_substrate_ethereum_header(&header),
into_substrate_ethereum_receipts(&receipts),
)
})
.collect(),
),
);
let function = bridge_node_runtime::Call::BridgeEthPoA(bridge_node_runtime::BridgeEthPoACall::import_headers(
headers
.into_iter()
.map(|header| {
let (header, receipts) = header.extract();
(
into_substrate_ethereum_header(&header),
into_substrate_ethereum_receipts(&receipts),
)
})
.collect(),
));
let extra = |i: node_primitives::Index, f: node_primitives::Balance| {
(
@@ -331,10 +298,5 @@ fn create_submit_transaction(
let signer: sp_runtime::MultiSigner = signer.public().into();
let (function, extra, _) = raw_payload.deconstruct();
bridge_node_runtime::UncheckedExtrinsic::new_signed(
function,
signer.into_account().into(),
signature.into(),
extra,
)
bridge_node_runtime::UncheckedExtrinsic::new_signed(function, signer.into_account().into(), signature.into(), extra)
}
+28 -22
View File
@@ -30,17 +30,14 @@
// You should have received a copy of the GNU General Public License
// along with Parity-Bridge. If not, see <http://www.gnu.org/licenses/>.
pub use sp_bridge_eth_poa::{
Address, Bloom, Bytes, H256, Header as SubstrateEthereumHeader,
LogEntry as SubstrateEthereumLogEntry, Receipt as SubstrateEthereumReceipt,
TransactionOutcome as SubstrateEthereumTransactionOutcome, U256,
};
pub use crate::ethereum_types::H256 as TransactionHash;
use crate::ethereum_types::{
HEADER_ID_PROOF as ETHEREUM_HEADER_ID_PROOF,
Header as EthereumHeader, Receipt as EthereumReceipt, HEADER_ID_PROOF as ETHEREUM_HEADER_ID_PROOF,
RECEIPT_GAS_USED_PROOF as ETHEREUM_RECEIPT_GAS_USED_PROOF,
Header as EthereumHeader,
Receipt as EthereumReceipt,
};
pub use sp_bridge_eth_poa::{
Address, Bloom, Bytes, Header as SubstrateEthereumHeader, LogEntry as SubstrateEthereumLogEntry,
Receipt as SubstrateEthereumReceipt, TransactionOutcome as SubstrateEthereumTransactionOutcome, H256, U256,
};
/// Convert Ethereum header into Ethereum header for Substrate.
@@ -67,18 +64,27 @@ pub fn into_substrate_ethereum_header(header: &EthereumHeader) -> SubstrateEther
pub fn into_substrate_ethereum_receipts(
receipts: &Option<Vec<EthereumReceipt>>,
) -> Option<Vec<SubstrateEthereumReceipt>> {
receipts.as_ref().map(|receipts| receipts.iter().map(|receipt| SubstrateEthereumReceipt {
gas_used: receipt.gas_used.expect(ETHEREUM_RECEIPT_GAS_USED_PROOF),
log_bloom: receipt.logs_bloom.data().into(),
logs: receipt.logs.iter().map(|log_entry| SubstrateEthereumLogEntry {
address: log_entry.address,
topics: log_entry.topics.clone(),
data: log_entry.data.0.clone(),
}).collect(),
outcome: match (receipt.status, receipt.root) {
(Some(status), None) => SubstrateEthereumTransactionOutcome::StatusCode(status.as_u64() as u8),
(None, Some(root)) => SubstrateEthereumTransactionOutcome::StateRoot(root),
_ => SubstrateEthereumTransactionOutcome::Unknown,
},
}).collect())
receipts.as_ref().map(|receipts| {
receipts
.iter()
.map(|receipt| SubstrateEthereumReceipt {
gas_used: receipt.gas_used.expect(ETHEREUM_RECEIPT_GAS_USED_PROOF),
log_bloom: receipt.logs_bloom.data().into(),
logs: receipt
.logs
.iter()
.map(|log_entry| SubstrateEthereumLogEntry {
address: log_entry.address,
topics: log_entry.topics.clone(),
data: log_entry.data.0.clone(),
})
.collect(),
outcome: match (receipt.status, receipt.root) {
(Some(status), None) => SubstrateEthereumTransactionOutcome::StatusCode(status.as_u64() as u8),
(None, Some(root)) => SubstrateEthereumTransactionOutcome::StateRoot(root),
_ => SubstrateEthereumTransactionOutcome::Unknown,
},
})
.collect()
})
}
+108 -104
View File
@@ -15,22 +15,27 @@
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use crate::error::Error;
use crate::params::{Params, RPCUrlParam};
use crate::rpc::{self, SubstrateRPC};
use crate::params::{RPCUrlParam, Params};
use futures::{prelude::*, channel::{mpsc, oneshot}, future, select};
use futures::{
channel::{mpsc, oneshot},
future,
prelude::*,
select,
};
use jsonrpsee::{
raw::client::{RawClient, RawClientError, RawClientEvent, RawClientRequestId, RawClientSubscription},
transport::{
ws::{WsConnecError, WsTransportClient},
TransportClient,
ws::{WsTransportClient, WsConnecError},
},
};
use node_primitives::{Hash, Header};
use sp_core::Bytes;
use std::cell::RefCell;
use std::collections::HashMap;
use std::pin::Pin;
use sp_core::Bytes;
type ChainId = Hash;
@@ -71,24 +76,25 @@ async fn init_rpc_connection(url: &RPCUrlParam) -> Result<Chain, Error> {
let genesis_hash = rpc::genesis_block_hash(&mut client)
.await
.map_err(|e| Error::RPCError(e.to_string()))?
.ok_or_else(|| Error::InvalidChainState(format!(
"chain with RPC URL {} is missing a genesis block hash",
url_str,
)))?;
.ok_or_else(|| {
Error::InvalidChainState(format!(
"chain with RPC URL {} is missing a genesis block hash",
url_str,
))
})?;
let latest_finalized_hash = SubstrateRPC::chain_finalized_head(&mut client)
.await
.map_err(|e| Error::RPCError(e.to_string()))?;
let latest_finalized_header = SubstrateRPC::chain_header(
&mut client,
Some(latest_finalized_hash)
)
let latest_finalized_header = SubstrateRPC::chain_header(&mut client, Some(latest_finalized_hash))
.await
.map_err(|e| Error::RPCError(e.to_string()))?
.ok_or_else(|| Error::InvalidChainState(format!(
"chain {} is missing header for finalized block hash {}",
genesis_hash, latest_finalized_hash
)))?;
.ok_or_else(|| {
Error::InvalidChainState(format!(
"chain {} is missing header for finalized block hash {}",
genesis_hash, latest_finalized_hash
))
})?;
let (sender, receiver) = mpsc::channel(0);
@@ -101,41 +107,35 @@ async fn init_rpc_connection(url: &RPCUrlParam) -> Result<Chain, Error> {
state: ChainState {
current_finalized_head: latest_finalized_header,
bridges: HashMap::new(),
}
},
})
}
/// Returns IDs of the bridged chains.
async fn read_bridges(chain: &mut Chain, chain_ids: &[Hash])
-> Result<Vec<Hash>, Error>
{
async fn read_bridges(chain: &mut Chain, chain_ids: &[Hash]) -> Result<Vec<Hash>, Error> {
// This should make an RPC call to read this information from the bridge pallet state.
// For now, just pretend every chain is bridged to every other chain.
//
// TODO: The correct thing.
Ok(
chain_ids
.iter()
.cloned()
.filter(|&chain_id| chain_id != chain.genesis_hash)
.collect()
)
Ok(chain_ids
.iter()
.cloned()
.filter(|&chain_id| chain_id != chain.genesis_hash)
.collect())
}
pub async fn run_async(
params: Params,
exit: Box<dyn Future<Output=()> + Unpin + Send>
) -> Result<(), Error>
{
pub async fn run_async(params: Params, exit: Box<dyn Future<Output = ()> + Unpin + Send>) -> Result<(), Error> {
let chains = init_chains(&params).await?;
let (chain_tasks, exit_signals) = chains.into_iter()
let (chain_tasks, exit_signals) = chains
.into_iter()
.map(|(chain_id, chain_cell)| {
let chain = chain_cell.into_inner();
let (task_exit_signal, task_exit_receiver) = oneshot::channel();
let task_exit = Box::new(task_exit_receiver.map(|result| {
result.expect("task_exit_signal is not dropped before send() is called")
}));
let task_exit = Box::new(
task_exit_receiver
.map(|result| result.expect("task_exit_signal is not dropped before send() is called")),
);
let chain_task = async_std::task::spawn(async move {
if let Err(err) = chain_task(chain_id, chain, task_exit).await {
log::error!("Error in task for chain {}: {}", chain_id, err);
@@ -156,39 +156,43 @@ pub async fn run_async(
Ok(())
}
fn initial_next_events<'a>(chains: &'a HashMap<ChainId, RefCell<Chain>>)
-> Vec<Pin<Box<dyn Future<Output=Result<(ChainId, RawClientEvent), Error>> + 'a>>>
{
chains.values()
fn initial_next_events<'a>(
chains: &'a HashMap<ChainId, RefCell<Chain>>,
) -> Vec<Pin<Box<dyn Future<Output = Result<(ChainId, RawClientEvent), Error>> + 'a>>> {
chains
.values()
.map(|chain_cell| async move {
let mut chain = chain_cell.borrow_mut();
let event = chain.client.next_event()
let event = chain
.client
.next_event()
.await
.map_err(|err| Error::RPCError(err.to_string()))?;
Ok((chain.genesis_hash, event))
})
.map(|fut| Box::pin(fut) as Pin<Box<dyn Future<Output=_>>>)
.map(|fut| Box::pin(fut) as Pin<Box<dyn Future<Output = _>>>)
.collect()
}
async fn next_event<'a>(
next_events: Vec<Pin<Box<dyn Future<Output=Result<(ChainId, RawClientEvent), Error>> + 'a>>>,
next_events: Vec<Pin<Box<dyn Future<Output = Result<(ChainId, RawClientEvent), Error>> + 'a>>>,
chains: &'a HashMap<ChainId, RefCell<Chain>>,
)
-> (
Result<(Hash, RawClientEvent), Error>,
Vec<Pin<Box<dyn Future<Output=Result<(ChainId, RawClientEvent), Error>> +'a>>>
)
{
) -> (
Result<(Hash, RawClientEvent), Error>,
Vec<Pin<Box<dyn Future<Output = Result<(ChainId, RawClientEvent), Error>> + 'a>>>,
) {
let (result, _, mut rest) = future::select_all(next_events).await;
match result {
Ok((chain_id, _)) => {
let fut = async move {
let chain_cell = chains.get(&chain_id)
let chain_cell = chains
.get(&chain_id)
.expect("chain must be in the map as a function precondition; qed");
let mut chain = chain_cell.borrow_mut();
let event = chain.client.next_event()
let event = chain
.client
.next_event()
.await
.map_err(|err| Error::RPCError(err.to_string()))?;
Ok((chain_id, event))
@@ -209,9 +213,7 @@ async fn init_chains(params: &Params) -> Result<HashMap<ChainId, RefCell<Chain>>
.collect::<Result<HashMap<_, _>, _>>()?;
// TODO: Remove when read_bridges is implemented correctly.
let chain_ids = chains.keys()
.cloned()
.collect::<Vec<_>>();
let chain_ids = chains.keys().cloned().collect::<Vec<_>>();
// let chain_ids_slice = chain_ids.as_slice();
for (&chain_id, chain_cell) in chains.iter() {
@@ -229,15 +231,18 @@ async fn init_chains(params: &Params) -> Result<HashMap<ChainId, RefCell<Chain>>
let genesis_head = SubstrateRPC::chain_header(&mut chain.client, chain_id)
.await
.map_err(|e| Error::RPCError(e.to_string()))?
.ok_or_else(|| Error::InvalidChainState(format!(
"chain {} is missing a genesis block header", chain_id
)))?;
.ok_or_else(|| {
Error::InvalidChainState(format!("chain {} is missing a genesis block header", chain_id))
})?;
let channel = chain.sender.clone();
chain.state.bridges.insert(bridged_chain_id, BridgeState {
channel,
locally_finalized_head_on_bridged_chain: genesis_head,
});
chain.state.bridges.insert(
bridged_chain_id,
BridgeState {
channel,
locally_finalized_head_on_bridged_chain: genesis_head,
},
);
// The conditional ensures that we don't log twice per pair of chains.
if chain_id.as_ref() < bridged_chain_id.as_ref() {
@@ -250,28 +255,25 @@ async fn init_chains(params: &Params) -> Result<HashMap<ChainId, RefCell<Chain>>
Ok(chains)
}
async fn setup_subscriptions(chain: &mut Chain)
-> Result<(RawClientRequestId, RawClientRequestId), RawClientError<WsConnecError>>
{
let new_heads_subscription_id = chain.client
.start_subscription(
"chain_subscribeNewHeads",
jsonrpsee::common::Params::None,
)
async fn setup_subscriptions(
chain: &mut Chain,
) -> Result<(RawClientRequestId, RawClientRequestId), RawClientError<WsConnecError>> {
let new_heads_subscription_id = chain
.client
.start_subscription("chain_subscribeNewHeads", jsonrpsee::common::Params::None)
.await
.map_err(RawClientError::Inner)?;
let finalized_heads_subscription_id = chain.client
.start_subscription(
"chain_subscribeFinalizedHeads",
jsonrpsee::common::Params::None,
)
let finalized_heads_subscription_id = chain
.client
.start_subscription("chain_subscribeFinalizedHeads", jsonrpsee::common::Params::None)
.await
.map_err(RawClientError::Inner)?;
let new_heads_subscription =
chain.client.subscription_by_id(new_heads_subscription_id)
.expect("subscription_id was returned from start_subscription above; qed");
let new_heads_subscription = chain
.client
.subscription_by_id(new_heads_subscription_id)
.expect("subscription_id was returned from start_subscription above; qed");
let new_heads_subscription = match new_heads_subscription {
RawClientSubscription::Active(_) => {}
RawClientSubscription::Pending(subscription) => {
@@ -279,9 +281,10 @@ async fn setup_subscriptions(chain: &mut Chain)
}
};
let finalized_heads_subscription =
chain.client.subscription_by_id(finalized_heads_subscription_id)
.expect("subscription_id was returned from start_subscription above; qed");
let finalized_heads_subscription = chain
.client
.subscription_by_id(finalized_heads_subscription_id)
.expect("subscription_id was returned from start_subscription above; qed");
let finalized_heads_subscription = match finalized_heads_subscription {
RawClientSubscription::Active(subscription) => {}
RawClientSubscription::Pending(subscription) => {
@@ -298,27 +301,26 @@ async fn handle_rpc_event(
event: RawClientEvent,
new_heads_subscription_id: RawClientRequestId,
finalized_heads_subscription_id: RawClientRequestId,
) -> Result<(), Error>
{
) -> Result<(), Error> {
match event {
RawClientEvent::SubscriptionNotif { request_id, result } =>
RawClientEvent::SubscriptionNotif { request_id, result } => {
if request_id == new_heads_subscription_id {
let header: Header = serde_json::from_value(result)
.map_err(Error::SerializationError)?;
let header: Header = serde_json::from_value(result).map_err(Error::SerializationError)?;
log::info!("Received new head {:?} on chain {}", header, chain_id);
} else if request_id == finalized_heads_subscription_id {
let header: Header = serde_json::from_value(result)
.map_err(Error::SerializationError)?;
let header: Header = serde_json::from_value(result).map_err(Error::SerializationError)?;
log::info!("Received finalized head {:?} on chain {}", header, chain_id);
// let old_finalized_head = chain_state.current_finalized_head;
chain.state.current_finalized_head = header;
for (bridged_chain_id, bridged_chain) in chain.state.bridges.iter_mut() {
if bridged_chain.locally_finalized_head_on_bridged_chain.number <
chain.state.current_finalized_head.number {
if bridged_chain.locally_finalized_head_on_bridged_chain.number
< chain.state.current_finalized_head.number
{
// Craft and submit an extrinsic over RPC
log::info!("Sending command to submit extrinsic to chain {}", chain_id);
let mut send_event = bridged_chain.channel
let mut send_event = bridged_chain
.channel
.send(Event::SubmitExtrinsic(Bytes(Vec::new())))
.fuse();
@@ -343,12 +345,17 @@ async fn handle_rpc_event(
}
} else {
return Err(Error::RPCError(format!(
"unexpected subscription response with request ID {:?}", request_id
"unexpected subscription response with request ID {:?}",
request_id
)));
},
_ => return Err(Error::RPCError(format!(
"unexpected RPC event from chain {}: {:?}", chain_id, event
))),
}
}
_ => {
return Err(Error::RPCError(format!(
"unexpected RPC event from chain {}: {:?}",
chain_id, event
)))
}
}
Ok(())
}
@@ -358,8 +365,7 @@ async fn handle_bridge_event<R: TransportClient>(
chain_id: ChainId,
rpc_client: &mut RawClient<R>,
event: Event,
) -> Result<(), Error>
{
) -> Result<(), Error> {
match event {
Event::SubmitExtrinsic(data) => {
log::info!("Submitting extrinsic to chain {}", chain_id);
@@ -374,13 +380,11 @@ async fn handle_bridge_event<R: TransportClient>(
async fn chain_task(
chain_id: ChainId,
mut chain: Chain,
exit: impl Future<Output=()> + Unpin + Send
) -> Result<(), Error>
{
let (new_heads_subscription_id, finalized_heads_subscription_id) =
setup_subscriptions(&mut chain)
.await
.map_err(|e| Error::RPCError(e.to_string()))?;
exit: impl Future<Output = ()> + Unpin + Send,
) -> Result<(), Error> {
let (new_heads_subscription_id, finalized_heads_subscription_id) = setup_subscriptions(&mut chain)
.await
.map_err(|e| Error::RPCError(e.to_string()))?;
let mut exit = exit.fuse();
loop {
+14 -21
View File
@@ -22,8 +22,8 @@ mod rpc;
use bridge::run_async;
use params::{Params, RPCUrlParam};
use clap::{App, Arg, value_t, values_t};
use futures::{prelude::*, channel};
use clap::{value_t, values_t, App, Arg};
use futures::{channel, prelude::*};
use std::cell::Cell;
use std::process;
@@ -32,9 +32,7 @@ fn main() {
env_logger::init();
let exit = setup_exit_handler();
let result = async_std::task::block_on(async move {
run_async(params, exit).await
});
let result = async_std::task::block_on(async move { run_async(params, exit).await });
if let Err(err) = result {
log::error!("{}", err);
process::exit(1);
@@ -60,22 +58,17 @@ fn parse_args() -> Params {
.value_name("HOST[:PORT]")
.help("The URL of a bridged Substrate node")
.takes_value(true)
.multiple(true)
.multiple(true),
)
.get_matches();
let base_path = value_t!(matches, "base-path", String)
.unwrap_or_else(|e| e.exit());
let rpc_urls = values_t!(matches, "rpc-url", RPCUrlParam)
.unwrap_or_else(|e| e.exit());
let base_path = value_t!(matches, "base-path", String).unwrap_or_else(|e| e.exit());
let rpc_urls = values_t!(matches, "rpc-url", RPCUrlParam).unwrap_or_else(|e| e.exit());
Params {
base_path,
rpc_urls,
}
Params { base_path, rpc_urls }
}
fn setup_exit_handler() -> Box<dyn Future<Output=()> + Unpin + Send> {
fn setup_exit_handler() -> Box<dyn Future<Output = ()> + Unpin + Send> {
let (exit_sender, exit_receiver) = channel::oneshot::channel();
let exit_sender = Cell::new(Some(exit_sender));
@@ -86,11 +79,11 @@ fn setup_exit_handler() -> Box<dyn Future<Output=()> + Unpin + Send> {
}
}
})
.expect("must be able to set Ctrl-C handler");
.expect("must be able to set Ctrl-C handler");
Box::new(exit_receiver.map(|result| {
result.expect(
"exit_sender cannot be dropped as it is moved into a globally-referenced closure"
)
}))
Box::new(
exit_receiver.map(|result| {
result.expect("exit_sender cannot be dropped as it is moved into a globally-referenced closure")
}),
)
}
+3 -3
View File
@@ -16,8 +16,8 @@
use crate::error::Error;
use url::Url;
use std::str::FromStr;
use url::Url;
const DEFAULT_WS_PORT: u16 = 9944;
@@ -42,8 +42,8 @@ impl FromStr for RPCUrlParam {
type Err = Error;
fn from_str(url_str: &str) -> Result<Self, Self::Err> {
let mut url = Url::parse(url_str)
.map_err(|e| Error::UrlError(format!("could not parse {}: {}", url_str, e)))?;
let mut url =
Url::parse(url_str).map_err(|e| Error::UrlError(format!("could not parse {}: {}", url_str, e)))?;
if url.scheme() != "ws" {
return Err(Error::UrlError(format!("must have scheme ws, found {}", url.scheme())));
+3 -3
View File
@@ -41,8 +41,8 @@ jsonrpsee::rpc_api! {
}
}
pub async fn genesis_block_hash<R: TransportClient>(client: &mut RawClient<R>)
-> Result<Option<Hash>, RawClientError<R::Error>>
{
pub async fn genesis_block_hash<R: TransportClient>(
client: &mut RawClient<R>,
) -> Result<Option<Hash>, RawClientError<R::Error>> {
SubstrateRPC::chain_block_hash(client, Some(NumberOrHex::Number(0))).await
}