Skip to content

Commit d66d39c

Browse files
authored
Merge pull request #9 from mlabs-haskell/szg251/fix-lbf
Fix PlutusData serialisation to be in line with PlutusTx (and other lbf supported libs)
2 parents fb93fa5 + 4528da0 commit d66d39c

File tree

8 files changed

+171
-40
lines changed

8 files changed

+171
-40
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ edition = "2021"
77

88
[dependencies]
99
proptest = "1.3.1"
10-
lbr-prelude = { git = "https://github.com/mlabs-haskell/lambda-buffers.git", rev = "59d3fb6422fef707c66fb83c2bd29327371d9bd3", optional = true }
10+
lbr-prelude = { git = "https://github.com/mlabs-haskell/lambda-buffers.git", rev = "7adcef1ea98dbfe9b3c4c527db3c499e078cb4d6", optional = true }
1111
serde_json = { version = "1.0.107", features = [
1212
"arbitrary_precision",
1313
], optional = true }

src/generators/correct/v1.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -290,10 +290,6 @@ pub fn arb_transaction_output() -> impl Strategy<Value = TransactionOutput> {
290290

291291
/// Strategy to generate a TxInInfo
292292
pub fn arb_tx_in_info() -> impl Strategy<Value = TxInInfo> {
293-
(arb_transaction_input(), arb_transaction_output()).prop_map(|(transaction_input, resolved)| {
294-
TxInInfo {
295-
transaction_input,
296-
resolved,
297-
}
298-
})
293+
(arb_transaction_input(), arb_transaction_output())
294+
.prop_map(|(reference, output)| TxInInfo { reference, output })
299295
}

src/generators/correct/v2.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,6 @@ pub fn arb_output_datum() -> impl Strategy<Value = OutputDatum> {
3939

4040
/// Strategy to generate a TxInInfo
4141
pub fn arb_tx_in_info() -> impl Strategy<Value = TxInInfo> {
42-
(arb_transaction_input(), arb_transaction_output()).prop_map(|(transaction_input, resolved)| {
43-
TxInInfo {
44-
transaction_input,
45-
resolved,
46-
}
47-
})
42+
(arb_transaction_input(), arb_transaction_output())
43+
.prop_map(|(reference, output)| TxInInfo { reference, output })
4844
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ pub mod lamval;
55
pub mod plutus_data;
66
pub mod v1;
77
pub mod v2;
8+
#[cfg(feature = "lbf")]
9+
pub use lbr_prelude::json;

src/plutus_data.rs

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
//! Plutus Data related types and traits
22
#[cfg(feature = "lbf")]
3-
use lbr_prelude::json::Json;
3+
use data_encoding::HEXLOWER;
4+
#[cfg(feature = "lbf")]
5+
use lbr_prelude::error::Error;
6+
#[cfg(feature = "lbf")]
7+
use lbr_prelude::json::{
8+
case_json_constructor, case_json_object, json_constructor, json_object, Json,
9+
};
410
use num_bigint::BigInt;
511
use std::collections::{BTreeMap, BTreeSet};
612

@@ -9,7 +15,6 @@ use serde::{Deserialize, Serialize};
915

1016
/// Data representation of on-chain data such as Datums and Redeemers
1117
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
12-
#[cfg_attr(feature = "lbf", derive(Json))]
1318
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1419
pub enum PlutusData {
1520
Constr(BigInt, Vec<PlutusData>),
@@ -41,6 +46,122 @@ impl PlutusData {
4146
}
4247
}
4348

49+
#[cfg(feature = "lbf")]
50+
impl Json for PlutusData {
51+
fn to_json(&self) -> serde_json::Value {
52+
match self {
53+
PlutusData::Constr(index, fields) => json_constructor(
54+
"Constr",
55+
vec![json_object(vec![
56+
("index".to_string(), index.to_json()),
57+
("fields".to_string(), fields.to_json()),
58+
])],
59+
),
60+
PlutusData::Map(map) => json_constructor("Map", vec![map.to_json()]),
61+
PlutusData::List(list) => json_constructor("List", vec![list.to_json()]),
62+
PlutusData::Integer(int) => json_constructor("Integer", vec![int.to_json()]),
63+
PlutusData::Bytes(bytes) => {
64+
json_constructor("Bytes", vec![String::to_json(&HEXLOWER.encode(bytes))])
65+
}
66+
}
67+
}
68+
69+
fn from_json(value: &serde_json::Value) -> Result<PlutusData, Error> {
70+
case_json_constructor(
71+
"PlutusV1.PlutusData",
72+
vec![
73+
(
74+
"Constr",
75+
Box::new(|ctor_fields| match &ctor_fields[..] {
76+
[val] => case_json_object(
77+
|obj| {
78+
let index = obj.get("index").ok_or(Error::UnexpectedFieldName {
79+
wanted: "index".to_owned(),
80+
got: obj.keys().cloned().collect(),
81+
parser: "PlutusV1.PlutusData".to_owned(),
82+
})?;
83+
84+
let fields =
85+
obj.get("fields").ok_or(Error::UnexpectedFieldName {
86+
wanted: "fields".to_owned(),
87+
got: obj.keys().cloned().collect(),
88+
parser: "PlutusV1.PlutusData".to_owned(),
89+
})?;
90+
Ok(PlutusData::Constr(
91+
BigInt::from_json(index)?,
92+
<Vec<PlutusData>>::from_json(fields)?,
93+
))
94+
},
95+
val,
96+
),
97+
_ => Err(Error::UnexpectedArrayLength {
98+
wanted: 1,
99+
got: ctor_fields.len(),
100+
parser: "PlutusV1.PlutusData".to_owned(),
101+
}),
102+
}),
103+
),
104+
(
105+
"Map",
106+
Box::new(|ctor_fields| match &ctor_fields[..] {
107+
[val] => Ok(PlutusData::Map(Json::from_json(val)?)),
108+
_ => Err(Error::UnexpectedArrayLength {
109+
wanted: 1,
110+
got: ctor_fields.len(),
111+
parser: "PlutusV1.PlutusData".to_owned(),
112+
}),
113+
}),
114+
),
115+
(
116+
"List",
117+
Box::new(|ctor_fields| match &ctor_fields[..] {
118+
[val] => Ok(PlutusData::List(Json::from_json(val)?)),
119+
_ => Err(Error::UnexpectedArrayLength {
120+
wanted: 1,
121+
got: ctor_fields.len(),
122+
parser: "PlutusV1.PlutusData".to_owned(),
123+
}),
124+
}),
125+
),
126+
(
127+
"Integer",
128+
Box::new(|ctor_fields| match &ctor_fields[..] {
129+
[val] => Ok(PlutusData::Integer(Json::from_json(val)?)),
130+
_ => Err(Error::UnexpectedArrayLength {
131+
wanted: 1,
132+
got: ctor_fields.len(),
133+
parser: "PlutusV1.PlutusData".to_owned(),
134+
}),
135+
}),
136+
),
137+
(
138+
"Bytes",
139+
Box::new(|ctor_fields| match &ctor_fields[..] {
140+
[val] => {
141+
let bytes = String::from_json(val).and_then(|str| {
142+
HEXLOWER.decode(&str.into_bytes()).map_err(|_| {
143+
Error::UnexpectedJsonInvariant {
144+
wanted: "base16 string".to_owned(),
145+
got: "unexpected string".to_owned(),
146+
parser: "Plutus.V1.Bytes".to_owned(),
147+
}
148+
})
149+
})?;
150+
Ok(PlutusData::Bytes(bytes))
151+
}
152+
_ => Err(Error::UnexpectedArrayLength {
153+
wanted: 1,
154+
got: ctor_fields.len(),
155+
parser: "PlutusV1.PlutusData".to_owned(),
156+
}),
157+
}),
158+
),
159+
],
160+
value,
161+
)
162+
}
163+
}
164+
44165
/// Deserialise a Plutus data using parsers for each variant
45166
pub fn case_plutus_data<'a, T>(
46167
ctor_case: impl FnOnce(&'a BigInt) -> Box<dyn 'a + FnOnce(&'a Vec<PlutusData>) -> T>,

src/v1/transaction.rs

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,27 @@ pub struct TransactionHash(pub LedgerBytes);
7272

7373
impl IsPlutusData for TransactionHash {
7474
fn to_plutus_data(&self) -> PlutusData {
75-
self.0.to_plutus_data()
75+
PlutusData::Constr(BigInt::from(0), vec![self.0.to_plutus_data()])
7676
}
7777

7878
fn from_plutus_data(data: &PlutusData) -> Result<Self, PlutusDataError> {
79-
IsPlutusData::from_plutus_data(data).map(Self)
79+
match data {
80+
PlutusData::Constr(flag, fields) => match u32::try_from(flag) {
81+
Ok(0) => {
82+
verify_constr_fields(&fields, 1)?;
83+
Ok(TransactionHash(IsPlutusData::from_plutus_data(&fields[0])?))
84+
}
85+
_ => Err(PlutusDataError::UnexpectedPlutusInvariant {
86+
wanted: "Constr field to be 0".to_owned(),
87+
got: flag.to_string(),
88+
}),
89+
},
90+
91+
_ => Err(PlutusDataError::UnexpectedPlutusType {
92+
wanted: PlutusType::Constr,
93+
got: PlutusType::from(data),
94+
}),
95+
}
8096
}
8197
}
8298

@@ -89,8 +105,8 @@ impl IsPlutusData for TransactionHash {
89105
#[cfg_attr(feature = "lbf", derive(Json))]
90106
pub struct TransactionOutput {
91107
pub address: Address,
92-
pub datum_hash: Option<DatumHash>,
93108
pub value: Value,
109+
pub datum_hash: Option<DatumHash>,
94110
}
95111

96112
impl IsPlutusData for TransactionOutput {
@@ -99,8 +115,8 @@ impl IsPlutusData for TransactionOutput {
99115
BigInt::from(0),
100116
vec![
101117
self.address.to_plutus_data(),
102-
self.datum_hash.to_plutus_data(),
103118
self.value.to_plutus_data(),
119+
self.datum_hash.to_plutus_data(),
104120
],
105121
)
106122
}
@@ -112,8 +128,8 @@ impl IsPlutusData for TransactionOutput {
112128
verify_constr_fields(&fields, 3)?;
113129
Ok(TransactionOutput {
114130
address: Address::from_plutus_data(&fields[0])?,
115-
datum_hash: <Option<DatumHash>>::from_plutus_data(&fields[1])?,
116-
value: Value::from_plutus_data(&fields[2])?,
131+
value: Value::from_plutus_data(&fields[1])?,
132+
datum_hash: <Option<DatumHash>>::from_plutus_data(&fields[2])?,
117133
})
118134
}
119135
_ => Err(PlutusDataError::UnexpectedPlutusInvariant {
@@ -153,17 +169,17 @@ pub type POSIXTimeRange = PlutusInterval<POSIXTime>;
153169
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
154170
#[cfg_attr(feature = "lbf", derive(Json))]
155171
pub struct TxInInfo {
156-
pub transaction_input: TransactionInput,
157-
pub resolved: TransactionOutput,
172+
pub reference: TransactionInput,
173+
pub output: TransactionOutput,
158174
}
159175

160176
impl IsPlutusData for TxInInfo {
161177
fn to_plutus_data(&self) -> PlutusData {
162178
PlutusData::Constr(
163179
BigInt::from(0),
164180
vec![
165-
self.transaction_input.to_plutus_data(),
166-
self.resolved.to_plutus_data(),
181+
self.reference.to_plutus_data(),
182+
self.output.to_plutus_data(),
167183
],
168184
)
169185
}
@@ -174,8 +190,8 @@ impl IsPlutusData for TxInInfo {
174190
Ok(0) => {
175191
verify_constr_fields(&fields, 2)?;
176192
Ok(TxInInfo {
177-
transaction_input: TransactionInput::from_plutus_data(&fields[0])?,
178-
resolved: TransactionOutput::from_plutus_data(&fields[1])?,
193+
reference: TransactionInput::from_plutus_data(&fields[0])?,
194+
output: TransactionOutput::from_plutus_data(&fields[1])?,
179195
})
180196
}
181197
_ => Err(PlutusDataError::UnexpectedPlutusInvariant {

src/v2/transaction.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ use serde::{Deserialize, Serialize};
2323
#[cfg_attr(feature = "lbf", derive(Json))]
2424
pub struct TransactionOutput {
2525
pub address: Address,
26+
pub value: Value,
2627
pub datum: OutputDatum,
2728
pub reference_script: Option<ScriptHash>,
28-
pub value: Value,
2929
}
3030

3131
impl IsPlutusData for TransactionOutput {
@@ -34,9 +34,9 @@ impl IsPlutusData for TransactionOutput {
3434
BigInt::from(0),
3535
vec![
3636
self.address.to_plutus_data(),
37+
self.value.to_plutus_data(),
3738
self.datum.to_plutus_data(),
3839
self.reference_script.to_plutus_data(),
39-
self.value.to_plutus_data(),
4040
],
4141
)
4242
}
@@ -48,9 +48,9 @@ impl IsPlutusData for TransactionOutput {
4848
verify_constr_fields(&fields, 4)?;
4949
Ok(TransactionOutput {
5050
address: Address::from_plutus_data(&fields[0])?,
51-
datum: OutputDatum::from_plutus_data(&fields[1])?,
52-
reference_script: <Option<ScriptHash>>::from_plutus_data(&fields[2])?,
53-
value: Value::from_plutus_data(&fields[3])?,
51+
value: Value::from_plutus_data(&fields[1])?,
52+
datum: OutputDatum::from_plutus_data(&fields[2])?,
53+
reference_script: <Option<ScriptHash>>::from_plutus_data(&fields[3])?,
5454
})
5555
}
5656
_ => Err(PlutusDataError::UnexpectedPlutusInvariant {
@@ -72,17 +72,17 @@ impl IsPlutusData for TransactionOutput {
7272
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7373
#[cfg_attr(feature = "lbf", derive(Json))]
7474
pub struct TxInInfo {
75-
pub transaction_input: TransactionInput,
76-
pub resolved: TransactionOutput,
75+
pub reference: TransactionInput,
76+
pub output: TransactionOutput,
7777
}
7878

7979
impl IsPlutusData for TxInInfo {
8080
fn to_plutus_data(&self) -> PlutusData {
8181
PlutusData::Constr(
8282
BigInt::from(0),
8383
vec![
84-
self.transaction_input.to_plutus_data(),
85-
self.resolved.to_plutus_data(),
84+
self.reference.to_plutus_data(),
85+
self.output.to_plutus_data(),
8686
],
8787
)
8888
}
@@ -93,8 +93,8 @@ impl IsPlutusData for TxInInfo {
9393
Ok(0) => {
9494
verify_constr_fields(&fields, 2)?;
9595
Ok(TxInInfo {
96-
transaction_input: TransactionInput::from_plutus_data(&fields[0])?,
97-
resolved: TransactionOutput::from_plutus_data(&fields[1])?,
96+
reference: TransactionInput::from_plutus_data(&fields[0])?,
97+
output: TransactionOutput::from_plutus_data(&fields[1])?,
9898
})
9999
}
100100
_ => Err(PlutusDataError::UnexpectedPlutusInvariant {

0 commit comments

Comments
 (0)