Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
645bd46
feated: http response struct to get the status code as well as the data
thomas-pommier-epi Aug 6, 2025
a55b3fc
feated: compile script command
bernTP Aug 7, 2025
8e86de3
feated: compile project endpoint
bernTP Aug 7, 2025
1b8cbc5
fixed: compilation importations and yaml parsing
bernTP Aug 8, 2025
963dd11
feated: seperating code infos from source infos
bernTP Aug 10, 2025
a6e4f10
feated: secp256k1 and b58 handling for deploy contract
bernTP Aug 10, 2025
7bf83e7
feated: config contruct method + contract address and utils functions…
bernTP Aug 10, 2025
45e16c0
feated: account struct + using PrivateKey trait
bernTP Aug 10, 2025
adac396
feated: project compile output parsing
bernTP Aug 11, 2025
cb2e6e2
feated: debug bytecode patching + std prefix insertion in fields
bernTP Aug 11, 2025
147892a
feated: signing via private key impl
bernTP Aug 11, 2025
3e2476c
feated: output value parsing + initial fields support
bernTP Aug 11, 2025
004360e
feated: codec decoder and encoder
bernTP Aug 11, 2025
e8802d9
feated: codecs (encoding) for parameters
bernTP Aug 11, 2025
4fbadbc
feated: submitting contract deploy transaction
bernTP Aug 11, 2025
ff3457a
feated: deploy contract command link
bernTP Aug 11, 2025
dd388a6
feated: initial fields support via CLI
bernTP Aug 11, 2025
223f47b
fixed: address creation + network ids mismatches
bernTP Aug 13, 2025
bd4cc01
fixed: u32 codecs + fields map implementations
bernTP Aug 13, 2025
92615b3
feated: state command + new parsing for ralph value BytesVec
bernTP Aug 14, 2025
63237df
feated: adding structs and maps for the initial fields
bernTP Aug 16, 2025
c5a7964
feated: test compiled project and serialize ralph values
bernTP Aug 16, 2025
a57eb73
feated: refactored main network alive checks and config parsing
bernTP Aug 17, 2025
53b6b54
feated: config with contract value (initFields and inputAssets)
bernTP Aug 17, 2025
493b734
feated: integration of initfields config to deploy and test contracts
bernTP Aug 17, 2025
43f367d
feated: contract call route
bernTP Aug 18, 2025
10e5d74
Merge branch 'main' into feature/contracts-routes
bernTP Sep 23, 2025
759d062
fixed: code cleaning with bail! and unused imports
bernTP Sep 23, 2025
737ba5d
fixed: tests with default network type and help output
bernTP Sep 23, 2025
5869d93
fixed: invalid url without base
bernTP Sep 23, 2025
f0de6ad
feated: default config file for CLI
bernTP Sep 23, 2025
87b9b10
fixed: warnings about dead code
bernTP Sep 29, 2025
617cfca
feated: missing contracts get routes
bernTP Sep 29, 2025
5ef66a5
feated: contract compilation test
bernTP Oct 5, 2025
023fd12
feated: additional compilation test + fixed import regex match code
bernTP Oct 6, 2025
ad4185b
feated: contract deploy tests + fixed: YAML strings parsing
bernTP Oct 8, 2025
39276a8
fixed: new snap for compilation + feated: default contracts filter
bernTP Oct 8, 2025
c965ca8
fixed: array and tuples integration + removing useless Map type
bernTP Oct 9, 2025
54b1677
feated: contracts call + refactoring in ralph parsing
bernTP Oct 10, 2025
32e34d0
fixed: nested arrays and structures arg + field encoding
bernTP Oct 10, 2025
a5a7333
fixed: wrong bytecode order for init fields
bernTP Oct 10, 2025
82c2195
feated: unordered args + fixed deployment via skipping __stdInterfaceId
bernTP Oct 10, 2025
1f23bc9
fixed: unecessary vtable for PrivateKey
bernTP Oct 10, 2025
3a09321
feated: b58 parsing in fields/args + state and code unit tests for co…
bernTP Oct 11, 2025
a828d49
Merge branch 'main' into feature/contracts-routes
bernTP Oct 12, 2025
0af90ae
feated: contracts testing framework with testcontainers
bernTP Oct 13, 2025
8c823ed
feated: help contracts arguments defined with ///
bernTP Oct 13, 2025
e8965f8
fixed: remove useless dead code
bernTP Oct 13, 2025
47671ff
fixed: testing cases from merge
bernTP Oct 13, 2025
81470cf
feated: more compile import tests
bernTP Oct 14, 2025
efe0ab5
chore: update dependencies
Oct 17, 2025
bdfdc6f
feat: delete empty file
Nfire2103 Oct 17, 2025
b28fb8f
chore: clean some stuff
Nfire2103 Oct 17, 2025
e1b7eb1
chore: lint
Nfire2103 Oct 17, 2025
3835c88
feated: contracts config file not genraated by default + used only on…
bernTP Oct 17, 2025
e1a4e97
fixed: clippy warnings on tests contracts functions
bernTP Oct 17, 2025
da10c2d
fixed: code formatting, number of arguments and other linter errors
bernTP Oct 19, 2025
a6666d6
fix: rust format
Nfire2103 Oct 21, 2025
4fe0e04
fix: network error
Nfire2103 Oct 21, 2025
4dc033c
fix: snapshot files
Nfire2103 Oct 21, 2025
bb1fec6
chore: update common path
Nfire2103 Oct 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
751 changes: 427 additions & 324 deletions Cargo.lock

