test 'mint' purpose and script context creation.

Alongside a bunch of other stuff from the coverage list. In
  particular, the mint transaction contains:

  - reference inputs
  - multiple outputs, with assets, and type-0, type-1 and type-6
    addresses.
  - an output with a datum hash
  - an output with an inline script
  - carries an extra datum witness, preimage of the embedded hash
  - mint, with 2 policies purposely ordered wrongly, with 1 and 2
    assets purposely ordered wrong. One of the mint is actually a
    burn (i.e. negative quantity)
This commit is contained in:
KtorZ 2024-08-10 15:56:53 +02:00
parent eea8dc7d0a
commit 6b6bace8a5
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
9 changed files with 2467 additions and 38 deletions

5
Cargo.lock generated vendored
View File

@ -1942,6 +1942,7 @@ checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f"
[[package]] [[package]]
name = "pallas-addresses" name = "pallas-addresses"
version = "0.29.0" version = "0.29.0"
source = "git+https://github.com/KtorZ/pallas.git?rev=8ea5a1adc9919b70b213dfe597e920d6e113120c#8ea5a1adc9919b70b213dfe597e920d6e113120c"
dependencies = [ dependencies = [
"base58", "base58",
"bech32", "bech32",
@ -1956,6 +1957,7 @@ dependencies = [
[[package]] [[package]]
name = "pallas-codec" name = "pallas-codec"
version = "0.29.0" version = "0.29.0"
source = "git+https://github.com/KtorZ/pallas.git?rev=8ea5a1adc9919b70b213dfe597e920d6e113120c#8ea5a1adc9919b70b213dfe597e920d6e113120c"
dependencies = [ dependencies = [
"hex", "hex",
"minicbor", "minicbor",
@ -1967,6 +1969,7 @@ dependencies = [
[[package]] [[package]]
name = "pallas-crypto" name = "pallas-crypto"
version = "0.29.0" version = "0.29.0"
source = "git+https://github.com/KtorZ/pallas.git?rev=8ea5a1adc9919b70b213dfe597e920d6e113120c#8ea5a1adc9919b70b213dfe597e920d6e113120c"
dependencies = [ dependencies = [
"cryptoxide", "cryptoxide",
"hex", "hex",
@ -1979,6 +1982,7 @@ dependencies = [
[[package]] [[package]]
name = "pallas-primitives" name = "pallas-primitives"
version = "0.29.0" version = "0.29.0"
source = "git+https://github.com/KtorZ/pallas.git?rev=8ea5a1adc9919b70b213dfe597e920d6e113120c#8ea5a1adc9919b70b213dfe597e920d6e113120c"
dependencies = [ dependencies = [
"base58", "base58",
"bech32", "bech32",
@ -1993,6 +1997,7 @@ dependencies = [
[[package]] [[package]]
name = "pallas-traverse" name = "pallas-traverse"
version = "0.29.0" version = "0.29.0"
source = "git+https://github.com/KtorZ/pallas.git?rev=8ea5a1adc9919b70b213dfe597e920d6e113120c#8ea5a1adc9919b70b213dfe597e920d6e113120c"
dependencies = [ dependencies = [
"hex", "hex",
"itertools 0.13.0", "itertools 0.13.0",

View File

@ -968,7 +968,7 @@ mod tests {
} }
#[test] #[test]
fn tx_to_plutus_data() { fn script_context_simple_send() {
let datum = Some(Data::constr(0, Vec::new())); let datum = Some(Data::constr(0, Vec::new()));
let redeemer = Redeemer { let redeemer = Redeemer {
@ -1005,4 +1005,64 @@ mod tests {
// from the Haskell ledger / cardano node. // from the Haskell ledger / cardano node.
insta::assert_debug_snapshot!(script_context.to_plutus_data()) insta::assert_debug_snapshot!(script_context.to_plutus_data())
} }
#[test]
fn script_context_mint() {
let redeemer = Redeemer {
tag: RedeemerTag::Mint,
index: 1,
data: Data::integer(42.into()),
ex_units: ExUnits {
mem: 1000000,
steps: 100000000,
},
};
let script_context = fixture_tx_info(
"84a9008182582000000000000000000000000000000000000000000000000000\
00000000000000000183a300581d600000000000000000000000000000000000\
0000000000000000000000011a000f42400282005820923918e403bf43c34b4e\
f6b48eb2ee04babed17320d8d1b9ff9ad086e86f44eca2005839000000000000\
0000000000000000000000000000000000000000000000000000000000000000\
0000000000000000000000000000000000000001821a000f4240a2581c12593b\
4cbf7fdfd8636db99fe356437cd6af8539aadaa0a401964874a14474756e611b\
00005af3107a4000581c0c8eaf490c53afbf27e3d84a3b57da51fbafe5aa7844\
3fcec2dc262ea14561696b656e182aa300583910000000000000000000000000\
0000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000001821a000f4240a1581c0c8eaf490c53afbf27e3\
d84a3b57da51fbafe5aa78443fcec2dc262ea14763617264616e6f0103d81847\
82034463666f6f02182a09a2581c12593b4cbf7fdfd8636db99fe356437cd6af\
8539aadaa0a401964874a14474756e611b00005af3107a4000581c0c8eaf490c\
53afbf27e3d84a3b57da51fbafe5aa78443fcec2dc262ea24763617264616e6f\
014561696b656e2d0b5820ffffffffffffffffffffffffffffffffffffffffff\
ffffffffffffffffffffff0d8182582000000000000000000000000000000000\
00000000000000000000000000000000001082581d6000000000000000000000\
0000000000000000000000000000000000001a3b9aca00110112818258200000\
00000000000000000000000000000000000000000000000000000000000000a3\
0582840100d87980821a000f42401a05f5e100840101182a821a000f42401a05\
f5e1000481d879800782587d587b010100323232323232322533333300800115\
3330033370e900018029baa001153330073006375400224a6660089445261533\
0054911856616c696461746f722072657475726e65642066616c736500136560\
02002002002002002153300249010b5f746d70323a20566f696400165734ae71\
55ceaab9e5573eae915895589301010032323232323232253333330080011533\
30033370e900018029baa001153330073006375400224a666008a6600a920110\
5f5f5f5f5f6d696e745f325f5f5f5f5f0014a22930a99802a4811856616c6964\
61746f722072657475726e65642066616c736500136560020020020020020021\
53300249010b5f746d70323a20566f696400165734ae7155ceaab9e5573eae91\
f5f6",
"8182582000000000000000000000000000000000000000000000000000000000\
0000000000",
"81a200581d600000000000000000000000000000000000000000000000000000\
0000011a000f4240",
)
.into_script_context(&redeemer, None)
.unwrap();
// NOTE: The initial snapshot has been generated using the Haskell
// implementation of the ledger library for that same serialized
// transactions. It is meant to control that our construction of the
// script context and its serialization matches exactly those
// from the Haskell ledger / cardano node.
insta::assert_debug_snapshot!(script_context.to_plutus_data());
}
} }

View File

@ -0,0 +1,641 @@
---
source: crates/uplc/src/tx/script_context.rs
expression: script_context.to_plutus_data()
---
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [
Array(
[
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [
BoundedBytes(
BoundedBytes(
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
],
),
),
BigInt(
Int(
Int(
Int {
neg: false,
val: 0,
},
),
),
),
],
},
),
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [
Constr(
Constr {
tag: 122,
any_constructor: None,
fields: [
BoundedBytes(
BoundedBytes(
[
57,
244,
127,
211,
179,
136,
239,
83,
196,
143,
8,
222,
36,
118,
109,
62,
85,
218,
222,
108,
174,
144,
140,
194,
78,
15,
79,
62,
],
),
),
],
},
),
Constr(
Constr {
tag: 122,
any_constructor: None,
fields: [],
},
),
],
},
),
Map(
Def(
[
(
BoundedBytes(
BoundedBytes(
[],
),
),
Map(
Def(
[
(
BoundedBytes(
BoundedBytes(
[],
),
),
BigInt(
Int(
Int(
Int {
neg: false,
val: 1000000000,
},
),
),
),
),
],
),
),
),
],
),
),
Constr(
Constr {
tag: 123,
any_constructor: None,
fields: [
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [],
},
),
],
},
),
Constr(
Constr {
tag: 122,
any_constructor: None,
fields: [],
},
),
],
},
),
],
},
),
],
),
Array(
[],
),
Array(
[
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [
BoundedBytes(
BoundedBytes(
[
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
17,
],
),
),
],
},
),
Constr(
Constr {
tag: 122,
any_constructor: None,
fields: [],
},
),
],
},
),
Map(
Def(
[
(
BoundedBytes(
BoundedBytes(
[],
),
),
Map(
Def(
[
(
BoundedBytes(
BoundedBytes(
[],
),
),
BigInt(
Int(
Int(
Int {
neg: false,
val: 1000000000,
},
),
),
),
),
],
),
),
),
],
),
),
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [],
},
),
Constr(
Constr {
tag: 122,
any_constructor: None,
fields: [],
},
),
],
},
),
],
),
BigInt(
Int(
Int(
Int {
neg: false,
val: 42,
},
),
),
),
Map(
Def(
[],
),
),
Array(
[],
),
Map(
Def(
[],
),
),
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [],
},
),
Constr(
Constr {
tag: 122,
any_constructor: None,
fields: [],
},
),
],
},
),
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [
Constr(
Constr {
tag: 123,
any_constructor: None,
fields: [],
},
),
Constr(
Constr {
tag: 122,
any_constructor: None,
fields: [],
},
),
],
},
),
],
},
),
Array(
[],
),
Map(
Def(
[
(
Constr(
Constr {
tag: 122,
any_constructor: None,
fields: [
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [
BoundedBytes(
BoundedBytes(
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
],
),
),
BigInt(
Int(
Int(
Int {
neg: false,
val: 0,
},
),
),
),
],
},
),
],
},
),
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [],
},
),
),
],
),
),
Map(
Def(
[],
),
),
BoundedBytes(
BoundedBytes(
[
120,
236,
20,
142,
166,
71,
207,
153,
105,
68,
104,
145,
175,
49,
147,
156,
93,
87,
178,
117,
162,
69,
87,
6,
120,
44,
97,
131,
239,
11,
98,
241,
],
),
),
Map(
Def(
[],
),
),
Array(
[],
),
Constr(
Constr {
tag: 122,
any_constructor: None,
fields: [],
},
),
Constr(
Constr {
tag: 122,
any_constructor: None,
fields: [],
},
),
],
},
),
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [],
},
),
Constr(
Constr {
tag: 122,
any_constructor: None,
fields: [
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [
BoundedBytes(
BoundedBytes(
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
],
),
),
BigInt(
Int(
Int(
Int {
neg: false,
val: 0,
},
),
),
),
],
},
),
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [
Constr(
Constr {
tag: 121,
any_constructor: None,
fields: [],
},
),
],
},
),
],
},
),
],
},
)

