Skip to content

Commit cef57f1

Browse files
committed
Merge remote-tracking branch 'origin/master' into tx-builder-max-output-size-fixes
# Conflicts: # rust/src/tx_builder.rs
2 parents d4f7b2e + 6238440 commit cef57f1

File tree

9 files changed

+294
-83
lines changed

9 files changed

+294
-83
lines changed

.github/workflows/pr-checks.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: PR Checks
2+
3+
on:
4+
pull_request_review:
5+
types: [submitted]
6+
7+
jobs:
8+
test:
9+
if: github.event.review && (github.event.review.state == 'approved' || contains(github.event.review.body, '/check'))
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v2
13+
with:
14+
submodules: 'recursive'
15+
- uses: actions/setup-node@v1
16+
with:
17+
node-version: '12.18.1'
18+
- name: Cache node modules
19+
uses: actions/cache@v1
20+
with:
21+
path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS
22+
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
23+
restore-keys: |
24+
${{ runner.os }}-build-${{ env.cache-name }}-
25+
${{ runner.os }}-build-
26+
${{ runner.os }}-
27+
- name: install
28+
run: |
29+
npm install
30+
- name: rust:test
31+
run: |
32+
npm run rust:test
33+
- name: rust:build-nodejs
34+
run: |
35+
npm run rust:build-nodejs

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
{
22
"name": "cardano-serialization-lib",
3-
"version": "8.0.0",
3+
"version": "8.1.0",
44
"description": "(De)serialization functions for the Cardano blockchain along with related utility functions",
55
"scripts": {
66
"rust:build-nodejs": "rimraf ./rust/pkg && cd rust; wasm-pack build --target=nodejs; wasm-pack pack; cd .. && npm run js:flowgen",
77
"rust:build-browser": "rimraf ./rust/pkg && cd rust; wasm-pack build --target=browser; wasm-pack pack; cd .. && npm run js:flowgen",
8+
"rust:build-web": "rimraf ./rust/pkg && cd rust; wasm-pack build --target=web; wasm-pack pack; cd .. && npm run js:flowgen",
89
"rust:build-asm": "rimraf ./rust/pkg && cd rust; wasm-pack build --target=browser --release; wasm-pack pack && npm run asm:build && npm run js:flowgen",
910
"rust:publish": "cd rust && cargo publish && cd ../",
1011
"asm:build": "./binaryen/bin/wasm2js ./rust/pkg/cardano_serialization_lib_bg.wasm --output ./rust/pkg/cardano_serialization_lib.asm.js && node ./scripts/wasm-to-asm",

rust/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cardano-serialization-lib"
3-
version = "8.0.0"
3+
version = "8.1.0"
44
edition = "2018"
55
authors = ["EMURGO"]
66
license = "MIT"

rust/pkg/cardano_serialization_lib.js.flow

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,6 @@
55
* @flow
66
*/
77

8-
/**
9-
* @param {Transaction} tx
10-
* @param {LinearFee} linear_fee
11-
* @returns {BigNum}
12-
*/
13-
declare export function min_fee(tx: Transaction, linear_fee: LinearFee): BigNum;
14-
158
/**
169
* @param {string} password
1710
* @param {string} salt
@@ -36,6 +29,13 @@ declare export function decrypt_with_password(
3629
data: string
3730
): string;
3831

32+
/**
33+
* @param {Transaction} tx
34+
* @param {LinearFee} linear_fee
35+
* @returns {BigNum}
36+
*/
37+
declare export function min_fee(tx: Transaction, linear_fee: LinearFee): BigNum;
38+
3939
/**
4040
* @param {TransactionHash} tx_body_hash
4141
* @param {ByronAddress} addr
@@ -1242,27 +1242,17 @@ declare export class CostModel {
12421242
static new(): CostModel;
12431243

12441244
/**
1245-
* @returns {number}
1246-
*/
1247-
len(): number;
1248-
1249-
/**
1250-
* @param {string} key
1251-
* @param {BigInt} value
1252-
* @returns {BigInt | void}
1253-
*/
1254-
insert(key: string, value: BigInt): BigInt | void;
1255-
1256-
/**
1257-
* @param {string} key
1258-
* @returns {BigInt | void}
1245+
* @param {number} operation
1246+
* @param {Int} cost
1247+
* @returns {Int}
12591248
*/
1260-
get(key: string): BigInt | void;
1249+
set(operation: number, cost: Int): Int;
12611250

12621251
/**
1263-
* @returns {Strings}
1252+
* @param {number} operation
1253+
* @returns {Int}
12641254
*/
1265-
keys(): Strings;
1255+
get(operation: number): Int;
12661256
}
12671257
/**
12681258
*/

rust/src/plutus.rs

Lines changed: 40 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -84,32 +84,38 @@ impl ConstrPlutusData {
8484
const GENERAL_FORM_TAG: u64 = 102;
8585
}
8686

87+
const COST_MODEL_OP_COUNT: usize = 166;
88+
8789
#[wasm_bindgen]
8890
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
89-
pub struct CostModel(std::collections::BTreeMap<String, BigInt>);
91+
pub struct CostModel(Vec<Int>);
9092

9193
to_from_bytes!(CostModel);
9294

9395
#[wasm_bindgen]
9496
impl CostModel {
9597
pub fn new() -> Self {
96-
Self(std::collections::BTreeMap::new())
97-
}
98-
99-
pub fn len(&self) -> usize {
100-
self.0.len()
101-
}
102-
103-
pub fn insert(&mut self, key: String, value: &BigInt) -> Option<BigInt> {
104-
self.0.insert(key, value.clone())
98+
let mut costs = Vec::with_capacity(COST_MODEL_OP_COUNT);
99+
for _ in 0 .. COST_MODEL_OP_COUNT {
100+
costs.push(Int::new_i32(0));
101+
}
102+
Self(costs)
105103
}
106104

107-
pub fn get(&self, key: String) -> Option<BigInt> {
108-
self.0.get(&key).map(|v| v.clone())
105+
pub fn set(&mut self, operation: usize, cost: &Int) -> Result<Int, JsError> {
106+
if operation >= COST_MODEL_OP_COUNT {
107+
return Err(JsError::from_str(&format!("CostModel operation {} out of bounds. Max is {}", operation, COST_MODEL_OP_COUNT)));
108+
}
109+
let old = self.0[operation].clone();
110+
self.0[operation] = cost.clone();
111+
Ok(old)
109112
}
110113

111-
pub fn keys(&self) -> Strings {
112-
Strings(self.0.iter().map(|(k, _v)| k.clone()).collect::<Vec<_>>())
114+
pub fn get(&self, operation: usize) -> Result<Int, JsError> {
115+
if operation >= COST_MODEL_OP_COUNT {
116+
return Err(JsError::from_str(&format!("CostModel operation {} out of bounds. Max is {}", operation, COST_MODEL_OP_COUNT)));
117+
}
118+
Ok(self.0[operation].clone())
113119
}
114120
}
115121

@@ -295,8 +301,6 @@ enum PlutusDataEnum {
295301
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
296302
pub struct PlutusData(PlutusDataEnum);
297303

298-
const PLUTUS_BYTES_MAX_LEN: usize = 64;
299-
300304
to_from_bytes!(PlutusData);
301305

302306
#[wasm_bindgen]
@@ -317,12 +321,8 @@ impl PlutusData {
317321
Self(PlutusDataEnum::Integer(integer.clone()))
318322
}
319323

320-
pub fn new_bytes(bytes: Vec<u8>) -> Result<PlutusData, JsError> {
321-
if bytes.len() > PLUTUS_BYTES_MAX_LEN {
322-
Err(JsError::from_str(&format!("Max Plutus bytes too long: {}, max = {}", bytes.len(), PLUTUS_BYTES_MAX_LEN)))
323-
} else {
324-
Ok(Self(PlutusDataEnum::Bytes(bytes)))
325-
}
324+
pub fn new_bytes(bytes: Vec<u8>) -> Self {
325+
Self(PlutusDataEnum::Bytes(bytes))
326326
}
327327

328328
pub fn kind(&self) -> PlutusDataKind {
@@ -628,34 +628,36 @@ impl Deserialize for ConstrPlutusData {
628628

629629
impl cbor_event::se::Serialize for CostModel {
630630
fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer<W>) -> cbor_event::Result<&'se mut Serializer<W>> {
631-
serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?;
632-
for (key, value) in &self.0 {
633-
serializer.write_text(&key)?;
634-
value.serialize(serializer)?;
631+
serializer.write_map(cbor_event::Len::Len(COST_MODEL_OP_COUNT as u64))?;
632+
for cost in &self.0 {
633+
cost.serialize(serializer)?;
635634
}
636635
Ok(serializer)
637636
}
638637
}
639638

640639
impl Deserialize for CostModel {
641640
fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
642-
let mut table = std::collections::BTreeMap::new();
641+
let mut arr = Vec::new();
643642
(|| -> Result<_, DeserializeError> {
644-
let len = raw.map()?;
645-
while match len { cbor_event::Len::Len(n) => table.len() < n as usize, cbor_event::Len::Indefinite => true, } {
643+
let len = raw.array()?;
644+
while match len { cbor_event::Len::Len(n) => arr.len() < n as usize, cbor_event::Len::Indefinite => true, } {
646645
if raw.cbor_type()? == CBORType::Special {
647646
assert_eq!(raw.special()?, CBORSpecial::Break);
648647
break;
649648
}
650-
let key = String::deserialize(raw)?;
651-
let value = BigInt::deserialize(raw)?;
652-
if table.insert(key.clone(), value).is_some() {
653-
return Err(DeserializeFailure::DuplicateKey(Key::Str(key)).into());
654-
}
649+
arr.push(Int::deserialize(raw)?);
650+
}
651+
if arr.len() != COST_MODEL_OP_COUNT {
652+
return Err(DeserializeFailure::OutOfRange{
653+
min: COST_MODEL_OP_COUNT,
654+
max: COST_MODEL_OP_COUNT,
655+
found: arr.len()
656+
}.into());
655657
}
656658
Ok(())
657659
})().map_err(|e| e.annotate("CostModel"))?;
658-
Ok(Self(table))
660+
Ok(Self(arr.try_into().unwrap()))
659661
}
660662
}
661663

@@ -862,7 +864,7 @@ impl cbor_event::se::Serialize for PlutusDataEnum {
862864
x.serialize(serializer)
863865
},
864866
PlutusDataEnum::Bytes(x) => {
865-
serializer.write_bytes(&x)
867+
write_bounded_bytes(serializer, &x)
866868
},
867869
}
868870
}
@@ -901,18 +903,10 @@ impl Deserialize for PlutusDataEnum {
901903
Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(),
902904
};
903905
match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> {
904-
Ok(raw.bytes()?)
906+
Ok(read_bounded_bytes(raw)?)
905907
})(raw)
906908
{
907-
Ok(variant) => if variant.len() <= PLUTUS_BYTES_MAX_LEN {
908-
return Ok(PlutusDataEnum::Bytes(variant));
909-
} else {
910-
return Err(DeserializeFailure::OutOfRange{
911-
min: 0,
912-
max: PLUTUS_BYTES_MAX_LEN,
913-
found: variant.len(),
914-
}.into());
915-
}
909+
Ok(variant) => return Ok(PlutusDataEnum::Bytes(variant)),
916910
Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(),
917911
};
918912
Err(DeserializeError::new("PlutusDataEnum", DeserializeFailure::NoVariantMatched.into()))

rust/src/tx_builder.rs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ impl TransactionBuilder {
428428
// you would need to have a very high amoun of assets (which add 1-36 bytes each)
429429
// in a single policy to make a difference. In the future if this becomes an issue
430430
// we can change that here.
431-
431+
432432
// this is the other part of the optimization but we need to take into account
433433
// the difference between CBOR encoding which can change which happens in two places:
434434
// a) length within assets of one policy id
@@ -1333,6 +1333,67 @@ mod tests {
13331333
let _final_tx = tx_builder.build(); // just test that it doesn't throw
13341334
}
13351335

1336+
#[test]
1337+
fn build_tx_no_useless_multiasset() {
1338+
let linear_fee = LinearFee::new(&to_bignum(44), &to_bignum(155381));
1339+
let mut tx_builder =
1340+
TransactionBuilder::new(&linear_fee, &to_bignum(1000000), &to_bignum(500000000), &to_bignum(2000000));
1341+
1342+
let policy_id = &PolicyID::from([0u8; 28]);
1343+
let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
1344+
1345+
// add an output that uses up all the token but leaves ADA
1346+
let mut input_amount = Value::new(&to_bignum(5_000_000));
1347+
let mut input_multiasset = MultiAsset::new();
1348+
input_multiasset.insert(policy_id, &{
1349+
let mut assets = Assets::new();
1350+
assets.insert(&name, &to_bignum(100));
1351+
assets
1352+
});
1353+
input_amount.set_multiasset(&input_multiasset);
1354+
1355+
tx_builder.add_input(
1356+
&ByronAddress::from_base58("Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3").unwrap().to_address(),
1357+
&TransactionInput::new(
1358+
&genesis_id(),
1359+
0
1360+
),
1361+
&input_amount
1362+
);
1363+
1364+
// add an input that contains an asset & ADA
1365+
let mut output_amount = Value::new(&to_bignum(2_000_000));
1366+
let mut output_multiasset = MultiAsset::new();
1367+
output_multiasset.insert(policy_id, &{
1368+
let mut assets = Assets::new();
1369+
assets.insert(&name, &to_bignum(100));
1370+
assets
1371+
});
1372+
output_amount.set_multiasset(&output_multiasset);
1373+
1374+
let output_addr = ByronAddress::from_base58("Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b").unwrap();
1375+
tx_builder.add_output(&TransactionOutput::new(
1376+
&output_addr.to_address(),
1377+
&output_amount
1378+
)).unwrap();
1379+
1380+
tx_builder.set_ttl(1);
1381+
1382+
let change_addr = ByronAddress::from_base58("Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho").unwrap();
1383+
let added_change = tx_builder.add_change_if_needed(
1384+
&change_addr.to_address()
1385+
);
1386+
assert!(added_change.unwrap());
1387+
assert_eq!(tx_builder.outputs.len(), 2);
1388+
let final_tx = tx_builder.build().unwrap();
1389+
let change_output = final_tx.outputs().get(1);
1390+
let change_assets = change_output.amount().multiasset();
1391+
1392+
// since all tokens got sent in the output
1393+
// the change should be only ADA and not have any multiasset struct in it
1394+
assert!(change_assets.is_none());
1395+
}
1396+
13361397
#[test]
13371398
fn build_tx_add_change_split_nfts() {
13381399
let linear_fee = LinearFee::new(&to_bignum(0), &to_bignum(1));

0 commit comments

Comments
 (0)