Skip to content

Commit 3217a5e

Browse files
authored
Merge pull request #460 from pragma-org/abailly/tesnet-cluster
feat: run a basic testnet cluster with docker
2 parents 5350b9e + d18cb1d commit 3217a5e

139 files changed

Lines changed: 6181 additions & 130 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/amaru-kernel/src/network.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ impl std::fmt::Display for NetworkName {
460460
Self::Mainnet => write!(f, "mainnet"),
461461
Self::Preprod => write!(f, "preprod"),
462462
Self::Preview => write!(f, "preview"),
463-
Self::Testnet(magic) => write!(f, "testnet:{}", magic),
463+
Self::Testnet(magic) => write!(f, "testnet_{}", magic),
464464
}
465465
}
466466
}
@@ -475,7 +475,7 @@ impl std::str::FromStr for NetworkName {
475475
"preview" => Ok(Self::Preview),
476476
_ => {
477477
let magic = s
478-
.strip_prefix("testnet:")
478+
.strip_prefix("testnet_")
479479
.ok_or(format!("Invalid network name {}", s))?;
480480

481481
magic

crates/amaru-ledger/src/bootstrap.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ use amaru_kernel::{
2424
DRep, DRepRegistration, DRepState, Epoch, EraHistory, Lovelace, MemoizedTransactionOutput,
2525
Point, PoolId, PoolParams, Proposal, ProposalId, ProposalPointer, ProposalState, Reward,
2626
ScriptHash, Set, Slot, StakeCredential, StrictMaybe, TransactionInput, TransactionPointer,
27-
UnitInterval, Vote, Voter, cbor, heterogeneous_array, network::NetworkName,
28-
protocol_parameters::ProtocolParameters,
27+
UnitInterval, Vote, Voter, cbor, heterogeneous_array, protocol_parameters::ProtocolParameters,
2928
};
3029
use amaru_progress_bar::ProgressBar;
3130
use std::{
@@ -63,7 +62,7 @@ pub fn import_initial_snapshot(
6362
db: &(impl Store + 'static),
6463
bytes: &[u8],
6564
point: &Point,
66-
network: NetworkName,
65+
era_history: &EraHistory,
6766
// A way to notify progress while importing. The second argument is a template argument, which
6867
// follows the format described in:
6968
//
@@ -74,8 +73,6 @@ pub fn import_initial_snapshot(
7473
// Assumes the presence of fully computed rewards when set.
7574
has_rewards: bool,
7675
) -> Result<Epoch, Box<dyn std::error::Error>> {
77-
let era_history = <&EraHistory>::from(network);
78-
7976
let mut d = cbor::Decoder::new(bytes);
8077

8178
d.array()?;

crates/amaru-network/src/chain_sync_client.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
use crate::point::to_network_point;
15+
use crate::point::{from_network_point, to_network_point};
1616
use amaru_kernel::{Point, peer::Peer};
1717
use pallas_network::miniprotocols::chainsync::{Client, ClientError, HeaderContent, NextResponse};
1818
use pallas_traverse::MultiEraHeader;
@@ -64,7 +64,7 @@ impl ChainSyncClient {
6464
intersection.slot = %self.intersection.last().map(|p| p.slot_or_default()).unwrap_or_default(),
6565
),
6666
)]
67-
pub async fn find_intersection(&mut self) -> Result<(), ChainSyncClientError> {
67+
pub async fn find_intersection(&mut self) -> Result<Point, ChainSyncClientError> {
6868
let client = &mut self.chain_sync;
6969
let (point, _) = client
7070
.find_intersect(
@@ -77,10 +77,10 @@ impl ChainSyncClient {
7777
.await
7878
.map_err(ChainSyncClientError::NetworkError)?;
7979

80-
point.ok_or(ChainSyncClientError::NoIntersectionFound {
80+
let intersection = point.ok_or(ChainSyncClientError::NoIntersectionFound {
8181
points: self.intersection.clone(),
8282
})?;
83-
Ok(())
83+
Ok(from_network_point(&intersection))
8484
}
8585

8686
pub fn intersection(&self) -> &[Point] {

crates/amaru-ouroboros/src/praos/header.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ pub fn assert_all<'a>(
123123
}),
124124
Box::new(move || {
125125
AssertVrfProofError::new(
126-
&vrf::Input::new(absolute_slot, epoch_nonce),
126+
absolute_slot,
127+
epoch_nonce,
127128
&header.header_body.leader_vrf_output()[..],
128129
&vrf::PublicKey::from(declared_vrf_key),
129130
&header.header_body.vrf_result,
@@ -201,7 +202,7 @@ pub enum AssertVrfProofError {
201202
MalformedProof(#[from] vrf::ProofFromBytesError),
202203

203204
#[error("Invalid VRF proof: {0}")]
204-
InvalidProof(#[from] vrf::ProofVerifyError),
205+
InvalidProof(vrf::ProofVerifyError, Slot, Hash<32>, Vec<u8>),
205206

206207
#[error("could not convert slice to array")]
207208
TryFromSliceError,
@@ -239,7 +240,9 @@ impl PartialEq for AssertVrfProofError {
239240
fn eq(&self, other: &Self) -> bool {
240241
match (self, other) {
241242
(Self::MalformedProof(l0), Self::MalformedProof(r0)) => l0 == r0,
242-
(Self::InvalidProof(l0), Self::InvalidProof(r0)) => l0 == r0,
243+
(Self::InvalidProof(l0, l1, l2, l3), Self::InvalidProof(r0, r1, r2, r3)) => {
244+
l0 == r0 && l1 == r1 && l2 == r2 && l3 == r3
245+
}
243246
(Self::TryFromSliceError, Self::TryFromSliceError) => true,
244247
(
245248
Self::ProofMismatch {
@@ -269,11 +272,13 @@ impl PartialEq for AssertVrfProofError {
269272
impl AssertVrfProofError {
270273
/// Assert that the VRF output from the block and its corresponding hash.
271274
pub fn new(
272-
input: &vrf::Input,
275+
absolute_slot: Slot,
276+
epoch_nonce: &Hash<32>,
273277
output: &[u8],
274278
leader_public_key: &vrf::PublicKey,
275279
certificate: &VrfCert,
276280
) -> Result<(), Self> {
281+
let input = &vrf::Input::new(absolute_slot, epoch_nonce);
277282
// TODO: Pallas should have fixed size slices here.
278283
let block_proof_hash: [u8; vrf::Proof::HASH_SIZE] = {
279284
let bytes: &[u8] = certificate.0.as_ref();
@@ -288,7 +293,14 @@ impl AssertVrfProofError {
288293

289294
// Verify the VRF proof
290295
let vrf_proof = vrf::Proof::try_from(&block_proof)?;
291-
let proof_hash = vrf_proof.verify(leader_public_key, input)?;
296+
let proof_hash = vrf_proof.verify(leader_public_key, input).map_err(|e| {
297+
Self::InvalidProof(
298+
e,
299+
absolute_slot,
300+
*epoch_nonce,
301+
leader_public_key.as_ref().to_vec(),
302+
)
303+
})?;
292304
if proof_hash.as_slice() != block_proof_hash {
293305
return Err(Self::ProofMismatch {
294306
declared: Box::new(block_proof_hash),

crates/amaru/src/bin/amaru/cmd/bootstrap.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,9 @@
1313
// limitations under the License.
1414

1515
use super::{
16-
import_headers::import_headers_for_network,
17-
import_ledger_state::import_all_from_directory,
18-
import_nonces::{InitialNonces, import_nonces},
16+
import_headers::import_headers_for_network, import_ledger_state::import_all_from_directory,
1917
};
20-
use crate::cmd::DEFAULT_NETWORK;
18+
use crate::cmd::{DEFAULT_NETWORK, import_nonces::import_nonces_from_file};
2119
use amaru::snapshots_dir;
2220
use amaru_kernel::{default_chain_dir, default_ledger_dir, network::NetworkName};
2321
use async_compression::tokio::bufread::GzipDecoder;
@@ -49,7 +47,7 @@ pub struct Args {
4947

5048
/// Network to bootstrap the node for.
5149
///
52-
/// Should be one of 'mainnet', 'preprod', 'preview' or 'testnet:<magic>' where
50+
/// Should be one of 'mainnet', 'preprod', 'preview' or 'testnet_<magic>' where
5351
/// `magic` is a 32-bits unsigned value denoting a particular testnet.
5452
#[arg(
5553
long,
@@ -114,9 +112,7 @@ async fn import_nonces_for_network(
114112
chain_dir: &PathBuf,
115113
) -> Result<(), Box<dyn Error>> {
116114
let nonces_file: PathBuf = config_dir.join("nonces.json");
117-
let content = tokio::fs::read_to_string(nonces_file).await?;
118-
let initial_nonces: InitialNonces = serde_json::from_str(&content)?;
119-
import_nonces(network.into(), chain_dir, initial_nonces).await?;
115+
import_nonces_from_file(network, &nonces_file, chain_dir).await?;
120116
Ok(())
121117
}
122118

crates/amaru/src/bin/amaru/cmd/convert_ledger_state.rs

Lines changed: 63 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,22 @@
1313
// limitations under the License.
1414

1515
use amaru_kernel::{
16-
Bound, EraHistory, EraParams, HEADER_HASH_SIZE, Hash, Nonce, Summary, cbor,
16+
Bound, EraHistory, EraParams, HEADER_HASH_SIZE, Hash, Nonce, Point, Summary, cbor,
1717
network::NetworkName,
1818
};
19-
use amaru_ouroboros_traits::Nonces;
2019
use clap::Parser;
2120
use std::path::{Path, PathBuf};
2221
use tokio::fs::{self};
22+
use tracing::{debug, info};
23+
24+
use crate::cmd::import_nonces::InitialNonces;
2325

2426
#[derive(Debug, Parser)]
2527
pub struct Args {
2628
/// Path to the CBOR encoded ledger state snapshot as serialised by Haskell
2729
/// node.
28-
#[arg(long, value_name = "SNAPSHOT", verbatim_doc_comment, num_args(0..))]
29-
snapshot: Vec<PathBuf>,
30+
#[arg(long, value_name = "SNAPSHOT", verbatim_doc_comment)]
31+
snapshot: PathBuf,
3032

3133
/// Directory to store converted snapshots into.
3234
///
@@ -55,10 +57,7 @@ pub enum Error {
5557

5658
pub(crate) async fn run(args: Args) -> Result<(), Box<dyn std::error::Error>> {
5759
let target_dir = args.target_dir.unwrap_or(PathBuf::from("."));
58-
for snapshot in args.snapshot {
59-
convert_one_snapshot_file(&target_dir, &snapshot, &args.network).await?;
60-
}
61-
60+
convert_one_snapshot_file(&target_dir, &args.snapshot, &args.network).await?;
6261
Ok(())
6362
}
6463

@@ -76,7 +75,12 @@ async fn convert_one_snapshot_file(
7675

7776
fs::create_dir_all(target_dir).await?;
7877

79-
convert_snapshot_to(snapshot, target_dir, network).await
78+
let converted = convert_snapshot_to(snapshot, target_dir, network).await?;
79+
info!(
80+
"converted ledger state from {:?} to {:?}",
81+
snapshot, converted
82+
);
83+
Ok(converted)
8084
}
8185

8286
async fn convert_snapshot_to(
@@ -118,7 +122,8 @@ async fn convert_snapshot_to(
118122
slot_length: 1000,
119123
},
120124
});
121-
let history = EraHistory::new(&eras, network.default_stability_window());
125+
126+
let era_history = EraHistory::new(&eras, network.default_stability_window());
122127

123128
// ledger state
124129
// https://github.com/abailly/ouroboros-consensus/blob/1508638f832772d21874e18e48b908fcb791cd49/ouroboros-consensus-cardano/src/shelley/Ouroboros/Consensus/Shelley/Ledger/Ledger.hs#L736
@@ -160,7 +165,7 @@ async fn convert_snapshot_to(
160165
d.array()?;
161166
// NOTE: The encoding of an AnnTip is not consistent with the encoding of a Tip
162167
let tip_slot = d.u64()?;
163-
let _tip_hash: Hash<HEADER_HASH_SIZE> = d.decode()?;
168+
let tip_hash: Hash<HEADER_HASH_SIZE> = d.decode()?;
164169
let _tip_height = d.u64()?;
165170

166171
// ChainDepState for Praos
@@ -210,26 +215,37 @@ async fn convert_snapshot_to(
210215
d.u8()?;
211216
let active: Nonce = d.decode()?;
212217

213-
d.array()?;
214-
d.u8()?;
215-
let tail = d.decode()?;
218+
// lab nonce
219+
d.skip()?;
216220

217-
// previous epoch nonce, can be defined or 0
218-
// FIXME: do we use it?
221+
// last epoch nonce
219222
d.skip()?;
220223

221-
let nonces = Nonces {
224+
let nonces = InitialNonces {
225+
at: Point::Specific(tip_slot, tip_hash.to_vec()),
222226
active,
223227
evolving,
224228
candidate,
225-
epoch: history.slot_to_epoch(tip_slot.into(), tip_slot.into())?,
226-
tail,
229+
tail: Hash::new([0; 32]),
227230
};
228231

229232
write_nonces(target_dir, slot, hash, nonces).await?;
233+
write_era_history(target_dir, slot, hash, &era_history).await?;
230234
write_ledger_snapshot(target_dir, slot, hash, &bytes[begin..end]).await
231235
}
232236

237+
async fn write_era_history(
238+
target_dir: &Path,
239+
slot: u64,
240+
hash: Hash<32>,
241+
era_history: &EraHistory,
242+
) -> Result<(), Box<dyn std::error::Error>> {
243+
let target_path = target_dir.join(format!("history.{}.{}.json", slot, hash));
244+
fs::write(&target_path, serde_json::to_string(era_history)?).await?;
245+
debug!("wrote era history {:?}", target_path);
246+
Ok(())
247+
}
248+
233249
/// This is the number of past eras before the current era in the "standard" Cardano history, e.g
234250
/// from Byron to Babbage. Bump this number when a hard fork happens.
235251
pub const PAST_ERAS_NUMBER: i32 = 6;
@@ -287,10 +303,11 @@ async fn write_nonces(
287303
target_dir: &Path,
288304
slot: u64,
289305
hash: Hash<HEADER_HASH_SIZE>,
290-
nonces: Nonces,
306+
nonces: InitialNonces,
291307
) -> Result<(), Box<dyn std::error::Error>> {
292308
let target_path = target_dir.join(format!("nonces.{}.{}.json", slot, hash));
293309
fs::write(&target_path, serde_json::to_string(&nonces)?).await?;
310+
debug!("wrote nonces file {:?}", target_path);
294311
Ok(())
295312
}
296313

@@ -302,6 +319,7 @@ async fn write_ledger_snapshot(
302319
) -> Result<PathBuf, Box<dyn std::error::Error>> {
303320
let target_path = target_dir.join(format!("{}.{}.cbor", slot, hash));
304321
fs::write(&target_path, ledger_data).await?;
322+
debug!("wrote ledger snapshot {:?}", target_path);
305323
Ok(target_path)
306324
}
307325

@@ -326,7 +344,7 @@ mod test {
326344
}
327345

328346
#[tokio::test]
329-
async fn generates_converted_snapshots_in_given_target_dir() {
347+
async fn generates_converted_snapshot_in_given_target_dir() {
330348
let network = NetworkName::Testnet(42);
331349
let tempdir = tempfile::tempdir().unwrap();
332350
let expected_paths = vec![
@@ -341,15 +359,19 @@ mod test {
341359
),
342360
];
343361

344-
let args = super::Args {
345-
snapshot: dir_content(Path::new("tests/data/convert")).await.unwrap(),
346-
target_dir: Some(tempdir.path().to_path_buf()),
347-
network,
348-
};
362+
let snapshots = dir_content(Path::new("tests/data/convert")).await.unwrap();
349363

350-
run(args)
351-
.await
352-
.expect("unexpected error in conversion test");
364+
for snapshot in snapshots {
365+
let args = super::Args {
366+
snapshot,
367+
target_dir: Some(tempdir.path().to_path_buf()),
368+
network,
369+
};
370+
371+
run(args)
372+
.await
373+
.expect("unexpected error in conversion test");
374+
}
353375

354376
assert!(
355377
expected_paths.iter().all(|p| p.exists()),
@@ -389,15 +411,19 @@ mod test {
389411
),
390412
];
391413

392-
let args = super::Args {
393-
snapshot: dir_content(Path::new("tests/data/convert")).await.unwrap(),
394-
target_dir: Some(target_dir.clone()),
395-
network,
396-
};
414+
let snapshots = dir_content(Path::new("tests/data/convert")).await.unwrap();
397415

398-
run(args)
399-
.await
400-
.expect("unexpected error in conversion test");
416+
for snapshot in snapshots {
417+
let args = super::Args {
418+
snapshot,
419+
target_dir: Some(target_dir.clone()),
420+
network,
421+
};
422+
423+
run(args)
424+
.await
425+
.expect("unexpected error in conversion test");
426+
}
401427

402428
assert!(
403429
expected_paths.iter().all(|p| p.exists()),

crates/amaru/src/bin/amaru/cmd/daemon.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub struct Args {
3333

3434
/// The target network to choose from.
3535
///
36-
/// Should be one of 'mainnet', 'preprod', 'preview' or 'testnet:<magic>' where
36+
/// Should be one of 'mainnet', 'preprod', 'preview' or 'testnet_<magic>' where
3737
/// `magic` is a 32-bits unsigned value denoting a particular testnet.
3838
#[arg(
3939
long,

crates/amaru/src/bin/amaru/cmd/dump_chain_db.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use std::{error::Error, path::PathBuf};
2626
pub struct Args {
2727
/// Network for which we are importing headers.
2828
///
29-
/// Should be one of 'mainnet', 'preprod', 'preview' or 'testnet:<magic>' where
29+
/// Should be one of 'mainnet', 'preprod', 'preview' or 'testnet_<magic>' where
3030
/// `magic` is a 32-bits unsigned value denoting a particular testnet.
3131
#[arg(
3232
long,

0 commit comments

Comments
 (0)