Support multi-validator in script context accept test.
This commit is contained in:
parent
b158469144
commit
eea8dc7d0a
|
@ -110,8 +110,8 @@ pub fn exec(
|
||||||
|
|
||||||
let with_redeemer = |redeemer: &Redeemer| {
|
let with_redeemer = |redeemer: &Redeemer| {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{} {:?} → {}",
|
"{} {:?}[{}]",
|
||||||
" Redeemer"
|
" Evaluating"
|
||||||
.if_supports_color(Stderr, |s| s.purple())
|
.if_supports_color(Stderr, |s| s.purple())
|
||||||
.if_supports_color(Stderr, |s| s.bold()),
|
.if_supports_color(Stderr, |s| s.bold()),
|
||||||
redeemer.tag,
|
redeemer.tag,
|
||||||
|
|
|
@ -49,11 +49,11 @@ pub enum Error {
|
||||||
#[error("can't eval without redeemers")]
|
#[error("can't eval without redeemers")]
|
||||||
NoRedeemers,
|
NoRedeemers,
|
||||||
#[error(
|
#[error(
|
||||||
"mismatch in expected redeemers\n{:>13} {}\n{:>13} {}",
|
"missing and/or unexpected validator(s) and/or redeemer(s)\n{:>13} {}\n{:>13} {}",
|
||||||
"Missing",
|
"Missing",
|
||||||
if .missing.is_empty() { "ø".to_string() } else { .missing.join(&format!("\n{:>13}", "")) },
|
if .missing.is_empty() { "ø".to_string() } else { .missing.join(&format!("\n{:>14}", "")) },
|
||||||
"Unexpected",
|
"Unexpected",
|
||||||
if .extra.is_empty() { "ø".to_string() } else { .extra.join(&format!("\n{:>13}", "")) },
|
if .extra.is_empty() { "ø".to_string() } else { .extra.join(&format!("\n{:>14}", "")) },
|
||||||
)]
|
)]
|
||||||
RequiredRedeemersMismatch {
|
RequiredRedeemersMismatch {
|
||||||
missing: Vec<String>,
|
missing: Vec<String>,
|
||||||
|
@ -87,7 +87,7 @@ pub enum Error {
|
||||||
MissingRequiredInlineDatumOrHash,
|
MissingRequiredInlineDatumOrHash,
|
||||||
#[error("redeemer points to an unsupported certificate type")]
|
#[error("redeemer points to an unsupported certificate type")]
|
||||||
UnsupportedCertificateType,
|
UnsupportedCertificateType,
|
||||||
#[error("failed script execution\n{:>13} {}", format!("{}({})", tag, index), err)]
|
#[error("failed script execution\n{:>13} {}", format!("{}[{}]", tag, index), err)]
|
||||||
RedeemerError {
|
RedeemerError {
|
||||||
tag: String,
|
tag: String,
|
||||||
index: u32,
|
index: u32,
|
||||||
|
|
|
@ -44,7 +44,7 @@ pub fn eval_redeemer(
|
||||||
}
|
}
|
||||||
.apply_data(redeemer.data.clone())
|
.apply_data(redeemer.data.clone())
|
||||||
.apply_data(script_context.to_plutus_data()),
|
.apply_data(script_context.to_plutus_data()),
|
||||||
ScriptContext::V3 { .. } => {
|
ScriptContext::V3 { .. } if datum.is_some() => {
|
||||||
program
|
program
|
||||||
// FIXME: Temporary, but needed until https://github.com/aiken-lang/aiken/pull/977
|
// FIXME: Temporary, but needed until https://github.com/aiken-lang/aiken/pull/977
|
||||||
// is implemented.
|
// is implemented.
|
||||||
|
@ -52,6 +52,13 @@ pub fn eval_redeemer(
|
||||||
.apply_data(Data::constr(0, vec![]))
|
.apply_data(Data::constr(0, vec![]))
|
||||||
.apply_data(script_context.to_plutus_data())
|
.apply_data(script_context.to_plutus_data())
|
||||||
}
|
}
|
||||||
|
ScriptContext::V3 { .. } => {
|
||||||
|
program
|
||||||
|
// FIXME: Temporary, but needed until https://github.com/aiken-lang/aiken/pull/977
|
||||||
|
// is implemented.
|
||||||
|
.apply_data(Data::constr(0, vec![]))
|
||||||
|
.apply_data(script_context.to_plutus_data())
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut eval_result = if let Some(costs) = cost_mdl_opt {
|
let mut eval_result = if let Some(costs) = cost_mdl_opt {
|
||||||
|
|
|
@ -185,7 +185,7 @@ pub fn has_exact_set_of_redeemers(
|
||||||
let missing: Vec<_> = redeemers_needed
|
let missing: Vec<_> = redeemers_needed
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|x| !wits_redeemer_keys.contains(&&x.0))
|
.filter(|x| !wits_redeemer_keys.contains(&&x.0))
|
||||||
.map(|x| format!("{} (key: {:?}, purpose: {:?})", x.2, x.0, x.1,))
|
.map(|x| format!("{:?}[{:?}] -> {}", x.0.tag, x.0.index, x.2))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let extra: Vec<_> = wits_redeemer_keys
|
let extra: Vec<_> = wits_redeemer_keys
|
||||||
|
|
|
@ -21,7 +21,7 @@ for convenience.
|
||||||
|
|
||||||
- Purpose
|
- Purpose
|
||||||
- [x] spend
|
- [x] spend
|
||||||
- [ ] mint
|
- [x] mint
|
||||||
- [ ] withdraw
|
- [ ] withdraw
|
||||||
- [ ] publish
|
- [ ] publish
|
||||||
- [ ] voting
|
- [ ] voting
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
[
|
||||||
|
{ 0: h'6000000000000000000000000000000000000000000000000000000000'
|
||||||
|
, 1: 1000000
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,65 @@
|
||||||
|
[
|
||||||
|
{ 0:
|
||||||
|
[ [h'0000000000000000000000000000000000000000000000000000000000000000', 0]
|
||||||
|
]
|
||||||
|
|
||||||
|
, 1:
|
||||||
|
[ { 0: h'6000000000000000000000000000000000000000000000000000000000'
|
||||||
|
, 1: 1000000
|
||||||
|
, 2: [1, 24(h'4463666F6F')]
|
||||||
|
}
|
||||||
|
, { 0: h'6000000000000000000000000000000000000000000000000000000000'
|
||||||
|
, 1:
|
||||||
|
[ 1000000
|
||||||
|
, { h'{{ mint.mint_1.hash }}': { h'74756e61': 100000000000000 }
|
||||||
|
, h'{{ mint.mint_2.hash }}': { h'61696b656e': 42 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
, { 0: h'6000000000000000000000000000000000000000000000000000000000'
|
||||||
|
, 1:
|
||||||
|
[ 1000000
|
||||||
|
, { h'{{ mint.mint_2.hash }}': { h'63617264616e6f': 1 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
, 3: 24(h'82034463666F6F')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
, 2: 42
|
||||||
|
|
||||||
|
, 9:
|
||||||
|
{ h'{{ mint.mint_1.hash }}':
|
||||||
|
{ h'74756e61': 100000000000000
|
||||||
|
}
|
||||||
|
, h'{{ mint.mint_2.hash }}':
|
||||||
|
{ h'63617264616e6f': 1
|
||||||
|
, h'61696b656e': -14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
, 11: h'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'
|
||||||
|
|
||||||
|
, 13:
|
||||||
|
[ [h'0000000000000000000000000000000000000000000000000000000000000000', 0]
|
||||||
|
]
|
||||||
|
|
||||||
|
, 16:
|
||||||
|
[ h'6000000000000000000000000000000000000000000000000000000000', 1000000000
|
||||||
|
]
|
||||||
|
|
||||||
|
, 17: 1
|
||||||
|
},
|
||||||
|
|
||||||
|
{ 5:
|
||||||
|
[ [1, 0, 121([]), [1000000, 100000000]]
|
||||||
|
, [1, 1, 42, [1000000, 100000000]]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
true,
|
||||||
|
|
||||||
|
null
|
||||||
|
]
|
|
@ -0,0 +1,6 @@
|
||||||
|
[
|
||||||
|
{ 0: h'70{{ simple_spend.spend.hash }}'
|
||||||
|
, 1: 1000000000
|
||||||
|
, 2: [1, 24(h'd87980')]
|
||||||
|
}
|
||||||
|
]
|
|
@ -4,8 +4,7 @@
|
||||||
]
|
]
|
||||||
|
|
||||||
, 1:
|
, 1:
|
||||||
[ [h'6011111111111111111111111111111111111111111111111111111111', 1000000000]
|
[]
|
||||||
]
|
|
||||||
|
|
||||||
, 2: 42
|
, 2: 42
|
||||||
|
|
||||||
|
@ -24,7 +23,7 @@
|
||||||
|
|
||||||
{ 5: [[0, 0, 121([]), [1000000, 100000000]]]
|
{ 5: [[0, 0, 121([]), [1000000, 100000000]]]
|
||||||
|
|
||||||
, 7: [h'{{ VALIDATOR }}']
|
, 7: [h'{{ simple_spend.spend.cbor }}']
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
81a300581d70{{ VALIDATOR_HASH }}011a3b9aca00028201d818{{ DATUM }}
|
|
File diff suppressed because one or more lines are too long
|
@ -16,13 +16,13 @@ AIKEN=${2:-"cargo run -r --quiet --"}
|
||||||
|
|
||||||
if ! command -v jq &> /dev/null
|
if ! command -v jq &> /dev/null
|
||||||
then
|
then
|
||||||
echo "\033[1mjq\033[0m missing from system but required."
|
echo -e "\033[1mjq\033[0m missing from system but required."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! command -v cbor-diag &> /dev/null
|
if ! command -v cbor-diag &> /dev/null
|
||||||
then
|
then
|
||||||
echo "\033[1mcbor-diag\033[0m missing from system but required."
|
echo -e "\033[1mcbor-diag\033[0m missing from system but required."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -31,21 +31,43 @@ if [ $? -ne 0 ]; then
|
||||||
exit $?
|
exit $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BLUEPRINT=$(jq ".validators[] | select(.title|contains(\"$TITLE\"))" plutus.json)
|
declare -a VALIDATORS=($(jq -c ".validators | map(select(.title|contains(\"$TITLE\"))) | .[]" plutus.json))
|
||||||
|
|
||||||
VALIDATOR_HASH=$(echo $BLUEPRINT | jq -r .hash)
|
if [ -z $VALIDATORS ]; then
|
||||||
VALIDATOR=$(echo $BLUEPRINT | jq -r .compiledCode)
|
echo -e "\033[31mvalidator \033[1m$TITLE\033[0m\033[31m not found!\033[0m"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
DATUM=$(cbor-diag --to hex --from diag <<< "h'$(cat ctx/$TITLE/datum.cbor)'")
|
TRANSACTION=$(cat ctx/$TITLE/tx.template)
|
||||||
|
RESOLVED_INPUTS=$(cat ctx/$TITLE/resolved_inputs.template)
|
||||||
|
|
||||||
sed "s/{{ VALIDATOR_HASH }}/$VALIDATOR_HASH/" ctx/$TITLE/resolved_inputs.template \
|
for ITEM in ${VALIDATORS[@]}; do
|
||||||
| sed "s/{{ DATUM }}/$DATUM/" \
|
VALIDATOR_NAME=$(echo $ITEM | jq -r .title)
|
||||||
> ctx/$TITLE/resolved_inputs.cbor
|
VALIDATOR_HASH=$(echo $ITEM | jq -r .hash)
|
||||||
|
VALIDATOR=$(echo $ITEM | jq -r .compiledCode)
|
||||||
|
|
||||||
sed "s/{{ VALIDATOR }}/$VALIDATOR/" ctx/$TITLE/tx.template \
|
RESOLVED_INPUTS=$(echo $RESOLVED_INPUTS \
|
||||||
| sed "s/{{ VALIDATOR_HASH }}/$VALIDATOR_HASH/" \
|
| sed "s/{{ $VALIDATOR_NAME.cbor }}/$VALIDATOR/g" \
|
||||||
| cbor-diag --to hex --from diag \
|
| sed "s/{{ $VALIDATOR_NAME.hash }}/$VALIDATOR_HASH/g")
|
||||||
> ctx/$TITLE/tx.cbor
|
|
||||||
|
TRANSACTION=$(echo $TRANSACTION \
|
||||||
|
| sed "s/{{ $VALIDATOR_NAME.cbor }}/$VALIDATOR/g" \
|
||||||
|
| sed "s/{{ $VALIDATOR_NAME.hash }}/$VALIDATOR_HASH/g")
|
||||||
|
done
|
||||||
|
|
||||||
|
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"
|
||||||
|
# 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 \
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
use aiken/dict
|
||||||
|
use aiken/transaction.{
|
||||||
|
InlineDatum, Input, Output, OutputReference, ScriptContext, Spend, Spending,
|
||||||
|
}
|
||||||
|
use aiken/transaction/credential.{Address, ScriptCredential}
|
||||||
|
use aiken/transaction/value
|
||||||
|
|
||||||
|
validator {
|
||||||
|
fn mint_1(_tmp2: Void, ctx: ScriptContext) {
|
||||||
|
True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validator {
|
||||||
|
fn mint_2(_tmp2: Void, ctx: ScriptContext) {
|
||||||
|
True
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
use aiken/dict
|
||||||
|
use aiken/transaction.{
|
||||||
|
InlineDatum, Input, Output, OutputReference, ScriptContext, ScriptInfo,
|
||||||
|
ScriptPurpose, Spend, Spending,
|
||||||
|
}
|
||||||
|
use aiken/transaction/credential.{Address, ScriptCredential}
|
||||||
|
use aiken/transaction/value
|
||||||
|
|
||||||
|
validator {
|
||||||
|
fn spend(_tmp1: Void, _tmp2: Void, ctx: ScriptContext) {
|
||||||
|
assert_transaction_id(ctx.transaction.id)
|
||||||
|
|
||||||
|
assert_script_info(ctx.info)
|
||||||
|
|
||||||
|
assert_inputs(ctx.transaction.inputs)
|
||||||
|
|
||||||
|
expect [] = ctx.transaction.outputs
|
||||||
|
|
||||||
|
expect [] = ctx.transaction.reference_inputs
|
||||||
|
|
||||||
|
expect [] = ctx.transaction.extra_signatories
|
||||||
|
|
||||||
|
expect 42 == ctx.transaction.fee
|
||||||
|
|
||||||
|
assert_redeemers(ctx.transaction.redeemers)
|
||||||
|
|
||||||
|
expect [] == dict.to_pairs(ctx.transaction.datums)
|
||||||
|
|
||||||
|
True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_transaction_id(id: ByteArray) {
|
||||||
|
expect
|
||||||
|
#"c6fbd346681a8f8337f6b3e51e6ec973f1509367eabc3a44c849af58a1d8471b" == id
|
||||||
|
Void
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_script_info(info: ScriptInfo) {
|
||||||
|
expect
|
||||||
|
Spending(
|
||||||
|
OutputReference {
|
||||||
|
transaction_id: #"0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
output_index: 0,
|
||||||
|
},
|
||||||
|
Some(void()),
|
||||||
|
) == info
|
||||||
|
Void
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_inputs(inputs: List<Input>) {
|
||||||
|
expect [
|
||||||
|
Input {
|
||||||
|
output_reference: OutputReference { transaction_id, output_index: 0 },
|
||||||
|
output: Output {
|
||||||
|
address,
|
||||||
|
value: resolved_input_value,
|
||||||
|
datum: InlineDatum(_),
|
||||||
|
reference_script: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] = inputs
|
||||||
|
|
||||||
|
expect
|
||||||
|
transaction_id == #"0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
|
||||||
|
expect resolved_input_value == value.from_lovelace(1000000000)
|
||||||
|
|
||||||
|
expect Address {
|
||||||
|
payment_credential: ScriptCredential(_),
|
||||||
|
stake_credential: None,
|
||||||
|
} = address
|
||||||
|
|
||||||
|
Void
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_redeemers(redeemers: Pairs<ScriptPurpose, Data>) {
|
||||||
|
expect
|
||||||
|
[
|
||||||
|
Pair(Spend(
|
||||||
|
OutputReference {
|
||||||
|
transaction_id: #"0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
output_index: 0,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
void()),
|
||||||
|
] == redeemers
|
||||||
|
Void
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void() -> Data {
|
||||||
|
let void: Data = Void
|
||||||
|
void
|
||||||
|
}
|
|
@ -1,69 +0,0 @@
|
||||||
use aiken/dict
|
|
||||||
use aiken/transaction.{
|
|
||||||
InlineDatum, Input, Output, OutputReference, ScriptContext, Spend, Spending,
|
|
||||||
}
|
|
||||||
use aiken/transaction/credential.{Address, ScriptCredential}
|
|
||||||
use aiken/transaction/value
|
|
||||||
|
|
||||||
validator {
|
|
||||||
fn spend(_tmp1: Void, _tmp2: Void, ctx: ScriptContext) {
|
|
||||||
let void: Data = Void
|
|
||||||
|
|
||||||
expect
|
|
||||||
#"78ec148ea647cf9969446891af31939c5d57b275a2455706782c6183ef0b62f1" == ctx.transaction.id
|
|
||||||
|
|
||||||
expect
|
|
||||||
Spending(
|
|
||||||
OutputReference {
|
|
||||||
transaction_id: #"0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
output_index: 0,
|
|
||||||
},
|
|
||||||
Some(void),
|
|
||||||
) == ctx.info
|
|
||||||
|
|
||||||
trace @"inputs": ctx.transaction.inputs
|
|
||||||
|
|
||||||
expect [
|
|
||||||
Input {
|
|
||||||
output_reference: OutputReference { transaction_id, output_index: 0 },
|
|
||||||
output: Output {
|
|
||||||
address,
|
|
||||||
value: resolved_input_value,
|
|
||||||
datum: InlineDatum(_),
|
|
||||||
reference_script: None,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
] = ctx.transaction.inputs
|
|
||||||
|
|
||||||
expect
|
|
||||||
transaction_id == #"0000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
|
|
||||||
expect resolved_input_value == value.from_lovelace(1000000000)
|
|
||||||
|
|
||||||
expect Address {
|
|
||||||
payment_credential: ScriptCredential(_),
|
|
||||||
stake_credential: None,
|
|
||||||
} = address
|
|
||||||
|
|
||||||
expect [] = ctx.transaction.reference_inputs
|
|
||||||
|
|
||||||
expect [] = ctx.transaction.extra_signatories
|
|
||||||
|
|
||||||
expect 42 == ctx.transaction.fee
|
|
||||||
|
|
||||||
expect
|
|
||||||
[
|
|
||||||
Pair(Spend(
|
|
||||||
OutputReference {
|
|
||||||
transaction_id: #"0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
output_index: 0,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
void),
|
|
||||||
] == ctx.transaction.redeemers
|
|
||||||
|
|
||||||
expect [] == dict.to_pairs(ctx.transaction.datums)
|
|
||||||
|
|
||||||
True
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue