Skip to content

Commit

Permalink
feat: tx-builder now supports spending from plutus scripts
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Input selectors now return selected inputs in lexicographic order
- new input selection parameter added 'mustSpendUtxo', which force such UTXOs to be part of the selection
- txBuilder now takes a new optional dependency TxEvaluator
- added to the txBuilder the following new methods 'addInput', 'addReferenceInput' and 'addDatum'
- the txBuilder now supports spending from script inputs
- the txBuilder now resolve unknown inputs from on-chain data
- outputBuilder 'datum' function can now take PlutusData as inline datum
- added to the OutputBuilder a new method 'scriptReference'
- walletUtilContext now requires an additional property 'chainHistoryProvider'
- initializeTx now takes the list of redeemerByType and the script versions of the plutus scripts in the transaction
  • Loading branch information
AngelCastilloB committed May 28, 2024
1 parent 8b0731d commit 936351e
Show file tree
Hide file tree
Showing 45 changed files with 2,270 additions and 188 deletions.
2 changes: 1 addition & 1 deletion packages/core/src/Serialization/Common/Datum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const DATUM_ARRAY_SIZE = 2;
*
* @param datum The datum to be checked for.
*/
const isDatumHash = (datum: unknown): datum is Cardano.DatumHash => datum !== null && typeof datum === 'string';
export const isDatumHash = (datum: unknown): datum is Cardano.DatumHash => datum !== null && typeof datum === 'string';

/** Represents different ways of associating a Datum with a UTxO in a transaction. */
export enum DatumKind {
Expand Down
13 changes: 13 additions & 0 deletions packages/core/src/Serialization/PlutusData/PlutusData.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Cardano from '../../Cardano';
import * as Crypto from '@cardano-sdk/crypto';
import { CborReader, CborReaderState, CborTag, CborWriter } from '../CBOR';
import { ConstrPlutusData } from './ConstrPlutusData';
import { HexBlob } from '@cardano-sdk/util';
Expand All @@ -11,6 +12,7 @@ import { bytesToHex } from '../../util/misc';
const MAX_WORD64 = 18_446_744_073_709_551_615n;
const INDEFINITE_BYTE_STRING = new Uint8Array([95]);
const MAX_BYTE_STRING_CHUNK_SIZE = 64;
const HASH_LENGTH_IN_BYTES = 32;

/**
* A type corresponding to the Plutus Core Data datatype.
Expand Down Expand Up @@ -212,6 +214,17 @@ export class PlutusData {
}
}

/**
* Computes the plutus data hash.
*
* @returns the plutus data hash.
*/
hash(): Crypto.Hash32ByteBase16 {
const hash = Crypto.blake2b(HASH_LENGTH_IN_BYTES).update(Buffer.from(this.toCbor(), 'hex')).digest();

return Crypto.Hash32ByteBase16(HexBlob.fromBytes(hash));
}

/**
* Creates a PlutusData object from the given Core PlutusData object.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Cardano from '../../../Cardano';
import * as Crypto from '@cardano-sdk/crypto';
import { CborReader, CborWriter } from '../../CBOR';
import { ExUnits } from '../../Common';
import { HexBlob, InvalidArgumentError, InvalidStateError } from '@cardano-sdk/util';
Expand All @@ -7,6 +8,7 @@ import { RedeemerTag } from './RedeemerTag';
import { hexToBytes } from '../../../util/misc';

const REDEEMER_ARRAY_SIZE = 4;
const HASH_LENGTH_IN_BYTES = 32;

/**
* The Redeemer is an argument provided to a Plutus smart contract (script) when
Expand Down Expand Up @@ -243,4 +245,15 @@ export class Redeemer {
this.#exUnits = exUnits;
this.#originalBytes = undefined;
}

/**
* Computes the redeemer hash.
*
* @returns the redeemer hash.
*/
hash(): Crypto.Hash32ByteBase16 {
const hash = Crypto.blake2b(HASH_LENGTH_IN_BYTES).update(Buffer.from(this.toCbor(), 'hex')).digest();

return Crypto.Hash32ByteBase16(HexBlob.fromBytes(hash));
}
}
7 changes: 7 additions & 0 deletions packages/core/test/Serialization/PlutusData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ describe('PlutusData', () => {
expect(() => Serialization.PlutusData.fromCbor(cbor)).not.toThrowError();
});

