Skip to content

Commit 423d0d1

Browse files
Merge pull request #6 from PoCInnovation/feature/contracts-routes
Feature/contracts routes
2 parents 004792c + bb1fec6 commit 423d0d1

File tree

308 files changed

+5728
-916
lines changed

Some content is hidden

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

308 files changed

+5728
-916
lines changed

Cargo.lock

Lines changed: 427 additions & 324 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,23 @@ use-self = "warn"
3737
[dependencies]
3838
anyhow = "1.0.100"
3939
bigdecimal = "0.4.8"
40+
blake2 = "0.10.6"
41+
bs58 = "0.5.1"
4042
clap = { version = "4", features = ["derive", "env"] }
41-
regex = "1.11.2"
42-
reqwest = { version = "0.12.23", features = ["json"] }
43-
serde = {version = "1.0.226", features = ["derive"] }
44-
serde_json = "1.0.145"
45-
strum = { version = "0.26.3", features = ["derive"] }
43+
futures = { version = "0.3.31", default-features = false }
4644
hex = "0.4.3"
45+
i256 = "0.2.3"
46+
indexmap = {version = "2.11.4", features = ["serde"]}
47+
regex = "1.12.2"
48+
reqwest = { version = "0.12.24", features = ["json"] }
4749
secp256k1 = "0.30.0"
48-
tokio = { version = "1.47.1", features = ["full"] }
50+
serde = { version = "1.0.228", features = ["derive"] }
51+
serde_json = { version = "1.0.145", features = ["raw_value"] }
52+
serde_yaml = "0.9.34"
53+
sha2 = "0.10.9"
54+
strum = { version = "0.27.2", features = ["derive"] }
55+
tokio = { version = "1.48.0", features = ["full"] }
56+
walkdir = "2.5.0"
4957

5058
[dev-dependencies]
5159
insta = { version = "1", features = ["filters"] }

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ Here are some examples of how to use the Kermit CLI:
129129
4. Compile a contract:
130130

131131
```bash
132-
kermit contracts compile tests/sub_contract.ral --contract-type project
132+
kermit contracts compile tests/contracts/sub_contract.ral --contract-type project
133133
```
134134

135135
You can do again more with kermit. I let you check the `kermit --help`, to take a look on all possibilities

src/account/account_struct.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use anyhow::Result;
2+
3+
use crate::account::{address::Address, signature::PrivateKey};
4+
5+
pub struct Account {
6+
pub private_key: Box<dyn PrivateKey>,
7+
pub address: Address,
8+
pub group: u8,
9+
}
10+
11+
impl Account {
12+
pub fn new(private_key: Box<dyn PrivateKey>) -> Result<Self> {
13+
let public_key = private_key.get_public_key()?;
14+
let address = Address::new(&public_key)?;
15+
let group = address.group_from_bytes();
16+
17+
Ok(Self {
18+
private_key,
19+
address,
20+
group,
21+
})
22+
}
23+
}

