Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
459 changes: 296 additions & 163 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions balius-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ flate2 = "1.0.33"
tar = "0.4.41"
serde = "1.0.210"
serde_json = "1.0.128"
pallas = "0.32.0"
pallas = "1.0.0"
redb = "2.1.3"
tokio = "1.40.0"
tracing = "0.1.40"
Expand All @@ -32,7 +32,7 @@ object_store = { version = "0.12.0", features = ["fs"] }
url = "2.5.4"
reqwest = { workspace = true }
opentelemetry = { version = "0.29.1", features = ["metrics", "trace"] }
rand = "0.8.5"
rand = { version = "0.10.1", features = ["sys_rng"] }
futures = "0.3.31"

[dev-dependencies]
Expand Down
4 changes: 2 additions & 2 deletions balius-runtime/src/ledgers/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ impl Ledger {
&mut self,
_refs: Vec<wit::TxoRef>,
) -> Result<Vec<wit::Utxo>, wit::LedgerError> {
let output = pallas::ledger::primitives::babbage::MintedTransactionOutput::PostAlonzo(pallas::ledger::primitives::babbage::MintedPostAlonzoTransactionOutput {
let output = pallas::ledger::primitives::babbage::TransactionOutput::PostAlonzo(pallas::ledger::primitives::babbage::PostAlonzoTransactionOutput {
address: pallas::ledger::addresses::Address::from_bech32("addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgse35a3x").unwrap().to_vec().into(),
value: pallas::ledger::primitives::babbage::Value::Coin(5_000_000),
datum_option: None,
script_ref: None,
});
}.into());

let cbor = pallas::codec::minicbor::to_vec(&output).unwrap();

Expand Down
8 changes: 4 additions & 4 deletions balius-runtime/src/sign/in_memory.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::collections::HashMap;

use pallas::crypto::key::ed25519;
use rand::rngs::OsRng;

use crate::wit::balius::app::sign as wit;

Expand Down Expand Up @@ -31,9 +30,10 @@ impl SignerProvider for Signer {
panic!("Unsupported algorithm")
}
let keys = self.map.entry(worker_id.to_string()).or_default();
let secret_key = keys
.entry(key_name)
.or_insert(ed25519::SecretKey::new(OsRng).into());
let secret_key = keys.entry(key_name).or_insert_with(|| {
let mut rng = rand::rng();
ed25519::SecretKey::new(&mut rng).into()
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.
secret_key.public_key()
}

Expand Down
10 changes: 5 additions & 5 deletions balius-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ readme = "README.md"
balius-core = { version = "0.5.2", path = "../balius-core" }
balius-macros = { version = "0.5.2", path = "../balius-macros" }
hex = "0.4.3"
pallas-addresses = { version = "0.32.0" }
pallas-codec = { version = "0.32.0" }
pallas-crypto = { version = "0.32.0" }
pallas-primitives = { version = "0.32.0" }
pallas-traverse = { version = "0.32.0" }
pallas-addresses = { version = "1.0.0" }
pallas-codec = { version = "1.0.0" }
pallas-crypto = { version = "1.0.0" }
pallas-primitives = { version = "1.0.0" }
pallas-traverse = { version = "1.0.0" }
prost = "0.13.3"
serde = { version = "1.0.210", features = ["derive"] }
serde_json = "1.0.128"
Expand Down
124 changes: 65 additions & 59 deletions balius-sdk/src/txbuilder/asset_math.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
use pallas_crypto::hash::Hash;
use pallas_primitives::{
conway::{self, Value},
AssetName, NonEmptyKeyValuePairs, NonZeroInt, PolicyId, PositiveCoin,
AssetName, NonZeroInt, PolicyId, PositiveCoin,
};
use std::collections::{hash_map::Entry, BTreeMap, HashMap};

use super::BuildError;

fn fold_assets<T>(
acc: &mut HashMap<pallas_codec::utils::Bytes, T>,
item: NonEmptyKeyValuePairs<pallas_codec::utils::Bytes, T>,
item: BTreeMap<pallas_codec::utils::Bytes, T>,
) where
T: SafeAdd + Copy,
{
for (key, value) in item.to_vec() {
for (key, value) in item {
match acc.entry(key) {
Entry::Occupied(mut entry) => {
if let Some(new_val) = value.try_add(*entry.get()) {
Expand All @@ -31,11 +31,11 @@ fn fold_assets<T>(

pub fn fold_multiassets<T>(
acc: &mut HashMap<Hash<28>, HashMap<pallas_codec::utils::Bytes, T>>,
item: NonEmptyKeyValuePairs<Hash<28>, NonEmptyKeyValuePairs<pallas_codec::utils::Bytes, T>>,
item: conway::Multiasset<T>,
) where
T: SafeAdd + Copy,
{
for (key, value) in item.to_vec() {
for (key, value) in item {
let mut map = acc.remove(&key).unwrap_or_default();
fold_assets(&mut map, value);
acc.insert(key, map);
Expand All @@ -54,15 +54,22 @@ where
fold_multiassets(&mut total_assets, assets);
}

let total_assets_vec = total_assets
let total_assets_vec: conway::Multiasset<T> = total_assets
.into_iter()
.filter_map(|(key, assets)| {
let assets_vec = assets.into_iter().collect();
Some((key, NonEmptyKeyValuePairs::from_vec(assets_vec)?))
if assets.is_empty() {
None
} else {
Some((key, assets.into_iter().collect()))
}
})
.collect();

NonEmptyKeyValuePairs::from_vec(total_assets_vec)
if total_assets_vec.is_empty() {
None
} else {
Some(total_assets_vec)
}
}

pub fn aggregate_values(items: impl IntoIterator<Item = Value>) -> Value {
Expand All @@ -89,43 +96,36 @@ pub fn aggregate_values(items: impl IntoIterator<Item = Value>) -> Value {
}

pub fn add_mint(value: &Value, mint: &conway::Mint) -> Result<Value, BuildError> {
let (coin, mut og_assets) = match value {
Value::Coin(c) => (*c, BTreeMap::new()),
Value::Multiasset(c, a) => {
let flattened: BTreeMap<&PolicyId, BTreeMap<&AssetName, u64>> = a
.iter()
.map(|(policy, assets)| {
let values = assets
.iter()
.map(move |(name, value)| (name, value.into()))
.collect();
(policy, values)
})
.collect();
(*c, flattened)
}
};
let mut final_assets = vec![];
let (coin, mut final_assets): (u64, BTreeMap<PolicyId, BTreeMap<AssetName, PositiveCoin>>) =
match value {
Value::Coin(c) => (*c, BTreeMap::new()),
Value::Multiasset(c, a) => (*c, a.clone()),
};

for (policy, mint_assets) in mint.iter() {
let assets = og_assets.remove(policy).unwrap_or_default();
let mut policy_assets = vec![];
let policy_assets = final_assets.entry(*policy).or_default();
for (name, value) in mint_assets.iter() {
let old_value = assets.get(name).copied().unwrap_or_default();
let old_value = policy_assets
.get(name)
.copied()
.map(u64::from)
.unwrap_or_default();
let minted: i64 = value.into();
let Some(new_value) = old_value.checked_add_signed(minted) else {
return Err(BuildError::OutputsTooHigh);
};
if let Ok(asset) = PositiveCoin::try_from(new_value) {
policy_assets.push((name.clone(), asset));
policy_assets.insert(name.clone(), asset);
} else {
policy_assets.remove(name);
}
}
if let Some(assets) = NonEmptyKeyValuePairs::from_vec(policy_assets) {
final_assets.push((*policy, assets));
}
}

if let Some(assets) = NonEmptyKeyValuePairs::from_vec(final_assets) {
Ok(Value::Multiasset(coin, assets))
final_assets.retain(|_, assets| !assets.is_empty());

if !final_assets.is_empty() {
Ok(Value::Multiasset(coin, final_assets))
} else {
Ok(Value::Coin(coin))
}
Expand Down Expand Up @@ -169,8 +169,8 @@ pub fn subtract_value(lhs: &Value, rhs: &Value) -> Result<Value, BuildError> {
policy_assets.push((name.clone(), final_coin));
}
}
if let Some(assets) = NonEmptyKeyValuePairs::from_vec(policy_assets) {
final_assets.push((*policy, assets));
if !policy_assets.is_empty() {
final_assets.push((*policy, policy_assets.into_iter().collect()));
}
}

Expand All @@ -179,8 +179,11 @@ pub fn subtract_value(lhs: &Value, rhs: &Value) -> Result<Value, BuildError> {
return Err(BuildError::OutputsTooHigh);
}

if let Some(assets) = NonEmptyKeyValuePairs::from_vec(final_assets) {
Ok(Value::Multiasset(final_coin, assets))
if !final_assets.is_empty() {
Ok(Value::Multiasset(
final_coin,
final_assets.into_iter().collect(),
))
} else {
Ok(Value::Coin(final_coin))
}
Expand Down Expand Up @@ -211,11 +214,11 @@ where
let quantity: NonZeroInt = f(quantity as i64).unwrap();
new_asset.push((name, quantity));
}
let asset = NonEmptyKeyValuePairs::from_vec(new_asset).unwrap();
let asset = new_asset.into_iter().collect();
new_assets.push((policy, asset));
}

Ok(NonEmptyKeyValuePairs::from_vec(new_assets).unwrap())
Ok(new_assets.into_iter().collect())
}

pub fn multiasset_coin_to_mint(
Expand Down Expand Up @@ -284,23 +287,25 @@ mod tests {

let value_a = Value::Multiasset(
100,
NonEmptyKeyValuePairs::Def(vec![(
[(
policy_id,
NonEmptyKeyValuePairs::Def(vec![(
asset_name.clone().into(),
50.try_into().unwrap(),
)]),
)]),
[(asset_name.clone().into(), 50.try_into().unwrap())]
.into_iter()
.collect(),
)]
.into_iter()
.collect(),
);
let value_b = Value::Multiasset(
200,
NonEmptyKeyValuePairs::Def(vec![(
[(
policy_id,
NonEmptyKeyValuePairs::Def(vec![(
asset_name.clone().into(),
30.try_into().unwrap(),
)]),
)]),
[(asset_name.clone().into(), 30.try_into().unwrap())]
.into_iter()
.collect(),
)]
.into_iter()
.collect(),
);

let result = aggregate_values(vec![value_a, value_b]);
Expand All @@ -309,13 +314,14 @@ mod tests {
result,
Value::Multiasset(
300,
NonEmptyKeyValuePairs::Def(vec![(
[(
policy_id,
NonEmptyKeyValuePairs::Def(vec![(
asset_name.clone().into(),
80.try_into().unwrap()
)]),
)]),
[(asset_name.clone().into(), 80.try_into().unwrap())]
.into_iter()
.collect()
)]
.into_iter()
.collect(),
)
);
}
Expand Down
56 changes: 14 additions & 42 deletions balius-sdk/src/txbuilder/build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use pallas_traverse::MultiEraValue;
use pallas_codec::utils::KeepRaw;
use std::collections::HashMap;
use std::sync::Arc;
use std::{collections::HashMap, ops::Deref as _};

use super::{
asset_math, primitives, BuildContext, BuildError, Ledger, PParams, TxExpr, TxoRef, UtxoPattern,
Expand Down Expand Up @@ -83,7 +83,7 @@ impl crate::txbuilder::Ledger for ExtLedgerFacade {
}
}

pub fn build<T, L>(mut tx: T, ledger: L) -> Result<primitives::Tx, BuildError>
pub fn build<T, L>(mut tx: T, ledger: L) -> Result<primitives::Tx<'static>, BuildError>
where
T: TxExpr,
L: Ledger + 'static,
Expand Down Expand Up @@ -113,7 +113,7 @@ where
.collect();
let utxos = ctx.ledger.read_utxos(&input_refs)?;
ctx.total_input =
asset_math::aggregate_values(utxos.txos().map(|txo| input_into_conway(&txo.value())));
asset_math::aggregate_values(utxos.txos().map(|txo| txo.value().into_conway()));
if let Some(mint) = &body.mint {
ctx.total_input = asset_math::add_mint(&ctx.total_input, mint)?;
}
Expand All @@ -132,64 +132,36 @@ where
let wit = tx.eval_witness_set(&ctx).unwrap();

let tx = primitives::Tx {
transaction_body: ctx.tx_body.take().unwrap(),
transaction_witness_set: wit,
transaction_body: KeepRaw::from(ctx.tx_body.take().unwrap()),
transaction_witness_set: KeepRaw::from(wit),
auxiliary_data: pallas_codec::utils::Nullable::Null,
success: true,
};

Ok(tx)
}

// TODO: this belongs in pallas-traverse
// https://github.com/txpipe/pallas/pull/545
fn input_into_conway(value: &MultiEraValue) -> primitives::Value {
use pallas_primitives::{alonzo, conway};
match value {
MultiEraValue::Byron(x) => conway::Value::Coin(*x),
MultiEraValue::AlonzoCompatible(x) => match x.deref() {
alonzo::Value::Coin(x) => conway::Value::Coin(*x),
alonzo::Value::Multiasset(x, assets) => {
let coin = *x;
let assets = assets
.iter()
.filter_map(|(k, v)| {
let v: Vec<(conway::Bytes, conway::PositiveCoin)> = v
.iter()
.filter_map(|(k, v)| Some((k.clone(), (*v).try_into().ok()?)))
.collect();
Some((*k, conway::NonEmptyKeyValuePairs::from_vec(v)?))
})
.collect();
if let Some(assets) = conway::NonEmptyKeyValuePairs::from_vec(assets) {
conway::Value::Multiasset(coin, assets)
} else {
conway::Value::Coin(coin)
}
}
},
MultiEraValue::Conway(x) => x.deref().clone(),
_ => panic!("unrecognized value"),
}
}

fn output_into_conway(output: &primitives::TransactionOutput) -> primitives::Value {
fn output_into_conway(output: &primitives::TransactionOutput<'_>) -> primitives::Value {
use pallas_primitives::{alonzo, conway};
match output {
primitives::TransactionOutput::Legacy(o) => match &o.amount {
alonzo::Value::Coin(c) => primitives::Value::Coin(*c),
alonzo::Value::Multiasset(c, assets) => {
let assets = assets
let assets: conway::Multiasset<conway::PositiveCoin> = assets
.iter()
.filter_map(|(k, v)| {
let v: Vec<(conway::Bytes, conway::PositiveCoin)> = v
.iter()
.filter_map(|(k, v)| Some((k.clone(), (*v).try_into().ok()?)))
.collect();
Some((*k, conway::NonEmptyKeyValuePairs::from_vec(v)?))
if v.is_empty() {
None
} else {
Some((*k, v.into_iter().collect()))
}
})
.collect();
if let Some(assets) = conway::NonEmptyKeyValuePairs::from_vec(assets) {
if !assets.is_empty() {
primitives::Value::Multiasset(*c, assets)
} else {
primitives::Value::Coin(*c)
Expand Down
Loading
Loading