Skip to content

Commit

Permalink
Add support for KUSD and KRV
Browse files Browse the repository at this point in the history
- Defined TransactionKind and AssetType enums.
- Added TransactionKind attribute to the Transaction struct.
- Integrated AssetType attribute in TransactionOutput, UtxoEntry, and PaymentOutput.
- Fix some unittests
  • Loading branch information
KashProtocol committed Dec 28, 2023
1 parent 543d2f5 commit cc8981b
Show file tree
Hide file tree
Showing 81 changed files with 1,241 additions and 415 deletions.
4 changes: 2 additions & 2 deletions cli/src/modules/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ impl Account {
if let Some(txid) = txid {
tprintln!(
ctx_,
"Scan detected {} KSHat index {}; transfer txid: {}",
"Scan detected {} KSH at index {}; transfer txid: {}",
sompi_to_kash_string(balance),
processed,
txid
Expand Down Expand Up @@ -257,7 +257,7 @@ impl Account {
if let Some(txid) = txid {
tprintln!(
ctx_,
"Scan detected {} KSHat index {}; transfer txid: {}",
"Scan detected {} KSH at index {}; transfer txid: {}",
sompi_to_kash_string(balance),
processed,
txid
Expand Down
24 changes: 16 additions & 8 deletions cli/src/modules/estimate.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::imports::*;
use kash_consensus_core::tx::TransactionKind;
use kash_wallet_core::tx::PaymentDestination;

#[derive(Default, Handler)]
#[help("Estimate the fees for a transaction of a given amount")]
#[help("Estimate the fees for a transaction of a given amount and transaction kind")]
pub struct Estimate;

impl Estimate {
Expand All @@ -11,19 +12,26 @@ impl Estimate {

let account = ctx.wallet().account()?;

if argv.is_empty() {
tprintln!(ctx, "usage: estimate <amount> [<priority fee>]");
// Expecting at least two arguments: amount and transaction kind
if argv.len() < 2 {
tprintln!(ctx, "usage: estimate <transaction kind> <amount> [<priority fee>]");
return Ok(());
}

let amount_sompi = try_parse_required_nonzero_kash_as_sompi_u64(argv.first())?;
let priority_fee_sompi = try_parse_optional_kash_as_sompi_i64(argv.get(1))?.unwrap_or(0);
let tx_kind_str = &argv[0];
let tx_kind =
TransactionKind::try_from(tx_kind_str).map_err(|_| Error::custom(format!("Invalid transaction kind: {}", tx_kind_str)))?;

let amount_sompi = try_parse_required_nonzero_kash_as_sompi_u64(argv.get(1))?;
let priority_fee_sompi = try_parse_optional_kash_as_sompi_i64(argv.get(2))?.unwrap_or(0);
let abortable = Abortable::default();

// just use any address for an estimate (change address)
// Just use any address for an estimate (change address)
let change_address = account.change_address()?;
let destination = PaymentDestination::PaymentOutputs(PaymentOutputs::from((change_address.clone(), amount_sompi)));
let estimate = account.estimate(destination, priority_fee_sompi.into(), None, &abortable).await?;
let destination_asset_type = tx_kind.asset_transfer_types().1;
let destination =
PaymentDestination::PaymentOutputs(PaymentOutputs::from((change_address.clone(), amount_sompi, destination_asset_type)));
let estimate = account.estimate(tx_kind, destination, priority_fee_sompi.into(), None, &abortable).await?;

tprintln!(ctx, "Estimate - {estimate}");

Expand Down
24 changes: 13 additions & 11 deletions cli/src/modules/send.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,35 @@
use crate::imports::*;
use kash_consensus_core::asset_type::AssetType;
use std::convert::TryFrom;

#[derive(Default, Handler)]
#[help("Send a Kash transaction to a public address")]
pub struct Send;

impl Send {
async fn main(self: Arc<Self>, ctx: &Arc<dyn Context>, argv: Vec<String>, _cmd: &str) -> Result<()> {
// address, amount, priority fee
let ctx = ctx.clone().downcast_arc::<KashCli>()?;

let account = ctx.wallet().account()?;

if argv.len() < 2 {
tprintln!(ctx, "usage: send <address> <amount> <priority fee>");
// Checking minimum argument length
if argv.len() < 3 {
tprintln!(ctx, "usage: send <asset type(KSH/KUSD/KRV)> <address> <amount> <priority fee>");
return Ok(());
}

let address = Address::try_from(argv.first().unwrap().as_str())?;
let amount_sompi = try_parse_required_nonzero_kash_as_sompi_u64(argv.get(1))?;
let priority_fee_sompi = try_parse_optional_kash_as_sompi_i64(argv.get(2))?.unwrap_or(0);
let outputs = PaymentOutputs::from((address.clone(), amount_sompi));
// Parsing asset type, address, and amounts
let asset_type = AssetType::from(argv.first().unwrap().as_str());
let address = Address::try_from(argv.get(1).unwrap().as_str())?;
let amount_sompi = try_parse_required_nonzero_kash_as_sompi_u64(argv.get(2))?;
let priority_fee_sompi = try_parse_optional_kash_as_sompi_i64(argv.get(3))?.unwrap_or(0);

let outputs = PaymentOutputs::from((address.clone(), amount_sompi, asset_type));
let abortable = Abortable::default();
let (wallet_secret, payment_secret) = ctx.ask_wallet_secret(Some(&account)).await?;

// let ctx_ = ctx.clone();
let (summary, _ids) = account
.send(
asset_type,
outputs.into(),
priority_fee_sompi.into(),
None,
Expand All @@ -39,8 +43,6 @@ impl Send {
.await?;

tprintln!(ctx, "Send - {summary}");
// tprintln!(ctx, "\nSending {} KSHto {address}, tx ids:", sompi_to_kash_string(amount_sompi));
// tprintln!(ctx, "{}\n", ids.into_iter().map(|a| a.to_string()).collect::<Vec<_>>().join("\n"));

Ok(())
}
Expand Down
6 changes: 4 additions & 2 deletions cli/src/modules/sweep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ impl Sweep {
let (wallet_secret, payment_secret) = ctx.ask_wallet_secret(Some(&account)).await?;
let abortable = Abortable::default();
// let ctx_ = ctx.clone();
let (summary, _ids) = account
let (summaries, _ids) = account
.sweep(
wallet_secret,
payment_secret,
Expand All @@ -23,7 +23,9 @@ impl Sweep {
)
.await?;

tprintln!(ctx, "Sweep: {summary}");
for summary in summaries.iter() {
tprintln!(ctx, "Sweep - {summary}");
}

Ok(())
}
Expand Down
25 changes: 17 additions & 8 deletions cli/src/modules/transfer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::imports::*;
use kash_consensus_core::asset_type::AssetType;

#[derive(Default, Handler)]
#[help("Transfer funds between wallet accounts")]
Expand All @@ -10,27 +11,35 @@ impl Transfer {

let account = ctx.wallet().account()?;

if argv.len() < 2 {
tprintln!(ctx, "usage: transfer <account> <amount> <priority fee>");
// Checking minimum argument length, now expecting 3 arguments
if argv.len() < 3 {
tprintln!(ctx, "usage: transfer <asset type(KSH/KUSD/KRV)> <account> <amount> <priority fee>");
return Ok(());
}

let target_account = argv.first().unwrap();
let target_account = ctx.find_accounts_by_name_or_id(target_account).await?;
// Parsing asset type
let asset_type_str = argv.first().unwrap();
let asset_type = AssetType::from(asset_type_str.as_str());

let target_account_str = argv.get(1).unwrap();
let target_account = ctx.find_accounts_by_name_or_id(target_account_str).await?;
if target_account.id() == account.id() {
return Err("Cannot transfer to the same account".into());
}
let amount_sompi = try_parse_required_nonzero_kash_as_sompi_u64(argv.get(1))?;
let priority_fee_sompi = try_parse_optional_kash_as_sompi_i64(argv.get(2))?.unwrap_or(0);

// Parsing amount and priority fee
let amount_sompi = try_parse_required_nonzero_kash_as_sompi_u64(argv.get(2))?;
let priority_fee_sompi = try_parse_optional_kash_as_sompi_i64(argv.get(3))?.unwrap_or(0);

let target_address = target_account.receive_address()?;
let (wallet_secret, payment_secret) = ctx.ask_wallet_secret(Some(&account)).await?;

let abortable = Abortable::default();
let outputs = PaymentOutputs::from((target_address.clone(), amount_sompi));
let outputs = PaymentOutputs::from((target_address.clone(), amount_sompi, asset_type));

// let ctx_ = ctx.clone();
let (summary, _ids) = account
.send(
asset_type,
outputs.into(),
priority_fee_sompi.into(),
None,
Expand Down
13 changes: 8 additions & 5 deletions consensus/core/benches/serde_benchmark.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use kash_consensus_core::asset_type::AssetType::KSH;
use kash_consensus_core::subnets::SUBNETWORK_ID_COINBASE;
use kash_consensus_core::tx::{
ScriptPublicKey, Transaction, TransactionId, TransactionInput, TransactionOutpoint, TransactionOutput,
ScriptPublicKey, Transaction, TransactionId, TransactionInput, TransactionKind, TransactionOutpoint, TransactionOutput,
};
use smallvec::smallvec;
use std::time::{Duration, Instant};
Expand Down Expand Up @@ -43,9 +44,10 @@ fn serialize_benchmark(c: &mut Criterion) {
},
],
vec![
TransactionOutput { value: 300, script_public_key: script_public_key.clone() },
TransactionOutput { value: 300, script_public_key },
TransactionOutput { value: 300, script_public_key: script_public_key.clone(), asset_type: KSH },
TransactionOutput { value: 300, script_public_key, asset_type: KSH },
],
TransactionKind::TransferKSH,
0,
SUBNETWORK_ID_COINBASE,
0,
Expand Down Expand Up @@ -105,9 +107,10 @@ fn deserialize_benchmark(c: &mut Criterion) {
},
],
vec![
TransactionOutput { value: 300, script_public_key: script_public_key.clone() },
TransactionOutput { value: 300, script_public_key },
TransactionOutput { value: 300, script_public_key: script_public_key.clone(), asset_type: KSH },
TransactionOutput { value: 300, script_public_key, asset_type: KSH },
],
TransactionKind::TransferKSH,
0,
SUBNETWORK_ID_COINBASE,
0,
Expand Down
88 changes: 88 additions & 0 deletions consensus/core/src/asset_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use serde::{Deserialize, Serialize};
use std::fmt;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::JsValue;
/// Enum representing different types of assets in the Kash blockchain.
/// This allows for the representation of multiple currencies such as KSH, KUSD, and KRV.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize, BorshSchema)]
#[wasm_bindgen(js_name = assetType)]
pub enum AssetType {
KSH = 0,
KUSD = 1,
KRV = 2,
}

impl From<u32> for AssetType {
fn from(value: u32) -> Self {
match value {
0 => AssetType::KSH,
1 => AssetType::KUSD,
2 => AssetType::KRV,
_ => panic!("Invalid AssetType value: {}", value),
}
}
}

impl From<AssetType> for u32 {
fn from(val: AssetType) -> Self {
match val {
AssetType::KSH => 0,
AssetType::KUSD => 1,
AssetType::KRV => 2,
}
}
}

impl From<u8> for AssetType {
fn from(value: u8) -> Self {
match value {
0 => AssetType::KSH,
1 => AssetType::KUSD,
2 => AssetType::KRV,
_ => panic!("Invalid AssetType value: {}", value),
}
}
}

impl From<&str> for AssetType {
fn from(value: &str) -> Self {
match value {
"KSH" => AssetType::KSH,
"KUSD" => AssetType::KUSD,
"KRV" => AssetType::KRV,
_ => panic!("Invalid AssetType value: {}", value),
}
}
}

impl TryFrom<JsValue> for AssetType {
type Error = JsValue;

fn try_from(js_value: JsValue) -> Result<Self, Self::Error> {
if let Some(value) = js_value.as_f64() {
match value as u8 {
0 => Ok(AssetType::KSH),
1 => Ok(AssetType::KUSD),
2 => Ok(AssetType::KRV),
_ => Err(JsValue::from_str("Invalid AssetType value")),
}
} else {
Err(JsValue::from_str("Invalid AssetType value"))
}
}
}

impl fmt::Display for AssetType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
AssetType::KSH => "KSH",
AssetType::KUSD => "KUSD",
AssetType::KRV => "KRV",
}
)
}
}
12 changes: 11 additions & 1 deletion consensus/core/src/config/genesis.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::tx::TransactionKind;
use crate::{block::Block, header::Header, subnets::SUBNETWORK_ID_COINBASE, tx::Transaction};
use kash_hashes::{Hash, ZERO_HASH};
use kash_muhash::EMPTY_MUHASH;
Expand All @@ -18,7 +19,16 @@ pub struct GenesisBlock {

impl GenesisBlock {
pub fn build_genesis_transactions(&self) -> Vec<Transaction> {
vec![Transaction::new(0, Vec::new(), Vec::new(), 0, SUBNETWORK_ID_COINBASE, 0, self.coinbase_payload.to_vec())]
vec![Transaction::new(
0,
Vec::new(),
Vec::new(),
TransactionKind::TransferKSH,
0,
SUBNETWORK_ID_COINBASE,
0,
self.coinbase_payload.to_vec(),
)]
}
}

Expand Down
7 changes: 7 additions & 0 deletions consensus/core/src/errors/asset_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use thiserror::Error;

#[derive(Error, Debug, Clone)]
pub enum AssetTypeError {
#[error("asset type {0} is not supported")]
UnsupportedAssetType(u8),
}
1 change: 1 addition & 0 deletions consensus/core/src/errors/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod asset_type;
pub mod block;
pub mod coinbase;
pub mod config;
Expand Down
3 changes: 3 additions & 0 deletions consensus/core/src/errors/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ pub enum TxRuleError {

#[error("input {0} sig op count is {1}, but the calculated value is {2}")]
WrongSigOpCount(usize, u64, u64),

#[error("invalid transaction type: {0}")]
InvalidTransactionType(String),
}

pub type TxResult<T> = std::result::Result<T, TxRuleError>;
Loading

0 comments on commit cc8981b

Please sign in to comment.