Large diffs are not rendered by default.

20 changes: 14 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,23 @@ use-self = "warn"
[dependencies]
anyhow = "1.0.100"
bigdecimal = "0.4.8"
blake2 = "0.10.6"
bs58 = "0.5.1"
clap = { version = "4", features = ["derive", "env"] }
regex = "1.11.2"
reqwest = { version = "0.12.23", features = ["json"] }
serde = {version = "1.0.226", features = ["derive"] }
serde_json = "1.0.145"
strum = { version = "0.26.3", features = ["derive"] }
futures = { version = "0.3.31", default-features = false }
hex = "0.4.3"
i256 = "0.2.3"
indexmap = {version = "2.11.4", features = ["serde"]}
regex = "1.12.2"
reqwest = { version = "0.12.24", features = ["json"] }
secp256k1 = "0.30.0"
tokio = { version = "1.47.1", features = ["full"] }
serde = { version = "1.0.228", features = ["derive"] }
serde_json = { version = "1.0.145", features = ["raw_value"] }
serde_yaml = "0.9.34"
sha2 = "0.10.9"
strum = { version = "0.27.2", features = ["derive"] }
tokio = { version = "1.48.0", features = ["full"] }
walkdir = "2.5.0"

[dev-dependencies]
insta = { version = "1", features = ["filters"] }
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ Here are some examples of how to use the Kermit CLI:
4. Compile a contract:

```bash
kermit contracts compile tests/sub_contract.ral --contract-type project
kermit contracts compile tests/contracts/sub_contract.ral --contract-type project
```

You can do again more with kermit. I let you check the `kermit --help`, to take a look on all possibilities
Expand Down
23 changes: 23 additions & 0 deletions src/account/account_struct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use anyhow::Result;

use crate::account::{address::Address, signature::PrivateKey};

pub struct Account {
pub private_key: Box<dyn PrivateKey>,
pub address: Address,
pub group: u8,
}

impl Account {
pub fn new(private_key: Box<dyn PrivateKey>) -> Result<Self> {
let public_key = private_key.get_public_key()?;
let address = Address::new(&public_key)?;
let group = address.group_from_bytes();

Ok(Self {
private_key,
address,
group,
})
}
}
96 changes: 96 additions & 0 deletions src/account/address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use anyhow::{Context, Result, bail};
use blake2::{
Blake2bVar,
digest::{Update, VariableOutput},
};
use bs58;
use serde::{Deserialize, Serialize, Serializer};

use crate::common::{djb2, xor_byte};

const TOTAL_NUMBER_OF_GROUPS: u8 = 4;