View File

@ -31,12 +31,12 @@ for convenience.
- [x] inputs - [x] inputs
- reference inputs - reference inputs
- [x] none - [x] none
- [ ] some - [x] some
- outputs - outputs
- [ ] none - [x] none
- [ ] some - [x] some
- [x] fee - [x] fee
- [ ] mint - [x] mint
- certificates - certificates
- [ ] none - [ ] none
- some - some
@ -63,7 +63,7 @@ for convenience.
- [x] none - [x] none
- [ ] some - [ ] some
- [ ] redeemers - [ ] redeemers
- [ ] datums - [x] datums
- votes - votes
- [ ] none - [ ] none
- [ ] some - [ ] some
@ -78,27 +78,27 @@ for convenience.
- [ ] without - [ ] without
- Address - Address
- [ ] type-0 (key | key) - [x] type-0 (key | key)
- [ ] type-1 (script | key) - [x] type-1 (script | key)
- [ ] type-2 (key | script) - [ ] type-2 (key | script)
- [ ] type-3 (script | script) - [ ] type-3 (script | script)
- [ ] type-4 (key | ptr) - [ ] type-4 (key | ptr)
- [ ] type-5 (script | ptr) - [ ] type-5 (script | ptr)
- [ ] type-6 (key | ø) - [x] type-6 (key | ø)
- [x] type-7 (key | ø) - [x] type-7 (key | ø)
- Value - Value
- [x] only ada - [x] only ada
- [ ] multi-assets - [x] multi-assets
- Output datum - Output datum
- [ ] none - [x] none
- [ ] hash - [x] hash
- [x] inline - [x] inline
- Output script - Output script
- [x] none - [x] none
- [ ] inline - [x] inline
- Governance Action - Governance Action
- parameter change - parameter change
@ -167,10 +167,6 @@ for convenience.
- [ ] with guardrail script - [ ] with guardrail script
- [ ] without guardrail script - [ ] without guardrail script
- Value
- [x] pure ada
- [ ] native assets
- Credential - Credential
- [ ] key - [ ] key
- [x] script - [x] script

