From 08f5990e9243bc2f869af524c99e2f86ec0d1c66 Mon Sep 17 00:00:00 2001 From: Pal Dorogi Date: Sun, 7 Jan 2024 18:10:06 +0100 Subject: [PATCH] Added converting recursive PlutusData & fixed kupmiosv5 provider script converting issue caused my previous PR --- bun.lockb | Bin 151411 -> 151748 bytes src/plutus/data.ts | 52 ++++++++++++++++++------ src/provider/kupmiosv5.ts | 8 +++- src/translucent/tx.ts | 65 +++++++++++++++++------------- src/wallets/chained.ts | 81 ++++++++++++++++++++------------------ src/wallets/mod.ts | 12 +++--- 6 files changed, 133 insertions(+), 85 deletions(-) diff --git a/bun.lockb b/bun.lockb index 687864c3761e5d5e210c1987a29f3c942f4eaf13..4306da23d6e4db443200f9c707f783e37532c675 100755 GIT binary patch delta 2119 zcmYk62~-nT6o%(b*b)eaMj$987^;9+St4R_Az%d;K$KD}%Hmp-LZJ{AS%QcbDbQfl zY4M0uMNw!)Mmwm82+AgkRH}efqditcELxA%LW{jmUVD1a{OA4md-uJ$^Cp=WrToj^ z@-;IZMbo!b9dg?Fo}xdd$Z_t&nFrU7+HQ`tvorDi`Xh(gM)tFUXbMMKaj1+oSDi|& zFxK}IQY!D=$7h#VcM5RAD(!njZ)t-J=$z&B zc!|V_ZS~af{!8)}XlVK~T8kvpv`UGQcCojJceJf-w`3|#**xzX67APvE^JPNfhNxQQ7*?thKgF;-qcx;A{CRFTRV4qUOR3rR@svKrz}~ zDlc?|ZByMbMihHsTMHh-3v2b2O@T$>!8>Y-t|ruVIVB6I>Z1-7Lp7KoLuZs4TKNPc~5`5d9oql6EwpbeBxa_?3 z>(Q}*dqD4BQ`zHH5d(h>O?5((6(fWC`aUy+j)>AjPOu?`1?4k<;dddNweZh44 zh0&m)OUv%T<1w`jPR1RFzwj9ed*dIJIPyq#F8Iuv_Js%azmkaK1;P2Bv~F$A3V0E6 z(N9|$U+ELe#x~~h)O$`SZZFPR7yL4;dsVX3zy1r+{EaKhihi#tJ)w@A{EaTkuX(@t zWL}Sxto29sjHtKX@}tDZPL2zzm$4Dsue$WZYem%9gfT=><@g6lLeJoqo>TstymU2Z zRo7h>mo(LS`OK#yoe?E9lcW_r=T5udl)mgJ4Y69-uTpLA@BCxz$dTi}I1k^RnD?Z5 zfwN~oJ6qUnD_4FTU-SOuSXovJTH+XCZgaLOeE+rmx((%Ro+X9p4?MQX@_*5}=c;b@ z`x{k0W~w^n`%7-Pmb)k$D+lJ8^-W1*pAHO6Qm*go$|yct*jOZ1ewp7q`H;Jvyhcx4 zZJ==RS{jFHf`mIx*T*Xyvn|)m_fnOWUavj4HcFH5sC#{!bx7%{j0nzL4;k^5D-FsD z-!K~&HC+^S#U$A0as}68%bxGk->X~nNMqf&693f~0-dSBj_s#Y(q{cu;eO@%&-btVwe)gztzB)bsrgXuv*d@j zcMU&`-&K)$wDVy|*6h)fONJflDqB8ehBDiypX9I1>v3C(9VB%z6Fnm5iOU=B~??@Iaj z$AyX{4414LFQcLe(}R2wsas1J{*${AwvI3ZU<$(46J`kPItYS!m4t-qTjL3}krePD z&n1gC5hehZ1s=yIgc*b1Mp!gqCcxZbEpBErVM6c)ARJo=GX-A=f@aiI3_$#p6F_qC zIO1T$J%~W|gvG;%tr_SrVF@te(|89|4jwm!Pa03dCz)yjgPTewY$AAn5RR0$OifYd zkn9EFPE!HmW-LHOAlzv>S!@YjLs$l363D~Az1+@SXwU|EFbT&{01Wq!BTGhe9O-d=$4~?B|Acv6fhKV` zD$%+b0!{Du2BJP@UJtq~Q1*@IGlDolFKX9E9^6lN(E%Zhi9^W762@X49SHkzIs-Zr z=9vUBJ&hR_(T3cYCiF`la$`=(X5~>r;s>;xTjxus8zNWcZUF7Tlnx;on!|K?L-oBOWXK5v=}qV#pCWo4 delta 1930 zcmX|=4OC2N7{~8>o4#gDN~tmW5-PTwDPvSNMUq`LT0(h~AuE9cA# zSne|8Pj`*vbx74AGX0sktlYKkVJ4?+cJ1dc>*k$X+%6>v9I-iTtw@2qJhp^K+fS(C z(S2qv9F_e7Ct7CykgMwW&R(V5rccZ49vZ6NxQggN7k_fhJatoQjhUkhl_VKKs*D7- zD*r_Stuy!TsSE`(RsI4!`skuZ0@Wso{dlrYX{_oHj3gALiq`I)m8VI|a*O`hqLZ7kXQ{3C5r<}y(2t9(%{q#gX>@R2 zIW_mnjYIPn9Z7$ryZ(YpWq3uX-`2?Bo;-C}b?++Qy)7NI_?nZJXSAfWqM;(b7Tr5u zP*<%!a8kHm+jswmNmTR0J;N5dk8DkDWxtyLc5-RFhGR|TwYTl;Klh5*yCq>^r=_=} zZz^3}`G(_JW!D|~!cND|YpKvH{S%jRdZOR2(7WxBD2qSp8B!*Wb?2N`8{mFUv^S?Z_MSy&=5EX^CBNImuK@^XDVdFoX zzlYj7R=zr!clEQL-`yPd?BuWVOD^(|gw`S7tkEvp6>ieL<}5$nl%l)GPA~gC&|>nP ziwjEB-!c!re!TJy*DLli-BW9A<9TelM@I3C&c5nW@8tOUu29W|=b1@fap$FNc3<17 zWEnNvf-}{}zvw-CK4bGxm}{3NPFvvEzh!QdH2rqnv?wL*_x6p@Gryj=VCGX^p0R!eyRMY18$c9PXn?lbSpPefS%?^`prVlI$JdSXdalxmuY%|Mv zz~;eP+(rb;CV)Q#f=DK|u#gW~CP)Xg6&OB?0mueCj$dHJJxm1Iv1~hx*cyUPvup>9 zcrznVF?ie*zGOT-2~-URH>DV3$pm2xi7yDpF1FAF{9zF8GzJ)MW-=%Xgd2@x>rBBb zSr*SSGsxG0$IU0OOaT5mc-&|r%cg+8;m<;RmGBTkkQxl`G?`^n!Q+i_Qz(KkTq3qMA}h{tVwI?lnJTJ zj2e-0OKPJjIgRS;K{^!Q31Fd!ylhIXdWo!=9X@2DE)r4irKCO8+k(data: Exact, type?: T): Datum | Redeemer { +function to( + data: Exact, + type?: T, + recType?: string, +): Datum | Redeemer { function serialize(data: Data): C.PlutusData { try { if (typeof data === "bigint") { @@ -301,7 +305,7 @@ function to(data: Exact, type?: T): Datum | Redeemer { throw new Error("Could not serialize the data: " + error); } } - const d = type ? castTo(data, type) : (data as Data); + const d = type ? castTo(data, type, recType) : (data as Data); return toHex(serialize(d).to_bytes()) as Datum | Redeemer; } @@ -618,11 +622,26 @@ function castFrom(data: Data, type: T): T { throw new Error("Could not type cast data."); } -function castTo(struct: Exact, type: T): Data { - const shape = type as Json; +function castTo( + struct: Exact, + type: T, + recType?: string, + recShape?: { + recType: string; + shape: T; + shapeType: string; + }, +): Data { + let shape = type as Json; if (!shape) throw new Error("Could not type cast struct."); - const shapeType = (shape.anyOf ? "enum" : "") || shape.dataType; + let shapeType = (shape.anyOf ? "enum" : "") || shape.dataType; + if (recType === shape.title) { + recShape = { recType: recType!, shape: shape, shapeType: shapeType }; + } else if (recShape && shape.$ref) { + shape = recShape.shape; + shapeType = recShape.shapeType; + } switch (shapeType) { case "integer": { if (typeof struct !== "bigint") { @@ -655,6 +674,8 @@ function castTo(struct: Exact, type: T): Data { castTo( (struct as Record)[field.title || "wrapper"], field, + recType, + recShape, ), ); return shape.hasConstr || shape.hasConstr === undefined @@ -664,7 +685,7 @@ function castTo(struct: Exact, type: T): Data { case "enum": { // When enum has only one entry it's a single constructor/record object if (shape.anyOf.length === 1) { - return castTo(struct, shape.anyOf[0]); + return castTo(struct, shape.anyOf[0], recType, recShape); } if (isBoolean(shape)) { @@ -679,7 +700,9 @@ function castTo(struct: Exact, type: T): Data { if (fields.length !== 1) { throw new Error("Could not type cast to nullable object."); } - return new Constr(0, [castTo(struct, fields[0])]); + return new Constr(0, [ + castTo(struct, fields[0], recType, recShape), + ]); } } switch (typeof struct) { @@ -721,13 +744,13 @@ function castTo(struct: Exact, type: T): Data { // check if named args args instanceof Array ? args.map((item, index) => - castTo(item, enumEntry.fields[index]), + castTo(item, enumEntry.fields[index], recType, recShape), ) : enumEntry.fields.map((entry: Json) => { const [_, item]: [string, Json] = Object.entries(args).find( ([title]) => title === entry.title, )!; - return castTo(item, entry); + return castTo(item, entry, recType, recShape); }), ); } @@ -741,13 +764,15 @@ function castTo(struct: Exact, type: T): Data { if (shape.items instanceof Array) { // tuple const fields = struct.map((item, index) => - castTo(item, shape.items[index]), + castTo(item, shape.items[index], recType, recShape), ); return shape.hasConstr ? new Constr(0, fields) : fields; } else { // array listConstraints(struct, shape); - return struct.map((item) => castTo(item, shape.items)); + return struct.map((item) => + castTo(item, shape.items, recType, recShape), + ); } } case "map": { @@ -759,7 +784,10 @@ function castTo(struct: Exact, type: T): Data { const map = new Map(); for (const [key, value] of struct.entries()) { - map.set(castTo(key, shape.keys), castTo(value, shape.values)); + map.set( + castTo(key, shape.keys, recType, recShape), + castTo(value, shape.values, recType, recShape), + ); } return map; } diff --git a/src/provider/kupmiosv5.ts b/src/provider/kupmiosv5.ts index 8232890..0e81078 100644 --- a/src/provider/kupmiosv5.ts +++ b/src/provider/kupmiosv5.ts @@ -263,12 +263,16 @@ export class KupmiosV5 implements Provider { } else if (language === "plutus:v1") { return { type: "PlutusV1", - script: toHex(C.PlutusScript.new(fromHex(script)).to_bytes()), + script: toHex( + C.PlutusV1Script.new(fromHex(script)).to_bytes(), + ), }; } else if (language === "plutus:v2") { return { type: "PlutusV2", - script: toHex(C.PlutusScript.new(fromHex(script)).to_bytes()), + script: toHex( + C.PlutusV2Script.new(fromHex(script)).to_bytes(), + ), }; } })()), diff --git a/src/translucent/tx.ts b/src/translucent/tx.ts index de9e531..d78339d 100644 --- a/src/translucent/tx.ts +++ b/src/translucent/tx.ts @@ -924,50 +924,61 @@ export class Tx { BigInt(slotConfig.zeroSlot), slotConfig.slotLength, ); - const redeemers = C.Redeemers.new() + const redeemers = C.Redeemers.new(); for (const redeemerBytes of uplcResults) { let redeemer: C.Redeemer = C.Redeemer.from_bytes(redeemerBytes); this.txBuilder.set_exunits( C.RedeemerWitnessKey.new(redeemer.tag(), redeemer.index()), redeemer.ex_units(), ); - redeemers.add(redeemer) + redeemers.add(redeemer); } let builtTx = this.txBuilder.build(0, changeAddress).build_unchecked(); { - const datums = C.PlutusList.new() - const unhashedData = builtTx.witness_set().plutus_data() - let hashes = [] - if (unhashedData){ - for (let i=0; iutxo.input().to_bytes()==input.to_bytes()) - const datum = utxo?.output().datum() - if (datum){ - const inline = datum.as_inline_data() - if (inline){ - datums.add(inline) - }else{ + const utxo = allUtxos.find( + (utxo) => utxo.input().to_bytes() == input.to_bytes(), + ); + const datum = utxo?.output().datum(); + if (datum) { + const inline = datum.as_inline_data(); + if (inline) { + datums.add(inline); + } else { const hash = datum.as_data_hash(); - if (hash){ - const idx = hashes.indexOf(hash.to_hex()) - const data = unhashedData?.get(idx)! - datums.add(data) + if (hash) { + const idx = hashes.indexOf(hash.to_hex()); + const data = unhashedData?.get(idx)!; + datums.add(data); } } } } - const languages = C.Languages.new() - languages.add(C.Language.new_plutus_v2()) - const sdh = C.calc_script_data_hash(redeemers, datums, costMdls, languages) - if (sdh){ - const bodyWithDataHash = builtTx.body() - bodyWithDataHash.set_script_data_hash(sdh) - builtTx = C.Transaction.new(bodyWithDataHash, builtTx.witness_set(), builtTx.auxiliary_data()) + const languages = C.Languages.new(); + languages.add(C.Language.new_plutus_v2()); + const sdh = C.calc_script_data_hash( + redeemers, + datums, + costMdls, + languages, + ); + if (sdh) { + const bodyWithDataHash = builtTx.body(); + bodyWithDataHash.set_script_data_hash(sdh); + builtTx = C.Transaction.new( + bodyWithDataHash, + builtTx.witness_set(), + builtTx.auxiliary_data(), + ); } } return new TxComplete(this.translucent, builtTx); diff --git a/src/wallets/chained.ts b/src/wallets/chained.ts index 2582a3f..a41cb9f 100644 --- a/src/wallets/chained.ts +++ b/src/wallets/chained.ts @@ -12,81 +12,86 @@ import { utxoToCore, fromHex, coreToUtxo, -} from '../mod' -import { AbstractWallet } from './abstract' +} from "../mod"; +import { AbstractWallet } from "./abstract"; export class ChainedWallet implements AbstractWallet { - translucent: Translucent - wallet: AbstractWallet - utxos: UTxO[] = [] + translucent: Translucent; + wallet: AbstractWallet; + utxos: UTxO[] = []; constructor(translucent: Translucent, wallet: AbstractWallet) { - this.translucent = translucent - this.wallet = wallet - wallet.getUtxos().then((utxos) => (this.utxos = utxos)) + this.translucent = translucent; + this.wallet = wallet; + wallet.getUtxos().then((utxos) => (this.utxos = utxos)); } async refreshUtxos() { - this.utxos = await this.wallet.getUtxos() + this.utxos = await this.wallet.getUtxos(); } - async chain(tx: Transaction, predicate: (utxo: UTxO)=>boolean){ - const txCore = C.Transaction.from_bytes(fromHex(tx)) - const hash = C.hash_transaction(txCore.body()) - const inputs = txCore.body().inputs() - const outputs = txCore.body().outputs() - const toConsume: Record = {} - for (let i=0; i boolean) { + const txCore = C.Transaction.from_bytes(fromHex(tx)); + const hash = C.hash_transaction(txCore.body()); + const inputs = txCore.body().inputs(); + const outputs = txCore.body().outputs(); + const toConsume: Record = {}; + for (let i = 0; i < inputs.len(); i++) { + const input = inputs.get(i); + toConsume[input.transaction_id().to_hex() + input.index()] = true; + } + this.utxos = this.utxos.filter( + (utxo) => toConsume[utxo.txHash + utxo.outputIndex.toString()] == true, + ); + for (let i = 0; i < outputs.len(); i++) { + const output = C.TransactionUnspentOutput.new( + C.TransactionInput.new(hash, C.BigNum.from_str(i.toString())), + outputs.get(i), + ); + const utxo = coreToUtxo(output); + if (predicate(utxo)) { + this.utxos.push(utxo); + } } - this.utxos = this.utxos.filter((utxo)=>toConsume[utxo.txHash+utxo.outputIndex.toString()]==true) - for (let i=0; i { - return this.wallet.address() + return this.wallet.address(); } rewardAddress(): Promise { - return this.wallet.rewardAddress() + return this.wallet.rewardAddress(); } getUtxos(): Promise { - return Promise.resolve(this.utxos) + return Promise.resolve(this.utxos); } getUtxosCore(): Promise { - const outputs = C.TransactionUnspentOutputs.new() - const utxos = this.utxos.map(utxoToCore) - for (const utxo of utxos){ - outputs.add(utxo) + const outputs = C.TransactionUnspentOutputs.new(); + const utxos = this.utxos.map(utxoToCore); + for (const utxo of utxos) { + outputs.add(utxo); } - return Promise.resolve(outputs) + return Promise.resolve(outputs); } getDelegation(): Promise { - return this.wallet.getDelegation() + return this.wallet.getDelegation(); } signTx(tx: C.Transaction): Promise { - return this.wallet.signTx(tx) + return this.wallet.signTx(tx); } signMessage( address: Address | RewardAddress, payload: Payload, ): Promise { - return this.wallet.signMessage(address, payload) + return this.wallet.signMessage(address, payload); } submitTx(signedTx: Transaction): Promise { - return this.wallet.submitTx(signedTx) + return this.wallet.submitTx(signedTx); } } diff --git a/src/wallets/mod.ts b/src/wallets/mod.ts index d02cf18..ed48d9c 100644 --- a/src/wallets/mod.ts +++ b/src/wallets/mod.ts @@ -1,6 +1,6 @@ -export * from "./abstract" -export * from "./chained" -export * from "./private_key" -export * from "./public_wallet" -export * from "./seed" -export * from "./wallet_connector" +export * from "./abstract"; +export * from "./chained"; +export * from "./private_key"; +export * from "./public_wallet"; +export * from "./seed"; +export * from "./wallet_connector";