#[repr(u8)]
#[allow(dead_code)]
pub enum AddressType {
P2PKH = 0x00,
P2MPKH = 0x01,
P2SH = 0x02,
P2C = 0x03,
P2PK = 0x04,
P2HMPK = 0x05,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Address {
pub key: String,
pub full_bytes: Vec<u8>,
pub bytes: Vec<u8>,
}

impl Serialize for Address {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.key)
}
}

impl<'de> Deserialize<'de> for Address {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let key = String::deserialize(deserializer)?;
Self::new(&key).map_err(serde::de::Error::custom)
}
}

impl Address {
pub fn new(public_key_str: &str) -> Result<Self> {
let public_key_bytes = hex::decode(public_key_str).context("Invalid hex")?;
let mut hasher = Blake2bVar::new(32).context("Failed to create Blake2bVar")?;
hasher.update(&public_key_bytes);
let mut hash_bytes = [0u8; 32];
hasher
.finalize_variable(&mut hash_bytes)
.context("Failed to finalize Blake2bVar")?;
let hash_bytes = &hash_bytes[..32];

let mut address_bytes = Vec::with_capacity(1 + 32);
address_bytes.push(AddressType::P2PKH as u8);
address_bytes.extend_from_slice(hash_bytes);

Ok(Self {
key: bs58::encode(&address_bytes).into_string(),
full_bytes: address_bytes,
bytes: hash_bytes.into(),
})
}

pub fn new_b58(b58_str: &str) -> Result<Self> {
let address_bytes = bs58::decode(b58_str)
.into_vec()
.context("Invalid base58 string")?;
if address_bytes.len() < 33 {
bail!("Invalid address length");
}

let key = b58_str.to_string();
let full_bytes = address_bytes.clone();
let bytes = address_bytes[1..].to_vec(); // The length > 1 was already checked

Ok(Self {
key,
full_bytes,
bytes,
})
}

pub fn group_from_bytes(&self) -> u8 {
let hint = djb2(&self.bytes) | 1;
let hash = xor_byte(hint);
hash % TOTAL_NUMBER_OF_GROUPS
}
}
3 changes: 3 additions & 0 deletions src/account/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod account_struct;
pub mod address;
pub mod signature;
64 changes: 64 additions & 0 deletions src/account/signature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use anyhow::{Context, Result, anyhow, bail};
use secp256k1::{Message, PublicKey, Secp256k1, SecretKey};

#[allow(dead_code)]
pub trait PrivateKey {
fn as_hex(&self) -> String;
fn get_public_key(&self) -> Result<String>;
fn sign(&self, tx_id: &str) -> Result<String>;
}

#[allow(dead_code)]
pub struct GLSecp256k1PrivateKey {
pub hex_key: String,
pub key: SecretKey,
}

impl PrivateKey for GLSecp256k1PrivateKey {
fn as_hex(&self) -> String {
self.hex_key.clone()
}

fn get_public_key(&self) -> Result<String> {
let secp = Secp256k1::new();
let public_key = PublicKey::from_secret_key(&secp, &self.key);
Ok(public_key.to_string())
}

fn sign(&self, tx_id: &str) -> Result<String> {
let tx_id_bytes = hex::decode(tx_id)?;
let message = Message::from_digest(
tx_id_bytes
.try_into()
.map_err(|_| anyhow!("Invalid hash length"))?,
);

let secp = Secp256k1::new();
let signature = secp.sign_ecdsa(&message, &self.key);
let serialized = signature.serialize_compact();
let signature = hex::encode(serialized);

Ok(signature)
}
}

#[allow(dead_code)]
impl GLSecp256k1PrivateKey {
pub fn new(key: &str) -> Result<Self> {
if !Self::is_valid(key) {
bail!("Invalid private key format");
}

let private_key = SecretKey::from_slice(&hex::decode(key).context("Invalid hex key")?)
.context("Failed to create secret key")?;

Ok(Self {
key: private_key,
hex_key: key.to_string(),
})
}

fn is_valid(hex_key: &str) -> bool {
hex_key.len() == 64 && hex_key.chars().all(|c| c.is_ascii_hexdigit())
}
}
10 changes: 6 additions & 4 deletions src/addresses.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use anyhow::Result;
use clap::Parser;

use crate::common::{get, print_output};
use crate::common::{check_network, get, print_output};

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

