Skip to content

Commit

Permalink
[fastx CLI] Add customisation to genesis (MystenLabs#461)
Browse files Browse the repository at this point in the history
* * genesis customisation with config files
* ability to preload move module

* fixup after rebase
  • Loading branch information
patrickkuo authored Feb 18, 2022
1 parent 4a2385f commit bfde55c
Show file tree
Hide file tree
Showing 9 changed files with 603 additions and 114 deletions.
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

0 comments on commit bfde55c

Please sign in to comment.