View File

@ -6,9 +6,9 @@
, 1: , 1:
[ { 0: h'6000000000000000000000000000000000000000000000000000000000' [ { 0: h'6000000000000000000000000000000000000000000000000000000000'
, 1: 1000000 , 1: 1000000
, 2: [1, 24(h'4463666F6F')] , 2: [0, h'923918e403bf43c34b4ef6b48eb2ee04babed17320d8d1b9ff9ad086e86f44ec']
} }
, { 0: h'6000000000000000000000000000000000000000000000000000000000' , { 0: h'000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
, 1: , 1:
[ 1000000 [ 1000000
, { h'{{ mint.mint_1.hash }}': { h'74756e61': 100000000000000 } , { h'{{ mint.mint_1.hash }}': { h'74756e61': 100000000000000 }
@ -16,7 +16,7 @@
} }
] ]
} }
, { 0: h'6000000000000000000000000000000000000000000000000000000000' , { 0: h'100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
, 1: , 1:
[ 1000000 [ 1000000
, { h'{{ mint.mint_2.hash }}': { h'63617264616e6f': 1 } , { h'{{ mint.mint_2.hash }}': { h'63617264616e6f': 1 }
@ -49,6 +49,10 @@
] ]
, 17: 1 , 17: 1
, 18:
[ [h'0000000000000000000000000000000000000000000000000000000000000000', 0]
]
}, },
{ 5: { 5:
@ -56,6 +60,14 @@
, [1, 1, 42, [1000000, 100000000]] , [1, 1, 42, [1000000, 100000000]]
] ]
, 4:
[ 121([])
]
, 7:
[ h'{{ mint.mint_1.cbor }}'
, h'{{ mint.mint_2.cbor }}'
]
}, },

File diff suppressed because one or more lines are too long

View File

@ -58,17 +58,6 @@ done
echo $RESOLVED_INPUTS | cbor-diag --to hex --from diag > ctx/$TITLE/resolved_inputs.cbor echo $RESOLVED_INPUTS | cbor-diag --to hex --from diag > ctx/$TITLE/resolved_inputs.cbor
echo $TRANSACTION | cbor-diag --to hex --from diag > ctx/$TITLE/tx.cbor echo $TRANSACTION | cbor-diag --to hex --from diag > ctx/$TITLE/tx.cbor
# echo "TRANSACTION"
# cat ctx/$TITLE/tx.cbor
# ogmios inspect transaction $(cat ctx/$TITLE/tx.cbor) | jq
# echo -e "\n\nINPUTS"
# cat ctx/inputs.cbor
#
# echo -e "\n\nRESOLVED_INPUTS"
# cat ctx/$TITLE/resolved_inputs.cbor
$AIKEN tx simulate \ $AIKEN tx simulate \
ctx/$TITLE/tx.cbor \ ctx/$TITLE/tx.cbor \
ctx/inputs.cbor \ ctx/inputs.cbor \

View File

@ -1,18 +1,128 @@
use aiken/dict use aiken/dict.{Dict}
use aiken/list
use aiken/transaction.{ use aiken/transaction.{
InlineDatum, Input, Output, OutputReference, ScriptContext, Spend, Spending, DatumHash, Input, Mint, Minting, NoDatum, Output, OutputReference,
ScriptContext, ScriptInfo, ScriptPurpose,
} }
use aiken/transaction/credential.{Address, ScriptCredential} use aiken/transaction/credential
use aiken/transaction/value use aiken/transaction/value.{PolicyId, Value}
const null28 = #"00000000000000000000000000000000000000000000000000000000"
const null32 =
#"0000000000000000000000000000000000000000000000000000000000000000"
const void_hash =
#"923918e403bf43c34b4ef6b48eb2ee04babed17320d8d1b9ff9ad086e86f44ec"
validator { validator {
fn mint_1(_tmp2: Void, ctx: ScriptContext) { fn mint_1(_tmp2: Void, ctx: ScriptContext) {
let our_policy_id = assert_script_info(ctx.info)
let other_policy_id = assert_redeemers(ctx.transaction.redeemers)
assert_outputs(ctx.transaction.outputs, our_policy_id, other_policy_id)
assert_mint(ctx.transaction.mint, our_policy_id, other_policy_id)
assert_reference_inputs(ctx.transaction.reference_inputs)
assert_datums(ctx.transaction.datums)
True True
} }
} }
fn assert_reference_inputs(inputs: List<Input>) -> Void {
expect
[
Input {
output_reference: OutputReference {
transaction_id: null32,
output_index: 0,
},
output: Output {
address: credential.from_verification_key(null28),
value: value.from_lovelace(1_000_000),
datum: NoDatum,
reference_script: None,
},
},
] == inputs
Void
}
fn assert_script_info(info: ScriptInfo) -> PolicyId {
expect Minting(policy_id) = info
policy_id
}
fn assert_redeemers(redeemers: Pairs<ScriptPurpose, Data>) -> PolicyId {
expect [Pair(Mint(other_policy_id), data), _] = redeemers
expect Void = data
other_policy_id
}
fn assert_datums(datums: Dict<ByteArray, Data>) -> Void {
let void: Data = Void
expect [Pair(void_hash, void)] == dict.to_pairs(datums)
Void
}
fn assert_outputs(
outputs: List<Output>,
our_policy_id: PolicyId,
other_policy_id: PolicyId,
) {
expect list.length(outputs) == 3
expect
Some(
Output {
address: credential.from_verification_key(null28),
value: value.from_lovelace(1_000_000),
datum: DatumHash(void_hash),
reference_script: None,
},
) == list.at(outputs, 0)
expect
Some(
Output {
address: credential.from_verification_key(null28)
|> credential.with_delegation_key(null28),
value: value.from_lovelace(1_000_000)
|> value.add(our_policy_id, "tuna", 100000000000000)
|> value.add(other_policy_id, "aiken", 42),
datum: NoDatum,
reference_script: None,
},
) == list.at(outputs, 1)
expect
Some(
Output {
address: credential.from_script(null28)
|> credential.with_delegation_key(null28),
value: value.from_lovelace(1_000_000)
|> value.add(other_policy_id, "cardano", 1),
datum: NoDatum,
reference_script: Some(
#"68ad54b3a8124d9fe5caaaf2011a85d72096e696a2fb3d7f86c41717",
),
},
) == list.at(outputs, 2)
Void
}
fn assert_mint(mint: Value, our_policy_id: PolicyId, other_policy_id: PolicyId) {
expect
[
(other_policy_id, "aiken", -14),
(other_policy_id, "cardano", 1),
(our_policy_id, "tuna", 100000000000000),
] == value.flatten(mint)
Void
}
validator { validator {
fn mint_2(_tmp2: Void, ctx: ScriptContext) { fn mint_2(_tmp2: Void, _ctx: ScriptContext) {
trace @"_____mint_2_____"
True True
} }
} }