impl AddressesSubcommands {
pub(crate) async fn run(self, url: String) -> Result<()> {
pub async fn run(self, url: &str) -> Result<()> {
check_network(url).await?;

let endpoint = match self {
Self::Balance { address, mem_pool } => {
format!("/addresses/{address}/balance?mempool={mem_pool}")
Expand All @@ -47,7 +49,7 @@ impl AddressesSubcommands {
},
};

let output = get(&url, &endpoint).await?;
let output = get(url, &endpoint).await?;
print_output(output)?;

Ok(())
Expand Down
28 changes: 23 additions & 5 deletions src/args.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
use clap::{Parser, Subcommand, ValueHint};

use crate::{
addresses::AddressesSubcommands, blockflow::BlockflowSubcommands,
contracts::ContractsSubcommands, infos::InfosSubcommands, miners::MinersSubcommands,
transactions::TransactionsSubcommands, utils::UtilsSubcommands, wallets::WalletsSubcommands,
addresses::AddressesSubcommands,
blockflow::BlockflowSubcommands,
contracts::{ContractsSubcommands, NetworkType},
infos::InfosSubcommands,
miners::MinersSubcommands,
transactions::TransactionsSubcommands,
utils::UtilsSubcommands,
wallets::WalletsSubcommands,
};

#[derive(Parser)]
#[command(version)]
pub(crate) struct Kermit {
pub struct Kermit {
#[clap(long, short, env, value_hint = ValueHint::Url,
default_value = "http://localhost:22973")]
pub url: String,
Expand All @@ -18,7 +23,7 @@ pub(crate) struct Kermit {
}

#[derive(Subcommand)]
pub(crate) enum KermitSubcommand {
pub enum KermitSubcommand {
/// Address management utilities.
#[command(visible_alias = "a")]
Addresses {
Expand All @@ -38,6 +43,19 @@ pub(crate) enum KermitSubcommand {
Contracts {
#[command(subcommand)]
command: ContractsSubcommands,

/// Path to the config YAML file
#[arg(long, short, default_value = "./alephium.config.yaml")]
config_file_path: String,

/// Create the config file for contracts automatically if not set
#[arg(long, default_value_t = false)]
auto_create_config_file: bool,

/// Network type may trigger a different behavior in contract
/// operations. Choose accordingly
#[arg(long, short, value_enum, default_value_t = NetworkType::Dev)]
network: NetworkType,
},

/// Infos about node and hashrate.
Expand Down
8 changes: 5 additions & 3 deletions src/blockflow.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use anyhow::Result;
use clap::Parser;

use crate::common::{get, print_output};
use crate::common::{check_network, get, print_output};

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

impl BlockflowSubcommands {
pub(crate) async fn run(self, url: &str) -> Result<()> {
pub async fn run(self, url: &str) -> Result<()> {
check_network(url).await?;

let endpoint = match self {
Self::Blocks { from_ts, to_ts } => {
let mut endpoint = format!("/blockflow/blocks?fromTs={from_ts}");
Expand Down
29 changes: 29 additions & 0 deletions src/common/crypto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
pub fn djb2(bytes: &[u8]) -> i32 {
let mut hash: i32 = 5381;
for &byte in bytes {
hash = (hash << 5).wrapping_add(hash).wrapping_add(byte.into());
}
hash
}

pub const fn xor_byte(int_value: i32) -> u8 {
let byte0 = ((int_value >> 24) & 0xff) as u8;
let byte1 = ((int_value >> 16) & 0xff) as u8;
let byte2 = ((int_value >> 8) & 0xff) as u8;
let byte3 = (int_value & 0xff) as u8;
byte0 ^ byte1 ^ byte2 ^ byte3
}

pub fn is_hex_string(input: &str) -> bool {
input.starts_with("0x")
&& (input.len() - 2).is_multiple_of(2)
&& input[2..].chars().all(|c| c.is_ascii_hexdigit())
}

pub fn is_b58(input: &str) -> bool {
input.starts_with("b58:")
&& input[4..].chars().all(|c| {
c.is_ascii_alphanumeric()
&& "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".contains(c)
})
}
Loading
Loading