src/account/address.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
use anyhow::{Context, Result, bail};
2+
use blake2::{
3+
Blake2bVar,
4+
digest::{Update, VariableOutput},
5+
};
6+
use bs58;
7+
use serde::{Deserialize, Serialize, Serializer};
8+
9+
use crate::common::{djb2, xor_byte};
10+
11+
const TOTAL_NUMBER_OF_GROUPS: u8 = 4;
12+
13+
#[repr(u8)]
14+
#[allow(dead_code)]
15+
pub enum AddressType {
16+
P2PKH = 0x00,
17+
P2MPKH = 0x01,
18+
P2SH = 0x02,
19+
P2C = 0x03,
20+
P2PK = 0x04,
21+
P2HMPK = 0x05,
22+
}
23+
24+
#[derive(Debug, Clone, PartialEq, Eq)]
25+
pub struct Address {
26+
pub key: String,
27+
pub full_bytes: Vec<u8>,
28+
pub bytes: Vec<u8>,
29+
}
30+
31+
impl Serialize for Address {
32+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
33+
where
34+
S: Serializer,
35+
{
36+
serializer.serialize_str(&self.key)
37+
}
38+
}
39+
40+
impl<'de> Deserialize<'de> for Address {
41+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
42+
where
43+
D: serde::Deserializer<'de>,
44+
{
45+
let key = String::deserialize(deserializer)?;
46+
Self::new(&key).map_err(serde::de::Error::custom)
47+
}
48+
}
49+
50+
impl Address {
51+
pub fn new(public_key_str: &str) -> Result<Self> {
52+
let public_key_bytes = hex::decode(public_key_str).context("Invalid hex")?;
53+
let mut hasher = Blake2bVar::new(32).context("Failed to create Blake2bVar")?;
54+
hasher.update(&public_key_bytes);
55+
let mut hash_bytes = [0u8; 32];
56+
hasher
57+
.finalize_variable(&mut hash_bytes)
58+
.context("Failed to finalize Blake2bVar")?;
59+
let hash_bytes = &hash_bytes[..32];
60+
61+
let mut address_bytes = Vec::with_capacity(1 + 32);
62+
address_bytes.push(AddressType::P2PKH as u8);
63+
address_bytes.extend_from_slice(hash_bytes);
64+
65+
Ok(Self {
66+
key: bs58::encode(&address_bytes).into_string(),
67+
full_bytes: address_bytes,
68+
bytes: hash_bytes.into(),
69+
})
70+
}
71+
72+
pub fn new_b58(b58_str: &str) -> Result<Self> {
73+
let address_bytes = bs58::decode(b58_str)
74+
.into_vec()
75+
.context("Invalid base58 string")?;
76+
if address_bytes.len() < 33 {
77+
bail!("Invalid address length");
78+
}
79+
80+
let key = b58_str.to_string();
81+
let full_bytes = address_bytes.clone();
82+
let bytes = address_bytes[1..].to_vec(); // The length > 1 was already checked
83+
84+
Ok(Self {
85+
key,
86+
full_bytes,
87+
bytes,
88+
})
89+
}
90+
91+
pub fn group_from_bytes(&self) -> u8 {
92+
let hint = djb2(&self.bytes) | 1;
93+
let hash = xor_byte(hint);
94+
hash % TOTAL_NUMBER_OF_GROUPS
95+
}
96+
}

src/account/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub mod account_struct;
2+
pub mod address;
3+
pub mod signature;

src/account/signature.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use anyhow::{Context, Result, anyhow, bail};
2+
use secp256k1::{Message, PublicKey, Secp256k1, SecretKey};
3+
4+
#[allow(dead_code)]
5+
pub trait PrivateKey {
6+
fn as_hex(&self) -> String;
7+
fn get_public_key(&self) -> Result<String>;
8+
fn sign(&self, tx_id: &str) -> Result<String>;
9+
}
10+
11+
#[allow(dead_code)]
12+
pub struct GLSecp256k1PrivateKey {
13+
pub hex_key: String,
14+
pub key: SecretKey,
15+
}
16+
17+
impl PrivateKey for GLSecp256k1PrivateKey {
18+
fn as_hex(&self) -> String {
19+
self.hex_key.clone()
20+
}
21+
22+
fn get_public_key(&self) -> Result<String> {
23+
let secp = Secp256k1::new();
24+
let public_key = PublicKey::from_secret_key(&secp, &self.key);
25+
Ok(public_key.to_string())
26+
}
27+
28+
fn sign(&self, tx_id: &str) -> Result<String> {
29+
let tx_id_bytes = hex::decode(tx_id)?;
30+
let message = Message::from_digest(
31+
tx_id_bytes
32+
.try_into()
33+
.map_err(|_| anyhow!("Invalid hash length"))?,
34+
);
35+
36+
let secp = Secp256k1::new();
37+
let signature = secp.sign_ecdsa(&message, &self.key);
38+
let serialized = signature.serialize_compact();
39+
let signature = hex::encode(serialized);
40+
41+
Ok(signature)
42+
}
43+
}
44+
45+
#[allow(dead_code)]
46+
impl GLSecp256k1PrivateKey {
47+
pub fn new(key: &str) -> Result<Self> {
48+
if !Self::is_valid(key) {
49+
bail!("Invalid private key format");
50+
}
51+
52+
let private_key = SecretKey::from_slice(&hex::decode(key).context("Invalid hex key")?)
53+
.context("Failed to create secret key")?;
54+
55+
Ok(Self {
56+
key: private_key,
57+
hex_key: key.to_string(),
58+
})
59+
}
60+
61+
fn is_valid(hex_key: &str) -> bool {
62+
hex_key.len() == 64 && hex_key.chars().all(|c| c.is_ascii_hexdigit())
63+
}
64+
}

src/addresses.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use anyhow::Result;
22
use clap::Parser;
33

