Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 27 additions & 8 deletions node/src/components/chainspec_handler/chainspec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,49 @@ use serde::{Deserialize, Serialize};
use types::{account::AccountHash, U512};

use super::{config, Error};
use crate::{components::contract_runtime::shared::wasm_costs::WasmCosts, types::Motes};
use crate::{
components::contract_runtime::shared::wasm_costs::WasmCosts,
crypto::{asymmetric_key::PublicKey, hash::hash},
types::Motes,
};

/// An account that exists at genesis.
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct GenesisAccount {
account_hash: AccountHash,
public_key: Option<PublicKey>,
balance: Motes,
bonded_amount: Motes,
}

impl GenesisAccount {
/// Constructs a new `GenesisAccount`.
/// Constructs a new `GenesisAccount` with no public key.
pub fn new(account_hash: AccountHash, balance: Motes, bonded_amount: Motes) -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this constructor still exist? When would we know an account hash, but not the corresponding public key?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When constructing the system account in the contract runtime. Specifically, we need it here: https://github.com/CasperLabs/casperlabs-node/blob/master/node/src/components/contract_runtime/core/engine_state/mod.rs#L419

GenesisAccount {
public_key: None,
account_hash,
balance,
bonded_amount,
}
}

/// Constructs a new `GenesisAccount` with a given public key.
pub fn with_public_key(public_key: PublicKey, balance: Motes, bonded_amount: Motes) -> Self {
// TODO: include the PK variant when hashing
let account_hash = AccountHash::new(hash(public_key).to_bytes());
GenesisAccount {
public_key: Some(public_key),
account_hash,
balance,
bonded_amount,
}
}

/// Returns the account's public key.
pub fn public_key(&self) -> Option<PublicKey> {
self.public_key
}