it('can compute correct hash', () => {
const data = Serialization.PlutusData.fromCbor(HexBlob('46010203040506'));

// Hash was generated with the CSL
expect(data.hash()).toEqual('f5e45fd57d6c5591dd9e83e76943827c4f4a9eacefd5ac974f48afd8420765a6');
});

describe('Integer', () => {
it('can encode a positive integer', () => {
const data = Serialization.PlutusData.newInteger(5n);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ describe('Redeemer', () => {
expect(redeemer.index()).toEqual(0n);
});

it('can compute correct hash', () => {
const redeemer = Redeemer.fromCore(core);

// Hash was generated with the CSL
expect(redeemer.hash()).toEqual('cfa253874f5f17b01d44e33377124e12fa0e7c8bcd88067fb9edb8c5f5ec662e');
});

describe('Redeemer tag: Spend', () => {
const spendCore = { ...core, purpose: RedeemerPurpose.spend };
const spendCbor = HexBlob('840000d8799f0102030405ff821b000086788ffc4e831b00015060e9e46451');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ import {
nativeScriptPolicyId,
util
} from '@cardano-sdk/core';
import { GreedyTxEvaluator, defaultSelectionConstraints } from '@cardano-sdk/tx-construction';
import { InputSelector, StaticChangeAddressResolver, roundRobinRandomImprove } from '@cardano-sdk/input-selection';
import { MultiSigTx } from './MultiSigTx';
import { Observable, firstValueFrom, interval, map, switchMap } from 'rxjs';
import { WalletNetworkInfoProvider } from '@cardano-sdk/wallet';
import { defaultSelectionConstraints } from '@cardano-sdk/tx-construction';

const randomHexChar = () => Math.floor(Math.random() * 16).toString(16);
const randomPublicKey = () => Crypto.Ed25519PublicKeyHex(Array.from({ length: 64 }).map(randomHexChar).join(''));
Expand Down Expand Up @@ -464,7 +464,9 @@ export class MultiSigWallet {
}
};
},
protocolParameters
protocolParameters,
redeemersByType: {},
txEvaluator: new GreedyTxEvaluator(() => this.#networkInfoProvider.protocolParameters())
});

const implicitCoin = Cardano.util.computeImplicitCoin(protocolParameters, {
Expand All @@ -476,6 +478,7 @@ export class MultiSigWallet {
constraints,
implicitValue: { coin: implicitCoin },
outputs: txOuts || new Set(),
preSelectedUtxo: new Set(),
utxo: new Set(utxo)
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const localNetworkPlutusV2CostModel = [
64_832, 32, 65_493, 32, 22_558, 32, 16_563, 32, 76_511, 32, 196_500, 453_240, 220, 0, 1, 1, 69_522, 11_687, 0, 1,
60_091, 32, 196_500, 453_240, 220, 0, 1, 1, 196_500, 453_240, 220, 0, 1, 1, 1_159_724, 392_670, 0, 2, 806_990, 30_482,
4, 1_927_926, 82_523, 4, 265_318, 0, 4, 0, 85_931, 32, 205_665, 812, 1, 1, 41_182, 32, 212_342, 32, 31_220, 32,
32_696, 32, 43_357, 32, 32_247, 32, 38_314, 32, 35_892_428, 10, 9_462_713, 1021, 10, 38_887_044, 32_947, 10
32_696, 32, 43_357, 32, 32_247, 32, 38_314, 32, 35_892_428, 10, 57_996_947, 18_975, 10, 38_887_044, 32_947, 10
];

/**
Expand Down Expand Up @@ -134,6 +134,7 @@ describe('PersonalWallet/phase2validation', () => {
}
}
]),
redeemersByType: { [Cardano.RedeemerPurpose.mint]: [scriptRedeemer] },
scriptIntegrityHash: scriptDataHash,
witness: { redeemers: [scriptRedeemer], scripts: [alwaysFailScript] }
};
Expand Down
Loading

0 comments on commit 936351e

Please sign in to comment.