4-
use crate::common::{get, print_output};
4+
use crate::common::{check_network, get, print_output};
55

66
/// CLI arguments for `kermit addresses`.
77
#[derive(Parser)]
8-
pub(crate) enum AddressesSubcommands {
8+
pub enum AddressesSubcommands {
99
/// Get the balance of an address.
1010
#[command(visible_alias = "b")]
1111
Balance {
@@ -28,7 +28,9 @@ pub(crate) enum AddressesSubcommands {
2828
}
2929

3030
impl AddressesSubcommands {
31-
pub(crate) async fn run(self, url: String) -> Result<()> {
31+
pub async fn run(self, url: &str) -> Result<()> {
32+
check_network(url).await?;
33+
3234
let endpoint = match self {
3335
Self::Balance { address, mem_pool } => {
3436
format!("/addresses/{address}/balance?mempool={mem_pool}")
@@ -47,7 +49,7 @@ impl AddressesSubcommands {
4749
},
4850
};
4951

50-
let output = get(&url, &endpoint).await?;
52+
let output = get(url, &endpoint).await?;
5153
print_output(output)?;
5254

5355
Ok(())

src/args.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
use clap::{Parser, Subcommand, ValueHint};
22

33
use crate::{
4-
addresses::AddressesSubcommands, blockflow::BlockflowSubcommands,
5-
contracts::ContractsSubcommands, infos::InfosSubcommands, miners::MinersSubcommands,
6-
transactions::TransactionsSubcommands, utils::UtilsSubcommands, wallets::WalletsSubcommands,
4+
addresses::AddressesSubcommands,
5+
blockflow::BlockflowSubcommands,
6+
contracts::{ContractsSubcommands, NetworkType},
7+
infos::InfosSubcommands,
8+
miners::MinersSubcommands,
9+
transactions::TransactionsSubcommands,
10+
utils::UtilsSubcommands,
11+
wallets::WalletsSubcommands,
712
};
813

914
#[derive(Parser)]
1015
#[command(version)]
11-
pub(crate) struct Kermit {
16+
pub struct Kermit {
1217
#[clap(long, short, env, value_hint = ValueHint::Url,
1318
default_value = "http://localhost:22973")]
1419
pub url: String,
@@ -18,7 +23,7 @@ pub(crate) struct Kermit {
1823
}
1924

2025
#[derive(Subcommand)]
21-
pub(crate) enum KermitSubcommand {
26+
pub enum KermitSubcommand {
2227
/// Address management utilities.
2328
#[command(visible_alias = "a")]
2429
Addresses {
@@ -38,6 +43,19 @@ pub(crate) enum KermitSubcommand {
3843
Contracts {
3944
#[command(subcommand)]
4045
command: ContractsSubcommands,
46+
47+
/// Path to the config YAML file
48+
#[arg(long, short, default_value = "./alephium.config.yaml")]
49+
config_file_path: String,
50+
51+
/// Create the config file for contracts automatically if not set
52+
#[arg(long, default_value_t = false)]
53+
auto_create_config_file: bool,
54+
55+
/// Network type may trigger a different behavior in contract
56+
/// operations. Choose accordingly
57+
#[arg(long, short, value_enum, default_value_t = NetworkType::Dev)]
58+
network: NetworkType,
4159
},
4260

4361
/// Infos about node and hashrate.

src/blockflow.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use anyhow::Result;
22
use clap::Parser;
33

4-
use crate::common::{get, print_output};
4+
use crate::common::{check_network, get, print_output};
55

66
/// CLI arguments for `kermit blockflow`.
77
#[derive(Parser)]
8-
pub(crate) enum BlockflowSubcommands {
8+
pub enum BlockflowSubcommands {
99
/// List blocks on the given time interval.
1010
#[command(visible_alias = "bs")]
1111
Blocks { from_ts: i64, to_ts: Option<i64> },
@@ -62,7 +62,9 @@ pub(crate) enum BlockflowSubcommands {
6262
}
6363

6464
impl BlockflowSubcommands {
65-
pub(crate) async fn run(self, url: &str) -> Result<()> {
65+
pub async fn run(self, url: &str) -> Result<()> {
66+
check_network(url).await?;
67+
6668
let endpoint = match self {
6769
Self::Blocks { from_ts, to_ts } => {
6870
let mut endpoint = format!("/blockflow/blocks?fromTs={from_ts}");

0 commit comments

Comments
 (0)