/// Returns the account's hash.
pub fn account_hash(&self) -> AccountHash {
self.account_hash
Expand All @@ -58,11 +81,13 @@ impl GenesisAccount {

impl Distribution<GenesisAccount> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> GenesisAccount {
let public_key = None;
let account_hash = AccountHash::new(rng.gen());
let balance = Motes::new(U512(rng.gen()));
let bonded_amount = Motes::new(U512(rng.gen()));

GenesisAccount {
public_key,
account_hash,
balance,
bonded_amount,
Expand Down Expand Up @@ -182,8 +207,6 @@ mod tests {

use tempfile::NamedTempFile;

use types::account::ACCOUNT_HASH_LENGTH;

use super::*;

const TEST_ROOT: &str = "resources/test/valid";
Expand Down Expand Up @@ -245,10 +268,6 @@ mod tests {

assert_eq!(spec.genesis.accounts.len(), 4);
for index in 0..4 {
assert_eq!(
spec.genesis.accounts[index].account_hash.value(),
[index as u8 + 1; ACCOUNT_HASH_LENGTH]
);
assert_eq!(
spec.genesis.accounts[index].balance,
Motes::new(U512::from(index + 1))
Expand Down
35 changes: 10 additions & 25 deletions node/src/components/chainspec_handler/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ use csv::ReaderBuilder;
use semver::Version;
use serde::{Deserialize, Serialize};

use types::{
account::{AccountHash, ACCOUNT_HASH_LENGTH},
U512,
};
use types::U512;

use super::{chainspec, Error};
use crate::{components::contract_runtime::shared::wasm_costs::WasmCosts, types::Motes};
use crate::{
components::contract_runtime::shared::wasm_costs::WasmCosts, crypto::asymmetric_key::PublicKey,
types::Motes,
};

const DEFAULT_CHAIN_NAME: &str = "casperlabs-devnet";
const DEFAULT_MINT_INSTALLER_PATH: &str = "mint_install.wasm";
Expand Down Expand Up @@ -308,7 +308,8 @@ pub(super) fn parse_toml<P: AsRef<Path>>(chainspec_path: P) -> Result<chainspec:
fn parse_accounts<P: AsRef<Path>>(file: P) -> Result<Vec<chainspec::GenesisAccount>, Error> {
#[derive(Debug, Deserialize)]
struct ParsedAccount {
account_hash: String,
public_key: String,
algorithm: String,
balance: String,
bonded_amount: String,
}
Expand All @@ -319,8 +320,9 @@ fn parse_accounts<P: AsRef<Path>>(file: P) -> Result<Vec<chainspec::GenesisAccou
let parsed: ParsedAccount = result?;
let balance = Motes::new(U512::from_dec_str(&parsed.balance)?);
let bonded_amount = Motes::new(U512::from_dec_str(&parsed.bonded_amount)?);
let account = chainspec::GenesisAccount::new(
AccountHash::new(string_to_array(parsed.account_hash)?),
let key_bytes = hex::decode(parsed.public_key)?;
let account = chainspec::GenesisAccount::with_public_key(
PublicKey::key_from_algorithm_name_and_bytes(&parsed.algorithm, key_bytes)?,
balance,
bonded_amount,
);
Expand All @@ -329,23 +331,6 @@ fn parse_accounts<P: AsRef<Path>>(file: P) -> Result<Vec<chainspec::GenesisAccou
Ok(accounts)
}

/// Parses a string into a 32-byte array. The string must be either hex or base64 encoded.
fn string_to_array(input: String) -> Result<[u8; ACCOUNT_HASH_LENGTH], Error> {
let decoded = if input.len() == 2 * ACCOUNT_HASH_LENGTH {
hex::decode(input)?
} else {
let decoded = base64::decode(input)?;
if decoded.len() != ACCOUNT_HASH_LENGTH {
return Err(Error::InvalidHashLength(decoded.len()));
}
decoded
};

let mut array = [0; ACCOUNT_HASH_LENGTH];
array.copy_from_slice(decoded.as_slice());
Ok(array)
}

#[cfg(test)]
mod tests {
use std::path::PathBuf;
Expand Down
8 changes: 4 additions & 4 deletions node/src/components/chainspec_handler/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ pub enum Error {
#[error("decoding from hex error: {0}")]
DecodingFromHex(#[from] hex::FromHexError),

/// Error while decoding a genesis account's key hash from base-64 format.
#[error("decoding from base-64 error: {0}")]
DecodingFromBase64(#[from] base64::DecodeError),

/// Error while decoding Motes from a decimal format.
#[error("decoding motes from base-10 error: {0}")]
DecodingMotes(#[from] FromDecStrErr),

/// Error while decoding a genesis account's key hash from base-64 format.
#[error("crypto module error: {0}")]
Crypto(#[from] crate::crypto::Error),

/// Decoding a genesis account's key hash yielded an invalid length byte array.
#[error("expected hash length of {}, got {0}", ACCOUNT_HASH_LENGTH)]
InvalidHashLength(usize),
Expand Down
12 changes: 12 additions & 0 deletions node/src/crypto/asymmetric_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use super::Result;

const ED25519_TAG: u8 = 0;
const ED25519: &str = "Ed25519";
const ED25519_LOWERCASE: &str = "ed25519";

/// A secret or private asymmetric key.
#[derive(Serialize, Deserialize)]
Expand Down Expand Up @@ -72,6 +73,17 @@ impl PublicKey {
Ok(PublicKey::Ed25519(ed25519::PublicKey::from_bytes(&bytes)?))
}

/// Constructs a new key from the algorithm name and a byte slice.
pub fn key_from_algorithm_name_and_bytes<N: AsRef<str>, T: AsRef<[u8]>>(
name: N,
bytes: T,
) -> Result<Self> {
match &*name.as_ref().trim().to_lowercase() {
ED25519_LOWERCASE => Self::ed25519_from_bytes(bytes),
_ => panic!("Invalid algorithm name!"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could just return an error here couldn't we? It'd need a new variant in the crypto::Error I suppose.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should - it means that a config file is invalid. Does it make sense to treat it as a recoverable error?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK - I see your point. I guess we can change it later if we find this method is used by other callers which need non-panicking behaviour.

}
}

/// Constructs a new Ed25519 variant from a byte slice.
pub fn ed25519_from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self> {
Ok(PublicKey::Ed25519(ed25519::PublicKey::from_bytes(
Expand Down
10 changes: 5 additions & 5 deletions resources/production/accounts.csv
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
SLx/2wN11ID70D5390/+3DC585VEVf4E2hWEOgpq8Mc=,100000000000000000,0
H2bqYyGkipNfZul9T35g7i1/yczGLfvjEPM7SDn8Yus=,1000000000000000,400000000000000
iedEeDwtcJAqXy73joLh9EECtesIymI0JB2V5Q9hWms=,1000000000000000,300000000000000
VptB1XTEY5AhLWmGYLUyYmndsKdh0SlCWIl6xxe0lYs=,1000000000000000,200000000000000
0oZSZmPKN2bIB4FUOhSMY18jiL/hKJgcPkrGnOqI3DU=,1000000000000000,100000000000001
48bc7fdb0375d480fbd03e77f74ffedc30b9f3954455fe04da15843a0a6af0c7,ed25519,100000000000000000,0
1f66ea6321a48a935f66e97d4f7e60ee2d7fc9ccc62dfbe310f33b4839fc62eb,ed25519,1000000000000000,400000000000000
89e744783c2d70902a5f2ef78e82e1f44102b5eb08ca6234241d95e50f615a6b,ed25519,1000000000000000,300000000000000
569b41d574c46390212d698660b5326269ddb0a761d1294258897ac717b4958b,ed25519,1000000000000000,200000000000000
d286526663ca3766c80781543a148c635f2388bfe128981c3e4ac69cea88dc35,ed25519,1000000000000000,100000000000001
8 changes: 4 additions & 4 deletions resources/test/valid/accounts.csv
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=,1,10
AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI=,2,20
0303030303030303030303030303030303030303030303030303030303030303,3,30
0404040404040404040404040404040404040404040404040404040404040404,4,40
48bc7fdb0375d480fbd03e77f74ffedc30b9f3954455fe04da15843a0a6af0c7,ed25519,1,10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess if we convert the algo name to lowercase when parsing, we could have some of these "ed25519" instances with uppercase?

1f66ea6321a48a935f66e97d4f7e60ee2d7fc9ccc62dfbe310f33b4839fc62eb,Ed25519,2,20
89e744783c2d70902a5f2ef78e82e1f44102b5eb08ca6234241d95e50f615a6b,eD25519,3,30
569b41d574c46390212d698660b5326269ddb0a761d1294258897ac717b4958b,ED25519,4,40