IntersectMBO / cardano-node

The core component that is used to participate in a Cardano decentralised blockchain.
https://cardano.org
Apache License 2.0
3.06k stars 721 forks source link

[BUG] - Transaction's valid time range is not passed correctly to ScriptContext #3090

Closed catch-21 closed 3 years ago

catch-21 commented 3 years ago

Internal/External Internal if an IOHK staff member.

Area Plutus Related to Plutus Scripts (Alonzo).

Summary It doesn't seem possible to check whether transaction's valid range meets a condition using a plutus validator script. Every attempt I've made results in script throwing error (we have no trace logging yet to add more helpful details).

Here is one example script: https://github.com/james-iohk/Alonzo-testnet/blob/dd94ec647c99193e55d3a640b893182a4580e19a/resources/plutus-sources/plutus-deadline/src/Cardano/PlutusExample/Deadline.hs#L31-L42 The condition defines that the interval up to my deadline (15th Oct) must contain the transaction's valid range, however:

  1. transaction build throws Plutus code called 'error' even though I am using a valid range that is well within interval.
  2. A transaction built with transaction build-raw that satisfies the condition fails on submit with ValidationTagMismatch (IsValid True)

I have performed a sanity check in that the script passes if my condition is True, so it is definitely (to dl) `contains` range failing.

Steps to reproduce

  1. Serialise the script for correct plutus version (I can provide)
  2. Deposit funds at script's address with some datumhash
  3. Use transaction build to create txbody to unlock funds (where current slot is 1058640):
    cardano-cli transaction build --alonzo-era \
    --tx-in $SCRIPT_UTXO \
    --tx-in-script-file deadline.plutus \
    --invalid-before 1050000 \
    --invalid-hereafter 1060000 \
    --tx-in-datum-value 666 \
    --tx-in-redeemer-value 1 \
    --tx-in-collateral $COLLATERAL_UTXO \
    --tx-out $(<acct.addr)+2000000 \
    --change-address $(<acct.addr) \
    --protocol-params-file pparams.json \
    --testnet-magic $(<node/db/protocolMagicId) \
    --out-file txbody
    Command failed: transaction build  Error: The following scripts have execution failures:
    the script for transaction input 0 (in the order of the TxIds) failed with The Plutus script evaluation failed: An error has occurred:  User error:
    The provided Plutus code called 'error'.

Expected behavior It should be possible to build and submit a transaction that performs conditional check on transaction's valid range. This means the txInfoValidRange from TxInfo is a correct POSIXTimeRange. I can only assume at the moment that no valid range is passed at all. (I think that in the absence of valid range negative infinity to positive infinity is used, but this is to be proved).

System info (please complete the following information):

catch-21 commented 3 years ago

I've done some further investigation an have proved that the valid range is always exactly 1000000000000. Here's my notes:

I've used branch jordan/script-context-test-final to test this. And btw this link tells us that "POSIX time is measured as the number of milliseconds since 1970-01-01T00:00:00Z"

Policy Script 1 (to deadline)

mkPolicy :: POSIXTime -> ScriptContext -> Bool
mkPolicy dl ctx = (to dl) `contains` range
  where
    info :: TxInfo
    info = scriptContextTxInfo ctx

    range :: POSIXTimeRange
    range = txInfoValidRange info

With this I pass in integer redeemer as the deadline. Using build cmd I can construct a transaction only when the deadline is equal to or greater than 1000000000000.

Passing example. At approx 08:25gmt 27th Aug Slot 570847 on alonzo-purple:

$ CARDANO_NODE_SOCKET_PATH=node/node.sock cardano-cli transaction build --alonzo-era \
--tx-in 2c7dd4bb39ef9af7bdefc3b48ac284c9f148ace8aeb4749a660c68e1b5ef0e42#0 \
--tx-in-collateral fe8a8af936248b9bc70b7d6cff9ffbcf65b19090256c1f75279937ba45755c58#0 \
--tx-out "$(<acct.addr)+5000000+1 $(<deadline-redeemer.policyId)" \
--mint "1 $(<deadline-redeemer.policyId)" \
--mint-script-file deadline-redeemer.plutus \
--mint-redeemer-value 1000000000000 \
--invalid-before 570000 \
--invalid-hereafter 580000 \
--change-address $(<acct.addr) \
--protocol-params-file pparams.json \
--testnet-magic $(cat node/db/protocolMagicId) \
--out-file txbody

