Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[fastx CLI] Add customisation to genesis #461

Merged
merged 2 commits into from
Feb 18, 2022
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
55 changes: 53 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,60 @@ This will create `sui` and `wallet` binaries in `target/release` directory.
./sui genesis
```

The genesis command creates 4 authorities, 5 user accounts each with 5 gas objects.
The genesis command creates 4 authorities, 5 user accounts each with 5 gas objects.
The network configuration are stored in `network.conf` and can be used subsequently to start the network.
A `wallet.conf` will also be generated to be used by the `wallet` binary to manage the newly created accounts.
A `wallet.conf` will also be generated to be used by the `wallet` binary to manage the newly created accounts.

### 2.1 Genesis customization

The genesis process can be customised by providing a genesis config file.

```shell
./sui genesis --config genesis.conf
```
Example `genesis.conf`
```json
{
"authorities": [
{
"key_pair": "xWhgxF5fagohi2V9jzUToxnhJbTwbtV2qX4dbMGXR7lORTBuDBe+ppFDnnHz8L/BcYHWO76EuQzUYe5pnpLsFQ==",
"host": "127.0.0.1",
"port": 10000,
"db_path": "./authorities_db/4e45306e0c17bea691439e71f3f0bfc17181d63bbe84b90cd461ee699e92ec15",
"stake": 1
}
],
"accounts": [
{
"address": "bd654f352c895d9ec14c491d3f2b4e1f98fb07323383bebe9f95ab625bff2fa0",
"gas_objects": [
{
"object_id": "5c68ac7ba66ef69fdea0651a21b531a37bf342b7",
"gas_value": 1000
}
]
}
],
"move_packages": ["<Paths to custom move packages>"],
"sui_framework_lib_path": "<Paths to sui framework lib>",
"move_framework_lib_path": "<Paths to move framework lib>"
}
```
All attributes in genesis.conf are optional, default value will be use if the attributes are not provided.
For example, the config shown below will create a network of 4 authorities, and pre-populate 2 gas objects for 4 accounts.
```json
{
"authorities": [
{},{},{},{}
],
"accounts": [
{ "gas_objects":[{},{}] },
{ "gas_objects":[{},{}] },
{ "gas_objects":[{},{}] },
{ "gas_objects":[{},{}] }
]
}
```

### 3. Starting the network

Expand Down
2 changes: 2 additions & 0 deletions sui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ move-package = { git = "https://github.com/diem/move", rev = "7683d09732dd930c58
move-core-types = { git = "https://github.com/diem/move", rev = "7683d09732dd930c581583bf5fde97fb7ac02ff7", features = ["address20"] }
move-bytecode-verifier = { git = "https://github.com/diem/move", rev = "7683d09732dd930c581583bf5fde97fb7ac02ff7" }

once_cell = "1.9.0"

[dev-dependencies]
tracing-test = "0.2.1"

Expand Down
186 changes: 182 additions & 4 deletions sui/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,29 @@
// SPDX-License-Identifier: Apache-2.0

use sui_types::base_types::*;
use sui_types::crypto::KeyPair;
use sui_types::crypto::{get_key_pair, KeyPair};

use crate::utils::Config;
use crate::utils::optional_address_as_hex;
use crate::utils::optional_address_from_hex;
use crate::utils::{Config, PortAllocator, DEFAULT_STARTING_PORT};
use anyhow::anyhow;
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::fmt::{Display, Formatter};
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use std::time::Duration;
use sui_framework::DEFAULT_FRAMEWORK_PATH;
use sui_network::transport;

const DEFAULT_WEIGHT: usize = 1;
const DEFAULT_GAS_AMOUNT: u64 = 100000;
pub const AUTHORITIES_DB_NAME: &str = "authorities_db";

static PORT_ALLOCATOR: Lazy<Mutex<PortAllocator>> =
Lazy::new(|| Mutex::new(PortAllocator::new(DEFAULT_STARTING_PORT)));

#[derive(Serialize, Deserialize)]
pub struct AccountInfo {
#[serde(serialize_with = "bytes_as_hex", deserialize_with = "bytes_from_hex")]
Expand All @@ -26,12 +40,64 @@ pub struct AuthorityInfo {
pub base_port: u16,
}

#[derive(Serialize, Deserialize)]
#[derive(Serialize)]
pub struct AuthorityPrivateInfo {
pub key_pair: KeyPair,
pub host: String,
pub port: u16,
pub db_path: PathBuf,
pub stake: usize,
}

// Custom deserializer with optional default fields
impl<'de> Deserialize<'de> for AuthorityPrivateInfo {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let (_, new_key_pair) = get_key_pair();

let json = Value::deserialize(deserializer)?;
let key_pair = if let Some(val) = json.get("key_pair") {
KeyPair::deserialize(val).map_err(serde::de::Error::custom)?
} else {
new_key_pair
};
let host = if let Some(val) = json.get("host") {
String::deserialize(val).map_err(serde::de::Error::custom)?
} else {
"127.0.0.1".to_string()
};
let port = if let Some(val) = json.get("port") {
u16::deserialize(val).map_err(serde::de::Error::custom)?
} else {
PORT_ALLOCATOR
.lock()
.map_err(serde::de::Error::custom)?
.next_port()
.ok_or_else(|| serde::de::Error::custom("No available port."))?
};
let db_path = if let Some(val) = json.get("db_path") {
PathBuf::deserialize(val).map_err(serde::de::Error::custom)?
} else {
PathBuf::from(".")
.join(AUTHORITIES_DB_NAME)
.join(encode_bytes_hex(key_pair.public_key_bytes()))
};
let stake = if let Some(val) = json.get("stake") {
usize::deserialize(val).map_err(serde::de::Error::custom)?
} else {
DEFAULT_WEIGHT
};

Ok(AuthorityPrivateInfo {
key_pair,
host,
port,
db_path,
stake,
})
}
}

#[derive(Serialize, Deserialize)]
Expand Down Expand Up @@ -85,15 +151,17 @@ impl Display for WalletConfig {
pub struct NetworkConfig {
pub authorities: Vec<AuthorityPrivateInfo>,
pub buffer_size: usize,
pub loaded_move_packages: Vec<(PathBuf, ObjectID)>,
#[serde(skip)]
config_path: PathBuf,
}

impl Config for NetworkConfig {
fn create(path: &Path) -> Result<Self, anyhow::Error> {
Ok(Self {
authorities: Vec::new(),
authorities: vec![],
buffer_size: transport::DEFAULT_MAX_DATAGRAM_SIZE.to_string().parse()?,
loaded_move_packages: vec![],
config_path: path.to_path_buf(),
})
}
Expand All @@ -106,3 +174,113 @@ impl Config for NetworkConfig {
&self.config_path
}
}

#[derive(Serialize, Deserialize, Default)]
#[serde(default)]
pub struct GenesisConfig {
pub authorities: Vec<AuthorityPrivateInfo>,
pub accounts: Vec<AccountConfig>,
pub move_packages: Vec<PathBuf>,
#[serde(default = "default_sui_framework_lib")]
pub sui_framework_lib_path: PathBuf,
#[serde(default = "default_move_framework_lib")]
pub move_framework_lib_path: PathBuf,
#[serde(skip)]
config_path: PathBuf,
}

#[derive(Serialize, Deserialize, Default)]
#[serde(default)]
pub struct AccountConfig {
#[serde(
skip_serializing_if = "Option::is_none",
serialize_with = "optional_address_as_hex",
deserialize_with = "optional_address_from_hex"
)]
pub address: Option<SuiAddress>,
pub gas_objects: Vec<ObjectConfig>,
}

#[derive(Serialize, Deserialize)]
pub struct ObjectConfig {
#[serde(default = "ObjectID::random")]
pub object_id: ObjectID,
#[serde(default = "default_gas_value")]
pub gas_value: u64,
}

fn default_gas_value() -> u64 {
DEFAULT_GAS_AMOUNT
}

fn default_sui_framework_lib() -> PathBuf {
PathBuf::from(DEFAULT_FRAMEWORK_PATH)
}

fn default_move_framework_lib() -> PathBuf {
PathBuf::from(DEFAULT_FRAMEWORK_PATH)
.join("deps")
.join("move-stdlib")
}

const DEFAULT_NUMBER_OF_AUTHORITIES: usize = 4;
const DEFAULT_NUMBER_OF_ACCOUNT: usize = 5;
const DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT: usize = 5;

impl GenesisConfig {
pub fn default_genesis(path: &Path) -> Result<Self, anyhow::Error> {
let working_dir = path.parent().ok_or(anyhow!("Cannot resolve file path."))?;
let mut authorities = Vec::new();
for _ in 0..DEFAULT_NUMBER_OF_AUTHORITIES {
// Get default authority config from deserialization logic.
let mut authority = AuthorityPrivateInfo::deserialize(Value::String(String::new()))?;
authority.db_path = working_dir
.join(AUTHORITIES_DB_NAME)
.join(encode_bytes_hex(&authority.key_pair.public_key_bytes()));
authorities.push(authority)
}
let mut accounts = Vec::new();
for _ in 0..DEFAULT_NUMBER_OF_ACCOUNT {
let mut objects = Vec::new();
for _ in 0..DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT {
objects.push(ObjectConfig {
object_id: ObjectID::random(),
gas_value: DEFAULT_GAS_AMOUNT,
})
}
accounts.push(AccountConfig {
address: None,
gas_objects: objects,
})
}
Ok(Self {
authorities,
accounts,
move_packages: vec![],
sui_framework_lib_path: default_sui_framework_lib(),
move_framework_lib_path: default_move_framework_lib(),
config_path: path.to_path_buf(),
})
}
}

impl Config for GenesisConfig {
fn create(path: &Path) -> Result<Self, anyhow::Error> {
Ok(Self {
authorities: vec![],
accounts: vec![],
config_path: path.to_path_buf(),
move_packages: vec![],
sui_framework_lib_path: default_sui_framework_lib(),
move_framework_lib_path: default_move_framework_lib(),
})
}

fn set_config_path(&mut self, path: &Path) {
self.config_path = path.to_path_buf()
}

fn config_path(&self) -> &Path {
&self.config_path
}
}
Loading