@@ -27,6 +27,7 @@ import (
2727 lcommon "github.com/blinklabs-io/gouroboros/ledger/common"
2828 "github.com/blinklabs-io/gouroboros/ledger/common/script"
2929 "github.com/blinklabs-io/gouroboros/ledger/conway"
30+ "github.com/blinklabs-io/plutigo/data"
3031)
3132
3233var ConwayEraDesc = EraDesc {
@@ -160,14 +161,133 @@ func ValidateTxConway(
160161 ls lcommon.LedgerState ,
161162 pp lcommon.ProtocolParameters ,
162163) error {
164+ // Validate TX through ledger validation rules
163165 errs := []error {}
166+ var err error
164167 for _ , validationFunc := range conway .UtxoValidationRules {
165- errs = append (
166- errs ,
167- validationFunc (tx , slot , ls , pp ),
168+ err = validationFunc (tx , slot , ls , pp )
169+ if err != nil {
170+ errs = append (
171+ errs ,
172+ err ,
173+ )
174+ }
175+ }
176+ if len (errs ) > 0 {
177+ return errors .Join (errs ... )
178+ }
179+ // Skip script evaluation if TX is marked as not valid
180+ if ! tx .IsValid () {
181+ return nil
182+ }
183+ // Resolve inputs
184+ resolvedInputs := []lcommon.Utxo {}
185+ for _ , tmpInput := range tx .Inputs () {
186+ tmpUtxo , err := ls .UtxoById (tmpInput )
187+ if err != nil {
188+ return err
189+ }
190+ resolvedInputs = append (
191+ resolvedInputs ,
192+ tmpUtxo ,
193+ )
194+ }
195+ // Resolve reference inputs
196+ resolvedRefInputs := []lcommon.Utxo {}
197+ for _ , tmpRefInput := range tx .ReferenceInputs () {
198+ tmpUtxo , err := ls .UtxoById (tmpRefInput )
199+ if err != nil {
200+ return err
201+ }
202+ resolvedRefInputs = append (
203+ resolvedRefInputs ,
204+ tmpUtxo ,
168205 )
169206 }
170- return errors .Join (errs ... )
207+ // Build TX script map
208+ scripts := make (map [lcommon.ScriptHash ]lcommon.Script )
209+ for _ , refInput := range resolvedRefInputs {
210+ tmpScript := refInput .Output .ScriptRef ()
211+ if tmpScript == nil {
212+ continue
213+ }
214+ scripts [tmpScript .Hash ()] = tmpScript
215+ }
216+ for _ , tmpScript := range tx .Witnesses ().PlutusV1Scripts () {
217+ scripts [tmpScript .Hash ()] = tmpScript
218+ }
219+ for _ , tmpScript := range tx .Witnesses ().PlutusV2Scripts () {
220+ scripts [tmpScript .Hash ()] = tmpScript
221+ }
222+ for _ , tmpScript := range tx .Witnesses ().PlutusV3Scripts () {
223+ scripts [tmpScript .Hash ()] = tmpScript
224+ }
225+ // Evaluate scripts
226+ var txInfoV3 script.TxInfo
227+ txInfoV3 , err = script .NewTxInfoV3FromTransaction (ls , tx , slices .Concat (resolvedInputs , resolvedRefInputs ))
228+ if err != nil {
229+ return err
230+ }
231+ for _ , redeemerPair := range txInfoV3 .(script.TxInfoV3 ).Redeemers {
232+ purpose := redeemerPair .Key
233+ redeemer := redeemerPair .Value
234+ // Lookup script from redeemer purpose
235+ tmpScript := scripts [purpose .ScriptHash ()]
236+ if tmpScript == nil {
237+ return fmt .Errorf ("could not find script with hash %s" , purpose .ScriptHash ().String ())
238+ }
239+ switch s := tmpScript .(type ) {
240+ case * lcommon.PlutusV3Script :
241+ sc := script .NewScriptContextV3 (txInfoV3 , redeemer , purpose )
242+ // Round-trip the script context through CBOR
243+ // This is a temporary hack to work around a bug in plutigo
244+ scCbor , err := data .Encode (sc .ToPlutusData ())
245+ if err != nil {
246+ return err
247+ }
248+ scNew , err := data .Decode (scCbor )
249+ if err != nil {
250+ return err
251+ }
252+ _ , err = s .Evaluate (
253+ scNew ,
254+ redeemer .ExUnits ,
255+ )
256+ if err != nil {
257+ /*
258+ fmt.Printf("TX ID: %s\n", tx.Hash().String())
259+ fmt.Printf("purpose = %#v, redeemer = %#v\n", purpose, redeemer)
260+ scriptHash := s.Hash()
261+ fmt.Printf("scriptHash = %s\n", scriptHash.String())
262+ fmt.Printf("tx = %x\n", tx.Cbor())
263+ // Build inputs/outputs strings that can be plugged into Aiken script_context tests for comparison
264+ var tmpInputs []lcommon.TransactionInput
265+ var tmpOutputs []lcommon.TransactionOutput
266+ for _, input := range slices.Concat(resolvedInputs, resolvedRefInputs) {
267+ tmpInputs = append(tmpInputs, input.Id)
268+ tmpOutputs = append(tmpOutputs, input.Output)
269+ }
270+ tmpInputsCbor, err2 := cbor.Encode(tmpInputs)
271+ if err2 != nil {
272+ return err2
273+ }
274+ fmt.Printf("tmpInputs = %x\n", tmpInputsCbor)
275+ tmpOutputsCbor, err2 := cbor.Encode(tmpOutputs)
276+ if err2 != nil {
277+ return err2
278+ }
279+ fmt.Printf("tmpOutputs = %x\n", tmpOutputsCbor)
280+ scCbor, err2 := data.Encode(sc.ToPlutusData())
281+ if err2 != nil {
282+ return err2
283+ }
284+ fmt.Printf("scCbor = %x\n", scCbor)
285+ */
286+ return err
287+ }
288+ }
289+ }
290+ return nil
171291}
172292
173293func EvaluateTxConway (
@@ -263,38 +383,6 @@ func EvaluateTxConway(
263383 Tag : redeemer .Tag ,
264384 Index : redeemer .Index ,
265385 }] = usedBudget
266- /*
267- if err != nil && !strings.Contains(err.Error(), "unimplemented") {
268- fmt.Printf("TX ID: %s\n", tx.Hash().String())
269- fmt.Printf("purpose = %#v, redeemer = %#v\n", purpose, redeemer)
270- scriptHash := s.Hash()
271- fmt.Printf("scriptHash = %s\n", scriptHash.String())
272- fmt.Printf("tx = %x\n", tx.Cbor())
273- // Build inputs/outputs strings that can be plugged into Aiken script_context tests for comparison
274- var tmpInputs []lcommon.TransactionInput
275- var tmpOutputs []lcommon.TransactionOutput
276- for _, input := range slices.Concat(resolvedInputs, resolvedRefInputs) {
277- tmpInputs = append(tmpInputs, input.Id)
278- tmpOutputs = append(tmpOutputs, input.Output)
279- }
280- tmpInputsCbor, err := cbor.Encode(tmpInputs)
281- if err != nil {
282- return 0, lcommon.ExUnits{}, nil, err
283- }
284- fmt.Printf("tmpInputs = %x\n", tmpInputsCbor)
285- tmpOutputsCbor, err := cbor.Encode(tmpOutputs)
286- if err != nil {
287- return 0, lcommon.ExUnits{}, nil, err
288- }
289- fmt.Printf("tmpOutputs = %x\n", tmpOutputsCbor)
290- fmt.Printf("sc = %#v\n", sc)
291- scCbor, err := data.Encode(sc.ToPlutusData())
292- if err != nil {
293- return 0, lcommon.ExUnits{}, nil, err
294- }
295- fmt.Printf("scCbor = %x\n", scCbor)
296- }
297- */
298386 default :
299387 return 0 , lcommon.ExUnits {}, nil , fmt .Errorf ("unimplemented script type: %T" , tmpScript )
300388 }
0 commit comments