Skip to content

Commit 09146b5

Browse files
authored
Merge pull request #28 from hyperledger/example-fixes
Fixes for issues found while documenting
2 parents deff38c + 233eced commit 09146b5

File tree

32 files changed

+365
-238
lines changed

32 files changed

+365
-238
lines changed

Cargo.lock

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

firefly-balius/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
[package]
22
name = "firefly-balius"
3-
version = "0.3.1"
3+
version = "0.4.0"
44
description = "Helpers to write contracts for the FireFly Cardano connector"
55
license-file.workspace = true
66
publish = false
77
edition = "2021"
88
repository = "https://github.com/hyperledger/firefly-cardano"
99

1010
[dependencies]
11-
balius-sdk = { git = "https://github.com/txpipe/balius.git", rev = "2a66c8c" }
11+
balius-sdk = "0.3.4"
1212
hex = "0.4"
1313
serde = { version = "1", features = ["derive"] }
1414
serde_json = "1"

firefly-balius/src/events.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,50 @@ use serde::{Deserialize, Serialize};
55

66
use crate::kv;
77

8+
/// A helper trait, which makes it easy to define custom events.
89
pub trait EventData: Serialize {
10+
/// The signature of this event.
11+
/// FireFly expects this to follow a certain format based on the schema of the event. For an event with the following schema:
12+
/// ```json
13+
/// {
14+
/// "name": "Created",
15+
/// "params": [
16+
/// {
17+
/// "name": "from",
18+
/// "schema": {
19+
/// "type": "string"
20+
/// }
21+
/// },
22+
/// {
23+
/// "name": "value",
24+
/// "schema": {
25+
/// "type": "number"
26+
/// }
27+
/// }
28+
/// ]
29+
/// }
30+
/// ```
31+
/// The signature should be `Created(string, number)`.
932
fn signature(&self) -> String;
1033
}
1134

35+
/**
36+
* A custom event. Pass this to [emit_events] to send it to consumers.
37+
*/
1238
#[derive(Serialize, Deserialize)]
1339
pub struct Event {
40+
/// The hash of the block which triggered this event.
1441
pub block_hash: Vec<u8>,
42+
/// The hash of the transaction which triggered this event.
1543
pub tx_hash: Vec<u8>,
44+
/// The signature of this event. See [EventData::signature] for details.
1645
pub signature: String,
46+
/// The data attached to this event.
1747
pub data: serde_json::Value,
1848
}
1949

2050
impl Event {
51+
/// Builds a new event for the given uTXO.
2152
pub fn new<D, E: EventData>(utxo: &Utxo<D>, data: &E) -> WorkerResult<Self> {
2253
Ok(Self {
2354
block_hash: utxo.block_hash.clone(),
@@ -28,6 +59,7 @@ impl Event {
2859
}
2960
}
3061

62+
/// Emits a group of custom events to the FireFly Cardano connector.
3163
pub fn emit_events(events: Vec<Event>) -> WorkerResult<()> {
3264
if events.is_empty() {
3365
return Ok(());

firefly-balius/src/kv.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use balius_sdk::{wit::balius::app::kv, WorkerResult};
22
use serde::{Deserialize, Serialize};
33

4+
/// Retrieve a value from the KV store. Returns None if the value does not already exist.
45
pub fn get<D: for<'a> Deserialize<'a>>(key: &str) -> WorkerResult<Option<D>> {
56
match kv::get_value(key) {
67
Ok(bytes) => Ok(Some(serde_json::from_slice(&bytes)?)),
@@ -9,6 +10,7 @@ pub fn get<D: for<'a> Deserialize<'a>>(key: &str) -> WorkerResult<Option<D>> {
910
}
1011
}
1112

13+
/// Store a value in the KV store.
1214
pub fn set<S: Serialize>(key: &str, value: &S) -> WorkerResult<()> {
1315
kv::set_value(key, &serde_json::to_vec(value)?)?;
1416
Ok(())

firefly-balius/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod logic;
44
mod monitor;
55
mod worker;
66

7+
pub use balius_sdk;
78
pub use events::*;
89
pub use logic::*;
910
pub use monitor::*;

firefly-balius/src/logic.rs

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,91 @@
1+
use std::collections::HashMap;
2+
13
use balius_sdk::txbuilder::{
2-
primitives::TransactionInput, BuildContext, BuildError, InputExpr, UtxoSource,
4+
primitives::TransactionInput, BuildContext, BuildError, InputExpr, UtxoSource, Value, ValueExpr,
35
};
46

5-
pub struct CoinSelectionInput(pub UtxoSource, pub u64);
7+
/// When used in a transaction, adds a minimal set of UTXOs with at least the given value (plus fees).
8+
pub struct CoinSelectionInput<V: ValueExpr>(pub UtxoSource, pub V);
69

710
// TODO: this is a naive approach to coin selection,
811
// balius can probably help with a better one
9-
impl InputExpr for CoinSelectionInput {
12+
impl<V: ValueExpr> InputExpr for CoinSelectionInput<V> {
1013
fn eval(&self, ctx: &BuildContext) -> Result<Vec<TransactionInput>, BuildError> {
1114
let utxos = self.0.resolve(ctx)?;
15+
let target_amount = self.1.eval(ctx)?;
16+
1217
// If we know the fee, add it to the target amount.
1318
// If not, overestimate the fee so we pick at least as many TXOs as needed.
14-
let target_lovelace = self.1
15-
+ if ctx.estimated_fee == 0 {
16-
2_000_000
17-
} else {
18-
ctx.estimated_fee
19-
};
19+
let fee = if ctx.estimated_fee == 0 {
20+
2_000_000
21+
} else {
22+
ctx.estimated_fee
23+
};
24+
let (target_lovelace, mut target_assets) = match target_amount {
25+
Value::Coin(c) => (c + fee, HashMap::new()),
26+
Value::Multiasset(c, assets) => {
27+
let asset_map = assets
28+
.into_iter()
29+
.map(|(policy_id, assets)| {
30+
(
31+
policy_id,
32+
assets
33+
.into_iter()
34+
.map(|(name, coin)| (name.to_vec(), u64::from(coin)))
35+
.collect::<HashMap<_, _>>(),
36+
)
37+
})
38+
.collect();
39+
(c + fee, asset_map)
40+
}
41+
};
2042

2143
let mut inputs = vec![];
2244
let mut lovelace_so_far = 0;
2345
let mut pairs: Vec<_> = utxos.iter().collect();
2446
pairs.sort_by_key(|(ref_, _)| (ref_.hash, ref_.index));
2547
for (ref_, txo) in pairs {
2648
let coin = txo.value().coin();
27-
if coin == 0 {
49+
let mut useful = coin != 0 && lovelace_so_far < target_lovelace;
50+
for policy_assets in txo.value().assets() {
51+
let Some(target_policy_assets) = target_assets.get_mut(policy_assets.policy())
52+
else {
53+
continue;
54+
};
55+
for asset in policy_assets.assets() {
56+
let Some(target_asset) = target_policy_assets.get_mut(asset.name()) else {
57+
continue;
58+
};
59+
let Some(amount) = asset.output_coin() else {
60+
continue;
61+
};
62+
if amount == 0 {
63+
continue;
64+
}
65+
useful = true;
66+
if amount < *target_asset {
67+
*target_asset -= amount;
68+
} else {
69+
target_policy_assets.remove(asset.name());
70+
}
71+
}
72+
if target_policy_assets.is_empty() {
73+
target_assets.remove(policy_assets.policy());
74+
}
75+
}
76+
if !useful {
2877
continue;
2978
}
3079
inputs.push(TransactionInput {
3180
transaction_id: ref_.hash,
3281
index: ref_.index,
3382
});
3483
lovelace_so_far += coin;
35-
if lovelace_so_far >= target_lovelace {
84+
if lovelace_so_far >= target_lovelace && target_assets.is_empty() {
3685
break;
3786
}
3887
}
39-
if lovelace_so_far >= target_lovelace {
88+
if lovelace_so_far >= target_lovelace && target_assets.is_empty() {
4089
Ok(inputs)
4190
} else {
4291
Err(BuildError::OutputsTooHigh)

firefly-balius/src/monitor.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ use serde::{Deserialize, Serialize};
55

66
use crate::kv;
77

8+
/// Interface which tells the framework to monitor specific transactions.
89
pub struct FinalityMonitor;
910

1011
impl FinalityMonitor {
12+
/// Tells the framework to monitor the given transaction.
13+
/// The current contract will emit events when the transaction has been accepted or rolled back,
14+
/// and also when it has been "finalized" (based on the condition passed here).
1115
pub fn monitor_tx(&self, hash: &str, condition: FinalizationCondition) -> WorkerResult<()> {
1216
let mut monitored_txs: HashMap<String, FinalizationCondition> =
1317
kv::get("__monitored_txs")?.unwrap_or_default();
@@ -17,7 +21,9 @@ impl FinalityMonitor {
1721
}
1822
}
1923

24+
/// How to decide when a transaction has been finalized
2025
#[derive(Serialize, Deserialize)]
2126
pub enum FinalizationCondition {
22-
AfterBlocks(usize),
27+
// Treat the transaction as finalized after the given number of blocks have reached the chain.
28+
AfterBlocks(u64),
2329
}

firefly-balius/src/worker.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
use std::marker::PhantomData;
22

3-
pub use balius_sdk;
43
use balius_sdk::{wit, Ack, Params, Worker, WorkerResult, _internal::Handler};
54
use serde::Deserialize;
65

6+
/// Parameters to the [WorkerExt::with_tx_submitted_handler] callback.
77
#[derive(Deserialize)]
88
pub struct SubmittedTx {
9+
/// The method which submitted this TX.
910
pub method: String,
11+
/// The hash of the submitted transaction.
1012
pub hash: String,
1113
}
1214

13-
pub struct SubmittedTxHandler<F, C>
15+
struct SubmittedTxHandler<F, C>
1416
where
1517
F: Fn(C, SubmittedTx) -> WorkerResult<Ack> + 'static,
1618
C: TryFrom<wit::Config>,
@@ -49,12 +51,16 @@ where
4951
}
5052
}
5153

54+
/// Helper methods on the Balius Worker.
5255
pub trait WorkerExt {
56+
/// Register a callback to run when a transaction has been submitted.
57+
/// The callback will be passed a [SubmittedTx] describing the transaction.
5358
fn with_tx_submitted_handler<C, F>(self, func: F) -> Self
5459
where
5560
C: TryFrom<wit::Config, Error = balius_sdk::Error> + Send + Sync + 'static,
5661
F: Fn(C, SubmittedTx) -> WorkerResult<Ack> + Send + Sync + 'static;
5762

63+
/// Register a callback to run whenever a new UTXO appears on-chain.
5864
fn with_new_txo_handler(self, handler: impl Handler) -> Self;
5965
}
6066

firefly-cardanoconnect/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "firefly-cardanoconnect"
3-
version = "0.3.1"
3+
version = "0.4.0"
44
description = "An implementation of the FireFly Connector API for Cardano"
55
license-file.workspace = true
66
publish = false
@@ -12,7 +12,7 @@ aide = { version = "0.14", features = ["axum", "axum-json", "axum-query"] }
1212
anyhow = "1"
1313
async-trait = "0.1"
1414
axum = { version = "0.8", features = ["macros", "ws"] }
15-
balius-runtime = { git = "https://github.com/txpipe/balius.git", rev = "2a66c8c" }
15+
balius-runtime = "0.3.4"
1616
blockfrost = { git = "https://github.com/SupernaviX/blockfrost-rust.git", rev = "cfa7a5e", default-features = false, features = ["rustls-tls"] }
1717
blockfrost-openapi = "0.1.69"
1818
clap = { version = "4", features = ["derive"] }

0 commit comments

Comments
 (0)