Passing: --mint-redeemer-value 1000000000001 Passing: The valid range passed to cli doesn't appear to make any difference because I can use slot beyond that deadline and it still passes build validation --invalid-hereafter 58000000000000 --mint-redeemer-value 1000000000000 (stands true whether measured in seconds or milliseconds) Failing: --mint-redeemer-value 999999999999

Policy Script 2 (from deadline)

mkPolicy dl ctx = (from dl) `contains` range

Using build cmd I can construct a transaction only when the deadline is equal to or less than 1000000000000. Passing: --mint-redeemer-value 1000000000000 Passing: --mint-redeemer-value 999999999999 Failing: --mint-redeemer-value 1000000000001

In conclusion. this means the valid range is always exactly 1000000000000 (must be same value for lower and upper bound), but why?

Jimbo4350 commented 3 years ago

@james-iohk has confirmed the correct validity interval is passed to the plutus script and I have also updated the script context test to confirm this.

tdiesler commented 2 years ago

Is this supposed to already work on testnet?

I send some Ada to the script and associate an invalidAfter time in millis with datum like this ...

image

The expire time is set to the boundary to the next epoch.

Then, I try to swap some tokens and set the --invalid-hereafter slot 5 mins into the future ...

image

The slot is computed as the time diff in seconds from the start time of epoch 0

In my script I verify that the datum value is after the POSIXTimeRange

image

So far, I thought that Tip (ref) reported in gLiveView is the actual slot number, but this does not seem to be the case

image

How does slot number relate to Tip? Perhaps I'm computing my --invalid-hereafter slot number incorrectly.

nhenin commented 2 years ago

Hi, I'm trying to implement the Vesting Contract from plutus-use-cases via cardano-cli and txInfoValidRange isn't behave as I would expect as well, so I'm interested in your discussion :-) ...

I have been using also the slot number given from the tip query like @tdiesler is explaining above...

From what I understood, the slot number you see is withing an Epoch (5 days so max ~ 432 000 Slots within ), in you screenshot it is 385 864... I would believe --invalid-hereafter is about absolute Slot numbers and not within the current Epoch...

I'm using this version of cardano-node (from Commits on Oct 11, 2021):

[nix-shell:~/dev/tokenomia]$ cardano-cli --version
cardano-cli 1.30.0 - linux-x86_64 - ghc-8.10
git rev e0fd8158d1c9b93fa1a74e869a45f9baf7b0e45a

Regarding cardano-ledger-specs, cardano-node is on https://github.com/input-output-hk/cardano-ledger-specs/commit/c6c4be1562e23a3dd48282387c4e48ff918fbab0 I believe the fix is here : https://github.com/input-output-hk/cardano-ledger-specs/pull/2451/commits/53d2d44f760a4743c1249e058125641700e2aa25 or at least this is in this area...

So it should be fixed theoretically...

This kind of struggling will be avoided as soon as we are able to get more information than :

Command failed: transaction build  Error: The following scripts have execution failures:
the script for transaction input 1 (in the order of the TxIds) failed with: 
The Plutus script evaluation failed: An error has occurred:  User error:
The provided Plutus code called 'error'.

I saw an issue related to this : https://github.com/input-output-hk/cardano-ledger-specs/issues/2513

using this for example would change my current life :-) :


-- | Explain why a script might fail. Scripts come in two flavors:
--
-- (1) with 3  data arguments [data,redeemer,context]
--
-- (2) with 2 data arguments [redeemer,context].
--
-- It pays to decode the context data into a real context because that provides
-- way more information. But there is no guarantee the context data really can
-- be decoded.
explainPlutusFailure,
  explain_plutus_failure ::
    forall era.
    Show (Script era) =>
    Proxy era ->
    Language ->
    SBS.ShortByteString ->
    PV1.EvaluationError ->
    [PV1.Data] ->
    CostModel ->
    ExUnits ->
    ScriptResult

Extra information related to Slot numbering

I'm using this logic for making conversion from the slot from the tip query into posixTime when I need to :

image

I can then use that to have this kind of info :

- Last Slot Synced : 40192048 (Δ 16.673429471s s)
- Local Time : 2021-10-18T12:48:00.673429471Z
- Last Sync  : 2021-10-18T12:47:44Z
nhenin commented 2 years ago

