more shuffling content between crates
This commit is contained in:
parent
7163525fb1
commit
7d96313386
|
@ -246,8 +246,9 @@ dependencies = [
|
||||||
"blockfrost-openapi",
|
"blockfrost-openapi",
|
||||||
"cardano-connect",
|
"cardano-connect",
|
||||||
"cardano-tx-builder",
|
"cardano-tx-builder",
|
||||||
|
"futures",
|
||||||
"hex",
|
"hex",
|
||||||
"minicbor",
|
"minicbor 0.25.1",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -262,7 +263,7 @@ dependencies = [
|
||||||
"bech32 0.11.0",
|
"bech32 0.11.0",
|
||||||
"cryptoxide 0.5.1",
|
"cryptoxide 0.5.1",
|
||||||
"hex",
|
"hex",
|
||||||
"minicbor",
|
"minicbor 0.25.1",
|
||||||
"pallas-addresses",
|
"pallas-addresses",
|
||||||
"pallas-codec",
|
"pallas-codec",
|
||||||
"pallas-crypto",
|
"pallas-crypto",
|
||||||
|
@ -1229,22 +1230,17 @@ version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bech32 0.11.0",
|
"bech32 0.11.0",
|
||||||
"blockfrost",
|
"cardano-tx-builder",
|
||||||
"blockfrost-openapi",
|
|
||||||
"clap",
|
"clap",
|
||||||
"cryptoxide 0.5.1",
|
"cryptoxide 0.5.1",
|
||||||
"hex",
|
"hex",
|
||||||
"minicbor",
|
"minicbor 2.1.1",
|
||||||
"pallas-addresses",
|
|
||||||
"pallas-codec",
|
|
||||||
"pallas-crypto",
|
"pallas-crypto",
|
||||||
"pallas-primitives",
|
|
||||||
"rand",
|
"rand",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"uplc",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1254,7 +1250,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cardano-tx-builder",
|
"cardano-tx-builder",
|
||||||
"hex",
|
"hex",
|
||||||
"minicbor",
|
"minicbor 0.25.1",
|
||||||
"rand",
|
"rand",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1347,6 +1343,12 @@ dependencies = [
|
||||||
"minicbor-derive",
|
"minicbor-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minicbor"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4f182275033b808ede9427884caa8e05fa7db930801759524ca7925bd8aa7a82"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minicbor-derive"
|
name = "minicbor-derive"
|
||||||
version = "0.15.3"
|
version = "0.15.3"
|
||||||
|
@ -1521,7 +1523,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b2737b05f0dbb6d197feeb26ef15d2567e54833184bd469f5655a0537da89fa"
|
checksum = "8b2737b05f0dbb6d197feeb26ef15d2567e54833184bd469f5655a0537da89fa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex",
|
"hex",
|
||||||
"minicbor",
|
"minicbor 0.25.1",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
|
|
|
@ -25,3 +25,4 @@ serde_json = "1.0.138"
|
||||||
tokio = { version = "1.47.1", features = ["full"] }
|
tokio = { version = "1.47.1", features = ["full"] }
|
||||||
cardano-tx-builder = { path = "../cardano-tx-builder" }
|
cardano-tx-builder = { path = "../cardano-tx-builder" }
|
||||||
cardano-connect = { path = "../cardano-connect" }
|
cardano-connect = { path = "../cardano-connect" }
|
||||||
|
futures = "0.3.31"
|
||||||
|
|
|
@ -4,15 +4,16 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
|
use futures::stream::{self, StreamExt};
|
||||||
|
|
||||||
use blockfrost::{BlockfrostAPI, Pagination};
|
use blockfrost::{BlockfrostAPI, Pagination};
|
||||||
use blockfrost_openapi::models::tx_content_output_amount_inner::TxContentOutputAmountInner;
|
use blockfrost_openapi::models::{address_utxo_content_inner::AddressUtxoContentInner, tx_content_output_amount_inner::TxContentOutputAmountInner};
|
||||||
use cardano_connect::CardanoConnect;
|
use cardano_connect::CardanoConnect;
|
||||||
use cardano_tx_builder::{
|
use cardano_tx_builder::{
|
||||||
Address, BuildParameters, Credential, DatumOrHash, Input, Network, Output, PlutusData, Utxo,
|
Address, BuildParameters, Credential, Datum, Input, Network, Output, PlutusData, Script, Utxo,
|
||||||
Value,
|
Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -109,22 +110,54 @@ impl Blockfrost {
|
||||||
&self,
|
&self,
|
||||||
datum_hash: &Option<String>,
|
datum_hash: &Option<String>,
|
||||||
inline_datum: &Option<String>,
|
inline_datum: &Option<String>,
|
||||||
) -> Result<Option<DatumOrHash>> {
|
) -> Result<Datum> {
|
||||||
if let Some(inline_datum) = inline_datum {
|
match (inline_datum, datum_hash) {
|
||||||
Ok(Some(DatumOrHash::Data(plutus_data_from_inline(
|
(None, None) => Ok(Datum::None),
|
||||||
inline_datum,
|
(Some(inline_datum), _) => Ok(Datum::Data(plutus_data_from_inline(inline_datum)?)),
|
||||||
)?)))
|
(_, Some(datum_hash)) => {
|
||||||
} else {
|
Ok(Datum::Data(self.plutus_data_from_hash(&datum_hash).await?))
|
||||||
if let Some(datum_hash) = datum_hash {
|
|
||||||
Ok(Some(DatumOrHash::Data(
|
|
||||||
self.plutus_data_from_hash(&datum_hash).await?,
|
|
||||||
)))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn resolve_utxo(
|
||||||
|
&self,
|
||||||
|
bf_utxo: AddressUtxoContentInner,
|
||||||
|
) -> Result<Utxo> {
|
||||||
|
let datum = self
|
||||||
|
.resolve_datum(&bf_utxo.data_hash, &bf_utxo.inline_datum)
|
||||||
|
.await?;
|
||||||
|
let script_ref = match &bf_utxo.reference_script_hash {
|
||||||
|
None => None,
|
||||||
|
Some(hash) => Some(self.resolve_script(&hash).await?),
|
||||||
|
};
|
||||||
|
Ok(Utxo {
|
||||||
|
input: Input {
|
||||||
|
transaction_id: v2a(hex::decode(&bf_utxo.tx_hash)?)?.into(),
|
||||||
|
index: bf_utxo.tx_index as u64,
|
||||||
|
},
|
||||||
|
output: Output {
|
||||||
|
address: Address::from_bech32(&bf_utxo.address)?,
|
||||||
|
value: from_tx_content_output_amounts(&bf_utxo.amount[..])?,
|
||||||
|
datum,
|
||||||
|
script_ref,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Blockfrost client has the wrong type.
|
||||||
|
pub async fn resolve_script(&self, script_hash: &str) -> Result<Script> {
|
||||||
|
let plutus_version = self.plutus_version(script_hash);
|
||||||
|
let bytes = self.scripts_hash_cbor(script_hash);
|
||||||
|
match plutus_version.await? {
|
||||||
|
1 => Ok(Script::V1(bytes.await?)),
|
||||||
|
2 => Ok(Script::V2(bytes.await?)),
|
||||||
|
3 => Ok(Script::V3(bytes.await?)),
|
||||||
|
_ => Err(anyhow!("Unknown script")),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// Blockfrost client has the wrong type.
|
/// Blockfrost client has the wrong type.
|
||||||
pub async fn scripts_hash_cbor(&self, script_hash: &str) -> Result<Vec<u8>> {
|
pub async fn scripts_hash_cbor(&self, script_hash: &str) -> Result<Vec<u8>> {
|
||||||
let response = self
|
let response = self
|
||||||
|
@ -146,27 +179,43 @@ impl Blockfrost {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /// Blockfrost client has incomplete type
|
/// Blockfrost client has incomplete type
|
||||||
// pub async fn script_type(&self, script_hash : &str) -> Result<PlutusVersion>{
|
pub async fn plutus_version(&self, script_hash: &str) -> Result<u8> {
|
||||||
// let response = self
|
let response = self
|
||||||
// .client
|
.client
|
||||||
// .get(&format!( "{}/scripts/{}/cbor", self.base_url, script_hash))
|
.get(&format!("{}/scripts/{}/cbor", self.base_url, script_hash))
|
||||||
// .header("Accept", "application/json")
|
.header("Accept", "application/json")
|
||||||
// .header("project_id", self.project_id.as_str())
|
.header("project_id", self.project_id.as_str())
|
||||||
// .send()
|
.send()
|
||||||
// .await
|
.await
|
||||||
// .unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// match response.status() {
|
match response.status() {
|
||||||
// reqwest::StatusCode::OK => {
|
reqwest::StatusCode::OK => {
|
||||||
// let ResponseScript { plutus_type,.. } = response.json::<ResponseScript>().await.unwrap();
|
let ResponseScript { plutus_type, .. } =
|
||||||
// }
|
response.json::<ResponseScript>().await.unwrap();
|
||||||
// _ => Err(anyhow!("No script found")),
|
match plutus_type.as_str() {
|
||||||
// }
|
"plutusV1" => Ok(1),
|
||||||
// }
|
"plutusV2" => Ok(2),
|
||||||
|
"plutusV3" => Ok(3),
|
||||||
|
"plutusV4" => Ok(4),
|
||||||
|
_ => Err(anyhow!("Unknown plutus version")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(anyhow!("No script found")),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CardanoConnect for Blockfrost {
|
impl CardanoConnect for Blockfrost {
|
||||||
|
fn network(&self) -> Network {
|
||||||
|
self.network
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn health(&self) -> Result<String> {
|
||||||
|
Ok(format!("{:?}", self.api.health().await?))
|
||||||
|
}
|
||||||
|
|
||||||
async fn build_parameters(&self) -> BuildParameters {
|
async fn build_parameters(&self) -> BuildParameters {
|
||||||
let params = self
|
let params = self
|
||||||
.api
|
.api
|
||||||
|
@ -228,44 +277,21 @@ impl CardanoConnect for Blockfrost {
|
||||||
payment: &Credential,
|
payment: &Credential,
|
||||||
delegation: &Option<Credential>,
|
delegation: &Option<Credential>,
|
||||||
) -> Result<Vec<Utxo>> {
|
) -> Result<Vec<Utxo>> {
|
||||||
let addr = Address::new(self.network_id(), payment.clone(), delegation.clone());
|
let addr = Address::new(self.network(), payment.clone(), delegation.clone());
|
||||||
let response = self
|
let response = self
|
||||||
.api
|
.api
|
||||||
.addresses_utxos(&addr.to_bech32()?, Pagination::all())
|
.addresses_utxos(&addr.to_bech32(), Pagination::all())
|
||||||
.await?;
|
.await?;
|
||||||
response
|
let s = stream::iter(response)
|
||||||
.iter()
|
.map(move |bf_utxo| self.resolve_utxo(bf_utxo))
|
||||||
.map(|o| {
|
.buffer_unordered(10)
|
||||||
// FIXME: This should pull the datum and script reference
|
.collect::<Vec<Result<Utxo>>>()
|
||||||
let datum = None;
|
.await;
|
||||||
let script_ref = None;
|
s.into_iter().collect::<Result<Vec<Utxo>>>()
|
||||||
Ok(Utxo {
|
|
||||||
input: Input {
|
|
||||||
transaction_id: v2a(hex::decode(&o.tx_hash)?)?.into(),
|
|
||||||
index: o.tx_index as u64,
|
|
||||||
},
|
|
||||||
output: Output {
|
|
||||||
address: Address::from_bech32(&o.address).into(),
|
|
||||||
value: from_tx_content_output_amounts(&o.amount[..]),
|
|
||||||
datum,
|
|
||||||
script_ref,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn submit(&self, tx: Vec<u8>) -> Result<String> {
|
async fn submit(&self, tx: Vec<u8>) -> Result<String> {
|
||||||
let tx_hash = self.api.transactions_submit(tx).await?;
|
Ok(self.api.transactions_submit(tx).await?)
|
||||||
Ok(tx_hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn network(&self) -> Network {
|
|
||||||
self.network
|
|
||||||
}
|
|
||||||
|
|
||||||
fn health(&self) -> impl std::future::Future<Output = Result<String, String>> + Send {
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,56 +308,27 @@ struct ResponseScript {
|
||||||
serialised_size: u64,
|
serialised_size: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_tx_content_output_amounts(xs: &[TxContentOutputAmountInner]) -> Value {
|
fn from_tx_content_output_amounts(xs: &[TxContentOutputAmountInner]) -> Result<Value> {
|
||||||
/// FIXME : Using the sane version of value
|
let mut v = Value::zero();
|
||||||
let mut lovelaces = 0;
|
|
||||||
let mut assets = BTreeMap::new();
|
|
||||||
|
|
||||||
for asset in xs {
|
for asset in xs {
|
||||||
let quantity: u64 = asset.quantity.parse().unwrap();
|
let amount: i128 = asset.quantity.parse()?;
|
||||||
if asset.unit == UNIT_LOVELACE {
|
if asset.unit == UNIT_LOVELACE {
|
||||||
lovelaces += quantity;
|
v.add_lovelace(amount);
|
||||||
} else {
|
} else {
|
||||||
let policy_id: PolicyId = asset.unit[0..56].parse().unwrap();
|
let hash: [u8; 28] = v2a(hex::decode(&asset.unit[0..56])?)?;
|
||||||
let asset_name: AssetName = hex::decode(&asset.unit[56..]).unwrap().into();
|
let name: Vec<u8> = hex::decode(&asset.unit[56..])?;
|
||||||
assets
|
v.add_asset(hash, name, amount);
|
||||||
.entry(policy_id)
|
|
||||||
.and_modify(|m: &mut BTreeMap<AssetName, u64>| {
|
|
||||||
m.entry(asset_name.clone())
|
|
||||||
.and_modify(|q| *q += quantity)
|
|
||||||
.or_insert(quantity);
|
|
||||||
})
|
|
||||||
.or_insert_with(|| BTreeMap::from([(asset_name, quantity)]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(v)
|
||||||
if assets.is_empty() {
|
|
||||||
Value::Coin(lovelaces)
|
|
||||||
} else {
|
|
||||||
Value::Multiasset(
|
|
||||||
lovelaces,
|
|
||||||
NonEmptyKeyValuePairs::Def(
|
|
||||||
assets
|
|
||||||
.into_iter()
|
|
||||||
.map(|(policy_id, policies)| {
|
|
||||||
(
|
|
||||||
policy_id,
|
|
||||||
NonEmptyKeyValuePairs::Def(
|
|
||||||
policies
|
|
||||||
.into_iter()
|
|
||||||
.map(|(asset_name, quantity)| {
|
|
||||||
(asset_name, quantity.try_into().unwrap())
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn plutus_data_from_inline(inline_datum: &str) -> Result<PlutusData> {
|
pub fn plutus_data_from_inline(inline_datum: &str) -> Result<PlutusData> {
|
||||||
Ok(minicbor::decode(&hex::decode(inline_datum)?)?)
|
Ok(minicbor::decode(&hex::decode(inline_datum)?)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handles the map error
|
||||||
|
fn v2a<T, const N: usize>(v: Vec<T>) -> Result<[T; N]> {
|
||||||
|
<[T; N]>::try_from(v)
|
||||||
|
.map_err(|v: Vec<T>| anyhow!("Expected a Vec of length {}, but got {}", N, v.len()))
|
||||||
|
}
|
||||||
|
|
|
@ -9,12 +9,12 @@ use cardano_tx_builder::{
|
||||||
|
|
||||||
pub trait CardanoConnect {
|
pub trait CardanoConnect {
|
||||||
fn network(&self) -> Network;
|
fn network(&self) -> Network;
|
||||||
|
fn health(&self) -> impl std::future::Future<Output = Result<String>> + Send;
|
||||||
fn build_parameters(&self) -> impl std::future::Future<Output = BuildParameters> + Send;
|
fn build_parameters(&self) -> impl std::future::Future<Output = BuildParameters> + Send;
|
||||||
fn utxos_at(
|
fn utxos_at(
|
||||||
&self,
|
&self,
|
||||||
payment: &Credential,
|
payment: &Credential,
|
||||||
delegation: Option<&Credential>,
|
delegation: &Option<Credential>,
|
||||||
) -> impl std::future::Future<Output = Result<Vec<Utxo>>> + Send;
|
) -> impl std::future::Future<Output = Result<Vec<Utxo>>> + Send;
|
||||||
fn health(&self) -> impl std::future::Future<Output = Result<String, String>> + Send;
|
|
||||||
fn submit(&self, tx: Vec<u8>) -> impl std::future::Future<Output = Result<String>> + Send;
|
fn submit(&self, tx: Vec<u8>) -> impl std::future::Future<Output = Result<String>> + Send;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
use pallas_addresses;
|
use pallas_addresses;
|
||||||
pub use pallas_addresses::Network;
|
pub use pallas_addresses::Network;
|
||||||
use pallas_crypto::hash::Hash;
|
|
||||||
use pallas_primitives::NetworkId;
|
use pallas_primitives::NetworkId;
|
||||||
|
|
||||||
use crate::utils::v2a;
|
use crate::utils::v2a;
|
||||||
|
@ -29,10 +28,10 @@ fn parse_network(header: u8) -> Network {
|
||||||
|
|
||||||
/// Credential
|
/// Credential
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Credential {
|
pub enum Credential {
|
||||||
Key(Hash<28>),
|
Key([u8; 28]),
|
||||||
Script(Hash<28>),
|
Script([u8; 28]),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<pallas_addresses::ShelleyPaymentPart> for Credential {
|
impl Into<pallas_addresses::ShelleyPaymentPart> for Credential {
|
||||||
|
@ -78,13 +77,19 @@ impl Address {
|
||||||
let x = Address::try_from(bytes)?;
|
let x = Address::try_from(bytes)?;
|
||||||
Ok(x)
|
Ok(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_bech32(self) -> String {
|
||||||
|
Into::<pallas_addresses::ShelleyAddress>::into(self)
|
||||||
|
.to_bech32()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Vec<u8>> for Address {
|
impl TryFrom<Vec<u8>> for Address {
|
||||||
fn try_from(bytes: Vec<u8>) -> std::result::Result<Self, Self::Error> {
|
fn try_from(bytes: Vec<u8>) -> std::result::Result<Self, Self::Error> {
|
||||||
let header = *bytes.first().expect("Missing bytes");
|
let header = *bytes.first().expect("Missing bytes");
|
||||||
let network = parse_network(header);
|
let network = parse_network(header);
|
||||||
let payment: Hash<28> = v2a(bytes[1..=28].to_vec())?.into();
|
let payment: [u8; 28] = v2a(bytes[1..=28].to_vec())?.into();
|
||||||
let delegation = v2a(bytes[28..].to_vec());
|
let delegation = v2a(bytes[28..].to_vec());
|
||||||
match header & 0b1111_0000 {
|
match header & 0b1111_0000 {
|
||||||
0b0000_0000 => Ok(Address::new(
|
0b0000_0000 => Ok(Address::new(
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
use cryptoxide::digest::Digest;
|
||||||
|
use cryptoxide::sha2::Sha256;
|
||||||
|
use cryptoxide::blake2b::Blake2b;
|
||||||
|
|
||||||
|
pub fn sha2_256(msg: &[u8]) -> [u8;32] {
|
||||||
|
let mut output = [0;32];
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.input(msg);
|
||||||
|
hasher.result(&mut output);
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blake2b_224(msg: &[u8]) -> [u8;28] {
|
||||||
|
let mut output = [0;28];
|
||||||
|
let mut hasher = Blake2b::new(28);
|
||||||
|
hasher.input(msg);
|
||||||
|
hasher.result(&mut output);
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blake2b_256(msg: &[u8]) -> [u8;32] {
|
||||||
|
let mut output = [0;32];
|
||||||
|
let mut hasher = Blake2b::new(32);
|
||||||
|
hasher.input(msg);
|
||||||
|
hasher.result(&mut output);
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
|
@ -4,21 +4,19 @@ use super::pallas::era;
|
||||||
use super::plutus_data::PlutusData;
|
use super::plutus_data::PlutusData;
|
||||||
|
|
||||||
/// Datum
|
/// Datum
|
||||||
/// We are not being too clever with this.
|
/// As it may appear on a utxo
|
||||||
/// We make use of rust's `Optional` for the no datum case.
|
pub enum Datum {
|
||||||
/// We are yet to distinguish cases where we need the data,
|
None,
|
||||||
/// in place of the hash.
|
|
||||||
/// It should be documented.
|
|
||||||
pub enum DatumOrHash {
|
|
||||||
Hash([u8; 32]), // Check this
|
Hash([u8; 32]), // Check this
|
||||||
Data(PlutusData),
|
Data(PlutusData),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<era::DatumOption> for DatumOrHash {
|
impl Into<Option<era::DatumOption>> for Datum {
|
||||||
fn into(self) -> era::DatumOption {
|
fn into(self) -> Option<era::DatumOption> {
|
||||||
match self {
|
match self {
|
||||||
DatumOrHash::Hash(hash) => era::DatumOption::Hash(hash.into()),
|
Datum::None => None,
|
||||||
DatumOrHash::Data(plutus_data) => era::DatumOption::Data(CborWrap(plutus_data)),
|
Datum::Hash(hash) => Some(era::DatumOption::Hash(hash.into())),
|
||||||
|
Datum::Data(plutus_data) => Some(era::DatumOption::Data(CborWrap(plutus_data))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
use cryptoxide::ed25519;
|
||||||
|
|
||||||
|
use super::crypto::blake2b_224;
|
||||||
|
use super::address::{Credential};
|
||||||
|
|
||||||
|
pub struct Skey(pub [u8; 32]);
|
||||||
|
pub struct Vkey(pub [u8; 32]);
|
||||||
|
|
||||||
|
impl Skey {
|
||||||
|
pub fn vkey(&self) -> Vkey {
|
||||||
|
Vkey(ed25519::keypair(&self.0).1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sign(&self, msg: &[u8]) -> [u8;64] {
|
||||||
|
ed25519::signature(msg.as_ref(), &ed25519::keypair(&self.0).0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vkey {
|
||||||
|
pub fn hash(&self) -> [u8;28] {
|
||||||
|
blake2b_224(&self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify(&self, msg: &[u8], sig: &[u8;64]) -> bool {
|
||||||
|
ed25519::verify(msg, &self.0, sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn credential(&self) -> Credential {
|
||||||
|
Credential::Key(self.hash())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,12 @@
|
||||||
mod pallas;
|
mod pallas;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
pub mod crypto;
|
||||||
|
pub use crypto::{blake2b_224, blake2b_256, sha2_256};
|
||||||
|
|
||||||
|
pub mod key;
|
||||||
|
pub use key::{Skey, Vkey};
|
||||||
|
|
||||||
pub mod plutus_data;
|
pub mod plutus_data;
|
||||||
pub use plutus_data::PlutusData;
|
pub use plutus_data::PlutusData;
|
||||||
|
|
||||||
|
@ -14,7 +20,7 @@ pub mod value;
|
||||||
pub use value::{MultiAsset, Value};
|
pub use value::{MultiAsset, Value};
|
||||||
|
|
||||||
pub mod datum;
|
pub mod datum;
|
||||||
pub use datum::DatumOrHash;
|
pub use datum::Datum;
|
||||||
|
|
||||||
pub mod script;
|
pub mod script;
|
||||||
pub use script::Script;
|
pub use script::Script;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use super::pallas::era;
|
use super::pallas::era;
|
||||||
|
|
||||||
use super::address::Address;
|
use super::address::Address;
|
||||||
use super::datum::DatumOrHash;
|
use super::datum::Datum;
|
||||||
use super::script::Script;
|
use super::script::Script;
|
||||||
use super::value::Value;
|
use super::value::Value;
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ use super::value::Value;
|
||||||
pub struct Output {
|
pub struct Output {
|
||||||
pub address: Address,
|
pub address: Address,
|
||||||
pub value: Value,
|
pub value: Value,
|
||||||
pub datum: Option<DatumOrHash>,
|
pub datum: Datum,
|
||||||
pub script_ref: Option<Script>,
|
pub script_ref: Option<Script>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,8 +18,8 @@ impl Into<era::PostAlonzoTransactionOutput> for Output {
|
||||||
let address: pallas_addresses::ShelleyAddress = self.address.into();
|
let address: pallas_addresses::ShelleyAddress = self.address.into();
|
||||||
era::PostAlonzoTransactionOutput {
|
era::PostAlonzoTransactionOutput {
|
||||||
address: address.to_vec().into(),
|
address: address.to_vec().into(),
|
||||||
datum_option: self.datum.map(|x| x.into()),
|
datum_option: self.datum.into(),
|
||||||
value: self.value.into(),
|
value: self.value.try_into().expect("Failed to coerce value"),
|
||||||
script_ref: self.script_ref.map(|x| x.into()),
|
script_ref: self.script_ref.map(|x| x.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,71 @@
|
||||||
// TODO: Write some sane value handling fuctions:
|
// TODO: Write some sane value handling fuctions:
|
||||||
// - add
|
|
||||||
// - sub
|
|
||||||
// - prune
|
// - prune
|
||||||
// - split (negative, positive)
|
// - split (negative, positive)
|
||||||
// - is_positive
|
// - is_positive
|
||||||
|
|
||||||
use super::pallas::era;
|
use super::pallas::era;
|
||||||
use std::collections::HashMap;
|
use anyhow::{Error, Result};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
/// Naive version of value
|
/// Naive version of value
|
||||||
pub type MultiAsset = HashMap<[u8; 28], HashMap<Vec<u8>, u64>>;
|
pub type MultiAsset = BTreeMap<[u8; 28], BTreeMap<Vec<u8>, i128>>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Value(pub u64, pub MultiAsset);
|
pub struct Value(pub i128, pub MultiAsset);
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
pub fn add(self, _other: Value) -> Self {
|
pub fn zero() -> Self {
|
||||||
todo!()
|
Value(0, BTreeMap::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sub(self, _other: Value) -> Self {
|
pub fn add_lovelace(&mut self, amount: i128) {
|
||||||
todo!()
|
self.0 = self.0 + amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_asset(&mut self, hash: [u8; 28], name: Vec<u8>, amount: i128) {
|
||||||
|
self.1
|
||||||
|
.entry(hash)
|
||||||
|
.and_modify(|tree| {
|
||||||
|
tree.entry(name.clone())
|
||||||
|
.and_modify(|curr| *curr += amount)
|
||||||
|
.or_insert(amount);
|
||||||
|
})
|
||||||
|
.or_insert(vec![(name, amount)].into_iter().collect());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, other: Value) {
|
||||||
|
self.0 += other.0;
|
||||||
|
for (k0, v0) in other.1.iter() {
|
||||||
|
self.1
|
||||||
|
.entry(k0.clone())
|
||||||
|
.and_modify(|curr| {
|
||||||
|
for (k1, v1) in v0.iter() {
|
||||||
|
curr.entry(k1.clone())
|
||||||
|
.and_modify(|quantity| *quantity += v1)
|
||||||
|
.or_insert(v1.clone());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.or_insert(v0.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn neg(&mut self) {
|
||||||
|
self.0 = -self.0;
|
||||||
|
for class in self.1.values_mut() {
|
||||||
|
for quantity in class.values_mut() {
|
||||||
|
*quantity = 0 - *quantity;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<era::Value> for Value {
|
impl TryInto<era::Value> for Value {
|
||||||
fn into(self) -> era::Value {
|
fn try_into(self) -> Result<era::Value> {
|
||||||
todo!()
|
if self.1.is_empty() {
|
||||||
|
Ok(era::Value::Coin(self.0.try_into()?))
|
||||||
|
} else {
|
||||||
|
panic!("Not yet implemented")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
type Error = Error;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,12 @@ bech32 = "0.11.0"
|
||||||
clap = { version = "4.5.18", features = ["cargo", "derive"] }
|
clap = { version = "4.5.18", features = ["cargo", "derive"] }
|
||||||
cryptoxide = "0.5.1"
|
cryptoxide = "0.5.1"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
minicbor = { version = "0.25.1", features = ["alloc", "derive"] }
|
pallas-crypto = "0.33.0"
|
||||||
|
# minicbor = { version = "0.25.1", features = ["alloc", "derive"] }
|
||||||
rand = "0.9.2"
|
rand = "0.9.2"
|
||||||
reqwest = { version = "0.12.23", features = ["json"] }
|
reqwest = { version = "0.12.23", features = ["json"] }
|
||||||
serde = { version = "1.0.213", features = ["derive"] }
|
serde = { version = "1.0.213", features = ["derive"] }
|
||||||
serde_json = "1.0.138"
|
serde_json = "1.0.138"
|
||||||
tokio = { version = "1.47.1", features = ["full"] }
|
tokio = { version = "1.47.1", features = ["full"] }
|
||||||
|
cardano-tx-builder = { path = "../cardano-tx-builder" }
|
||||||
|
minicbor = "2.1.1"
|
||||||
|
|
|
@ -2,14 +2,14 @@ use clap::Subcommand;
|
||||||
|
|
||||||
mod cardano;
|
mod cardano;
|
||||||
mod data;
|
mod data;
|
||||||
mod tx;
|
// mod tx;
|
||||||
mod wallet;
|
mod wallet;
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum Cmd {
|
pub enum Cmd {
|
||||||
/// Txs
|
/// Txs
|
||||||
#[command(subcommand)]
|
// #[command(subcommand)]
|
||||||
Tx(tx::Cmd),
|
// Tx(tx::Cmd),
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Data(data::Cmd),
|
Data(data::Cmd),
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
|
@ -20,7 +20,7 @@ pub enum Cmd {
|
||||||
|
|
||||||
pub fn handle(cmd: Cmd) {
|
pub fn handle(cmd: Cmd) {
|
||||||
match cmd {
|
match cmd {
|
||||||
Cmd::Tx(inner) => tx::handle(inner),
|
// Cmd::Tx(inner) => tx::handle(inner),
|
||||||
Cmd::Data(inner) => data::handle(inner),
|
Cmd::Data(inner) => data::handle(inner),
|
||||||
Cmd::Cardano(inner) => cardano::handle(inner),
|
Cmd::Cardano(inner) => cardano::handle(inner),
|
||||||
Cmd::Wallet(inner) => wallet::handle(inner),
|
Cmd::Wallet(inner) => wallet::handle(inner),
|
||||||
|
|
|
@ -14,3 +14,4 @@ pub fn get_env() -> HashMap<String, String> {
|
||||||
}
|
}
|
||||||
env
|
env
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,51 +1,27 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use cryptoxide::hashing::blake2b_256;
|
|
||||||
use minicbor::encode;
|
|
||||||
use pallas_addresses::ShelleyPaymentPart;
|
|
||||||
use pallas_crypto::hash::{Hash, Hasher};
|
|
||||||
use pallas_crypto::key::ed25519::Signature;
|
|
||||||
use pallas_crypto::{self, key::ed25519::SecretKey};
|
|
||||||
use pallas_primitives::conway::{Tx, VKeyWitness};
|
|
||||||
|
|
||||||
use crate::tx::plutus::non_empty_set;
|
|
||||||
use crate::utils::v2a;
|
use crate::utils::v2a;
|
||||||
|
|
||||||
|
use cardano_tx_builder::Skey;
|
||||||
use rand::{rngs::OsRng, TryRngCore};
|
use rand::{rngs::OsRng, TryRngCore};
|
||||||
|
|
||||||
const PREFIX: &str = "wallet_";
|
const PREFIX: &str = "wallet_";
|
||||||
|
|
||||||
pub struct Wallet {
|
pub struct Wallet {
|
||||||
pub skey: [u8; 32],
|
pub skey: Skey
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wallet {
|
impl Wallet {
|
||||||
pub fn vkey(&self) -> [u8; 32] {
|
// pub fn sign(&self, tx: &mut Tx) {
|
||||||
SecretKey::from(self.skey).public_key().into()
|
// let mut msg = Vec::new();
|
||||||
}
|
// encode(&tx.transaction_body, &mut msg).unwrap();
|
||||||
|
// let tx_hash = blake2b_256(&msg);
|
||||||
pub fn key_hash(&self) -> Hash<28> {
|
// let sig = self.sign_hash(&tx_hash);
|
||||||
Hasher::<224>::hash(SecretKey::from(self.skey).public_key().as_ref())
|
// tx.transaction_witness_set.vkeywitness = non_empty_set(vec![VKeyWitness {
|
||||||
}
|
// vkey: self.vkey().to_vec().into(),
|
||||||
|
// signature: sig.as_ref().to_vec().into(),
|
||||||
pub fn payment_credential(&self) -> ShelleyPaymentPart {
|
// }])
|
||||||
ShelleyPaymentPart::Key(self.key_hash())
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sign_hash(&self, h: &[u8; 32]) -> Signature {
|
|
||||||
SecretKey::from(self.skey).sign(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sign(&self, tx: &mut Tx) {
|
|
||||||
let mut msg = Vec::new();
|
|
||||||
encode(&tx.transaction_body, &mut msg).unwrap();
|
|
||||||
let tx_hash = blake2b_256(&msg);
|
|
||||||
let sig = self.sign_hash(&tx_hash);
|
|
||||||
tx.transaction_witness_set.vkeywitness = non_empty_set(vec![VKeyWitness {
|
|
||||||
vkey: self.vkey().to_vec().into(),
|
|
||||||
signature: sig.as_ref().to_vec().into(),
|
|
||||||
}])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_env(env: &HashMap<String, String>) -> Wallet {
|
pub fn from_env(env: &HashMap<String, String>) -> Wallet {
|
||||||
|
|
Loading…
Reference in New Issue