UPDATE : It works on my side now....

tdiesler commented 2 years ago

Oh dear, the math is of. When I query the current slot like this ...

$ cardano-cli query tip --testnet-magic 1097911063
{
    "epoch": 163,
    "hash": "fdcd11b8ea673b62511c30b7a8a8f0c0be6140ad8e4fe1b11ef2d65f7592acce",
    "slot": 40265924,
    "block": 3003825,
    "era": "Alonzo",
    "syncProgress": "100.00"
}

I get 40265924, which is also what gLiveView shows as Tip However, I also get 40265924 / 432000 => 93 and not 163 So, I cant compute the current slot from the start time of epoch 0 Perhaps the start time for the current epoch is a better baseline

rdlrt commented 2 years ago

However, I also get 40265924 / 432000 => 93 and not 163

You cannot simply divide it directly by 432000, remember - the Byron era had 21600 slots per epoch, and you'd divide your slots accordingly

tdiesler commented 2 years ago

@rdlrt Merci - good point - the Byron era was before my time ;-)

tdiesler commented 2 years ago

@rdlrt Is there a specific epoch when the Shelley era started? Unfortunately, slot data does not seem to be available from Blockfrost. I must somehow be able to establish a baseline for time/slot. I could query the network tip at the current time, but that would not be deterministic.

tdiesler commented 2 years ago

https://discord.com/channels/@me/865286678464561252/899961500834357248

catch-21 commented 2 years ago

Fyi we recently discussed the topic of time/slots in Discord here: https://discord.com/channels/@me/865286678464561252/899961500834357248

The start of Shelley era is different for each network. I've not got a way to query for that but at least it's constant.

tdiesler commented 2 years ago

@james-iohk Do you happen to know how many preShelleyEpochs there are on mainnet?

rdlrt commented 2 years ago

Is there a specific epoch when the Shelley era started?

You can query param_proposals in dbsync for major protocol version updates (atleast for mainstream networks), but if you want direct constants, check here . If you want to see logic to derive them from genesis start time and current tip, see here

catch-21 commented 2 years ago

@tdiesler Yes, epoch 208 is first Shelley on Mainnet (I just checked in explorer when total slots changed) Thanks for sharing that @rdlrt :+1:

nhenin commented 2 years ago

Hi guys, my team and I are testing now the Vesting Contract (plutus-use-cases) on mainnet (the testnet is working fine...) and we discovered that the hard coded value 1000000000000 is still there only on mainnet :

txInfoValidRange = Interval
        { ivFrom = LowerBound
            ( Finite
                ( POSIXTime
                    { getPOSIXTime = 1000000000000 }
                )
            ) True
        , ivTo = UpperBound
            ( Finite
                ( POSIXTime
                    { getPOSIXTime = 1000000000000 }
                )
            ) False
        }
tdiesler commented 2 years ago

For what it's worth, here an implementation in TCL https://github.com/tdiesler/nessus-cardano/blob/main/plutus/tokenswap/context/tcl/include/utils.tcl#L395

image

Jimbo4350 commented 2 years ago

@nhenin Can you try again? The hardfork only came into affect on Oct 29th @ 5:45 pm -4:00 UTC

nhenin commented 2 years ago

I have tried again, last Friday the protocol version was equal to 5 and now to 6, so it works now to me on the mainnet :-) Thanks !

a-osiecki commented 2 years ago

I'm getting this failure when checking a transaction's valid range. I'm on tag 73f9a746362695dc2cb63ba757fbcabb81733d23 of cardano-node.

catch-21 commented 2 years ago

I'm getting this failure when checking a transaction's valid range. I'm on tag 73f9a74 of cardano-node.

Hey @a-osiecki. This particular issue is resolved but perhaps you're hitting something related. Could you provide more details (steps taken, error produced etc), ideally in a new issue, tag me and I'll take a look.

a-osiecki commented 2 years ago

Hi @james-iohk! I'm getting this error when testing a contract in a private testnet. Where should I post the issue?

catch-21 commented 2 years ago

@a-osiecki This is a generic validation error so you'll need to provide more context. If you believe you've hit a bug then raise a new issue in this repo with as much detail as possible. For guidence, we have tests for this issue before and after protocol upgrade using the emulator here. You can also ask for specific support in the Cardano Stack Exchange.