1515package alonzo
1616
1717import (
18+ //"encoding/json"
19+ "fmt"
1820 "math"
1921 "sort"
2022
23+ //"strconv"
24+ "strings"
25+
2126 "github.com/blinklabs-io/gouroboros/cbor"
2227 "github.com/blinklabs-io/gouroboros/ledger/common"
2328 "github.com/blinklabs-io/gouroboros/ledger/mary"
2429 cardano "github.com/utxorpc/go-codegen/utxorpc/v1alpha/cardano"
2530)
2631
32+ type PlutusVersion string
33+
34+ const (
35+ PlutusV1 PlutusVersion = "PlutusV1"
36+ PlutusV2 PlutusVersion = "PlutusV2"
37+ PlutusV3 PlutusVersion = "PlutusV3"
38+ )
39+
40+ type CostModel struct {
41+ Version PlutusVersion
42+ Parameters map [string ]int64
43+ Order []string
44+ }
45+
2746type AlonzoProtocolParameters struct {
2847 mary.MaryProtocolParameters
2948 MinPoolCost uint64
3049 AdaPerUtxoByte uint64
31- CostModels map [uint ][] int64
50+ CostModels map [PlutusVersion ] * CostModel
3251 ExecutionCosts common.ExUnitPrice
3352 MaxTxExUnits common.ExUnits
3453 MaxBlockExUnits common.ExUnits
@@ -50,7 +69,7 @@ func (p *AlonzoProtocolParameters) Update(
5069 p .AdaPerUtxoByte = * paramUpdate .AdaPerUtxoByte
5170 }
5271 if paramUpdate .CostModels != nil {
53- p .CostModels = paramUpdate .CostModels
72+ p .convertLegacyCostModels ( paramUpdate .CostModels )
5473 }
5574 if paramUpdate .ExecutionCosts != nil {
5675 p .ExecutionCosts = * paramUpdate .ExecutionCosts
@@ -72,10 +91,12 @@ func (p *AlonzoProtocolParameters) Update(
7291 }
7392}
7493
75- func (p * AlonzoProtocolParameters ) UpdateFromGenesis (genesis * AlonzoGenesis ) {
94+ func (p * AlonzoProtocolParameters ) UpdateFromGenesis (genesis * AlonzoGenesis ) error {
7695 if genesis == nil {
77- return
96+ return nil
7897 }
98+
99+ // Common parameter updates
79100 p .AdaPerUtxoByte = genesis .LovelacePerUtxoWord / 8
80101 p .MaxValueSize = genesis .MaxValueSize
81102 p .CollateralPercentage = genesis .CollateralPercentage
@@ -88,47 +109,117 @@ func (p *AlonzoProtocolParameters) UpdateFromGenesis(genesis *AlonzoGenesis) {
88109 Memory : uint64 (genesis .MaxBlockExUnits .Mem ),
89110 Steps : uint64 (genesis .MaxBlockExUnits .Steps ),
90111 }
91- if genesis . ExecutionPrices . Mem != nil &&
92- genesis .ExecutionPrices .Steps != nil {
112+
113+ if genesis . ExecutionPrices . Mem != nil && genesis .ExecutionPrices .Steps != nil {
93114 p .ExecutionCosts = common.ExUnitPrice {
94115 MemPrice : & cbor.Rat {Rat : genesis .ExecutionPrices .Mem .Rat },
95116 StepPrice : & cbor.Rat {Rat : genesis .ExecutionPrices .Steps .Rat },
96117 }
97118 }
98119
99- // Process cost models
100120 if genesis .CostModels != nil {
101- p .CostModels = make (map [uint ][]int64 )
121+ p .CostModels = make (map [PlutusVersion ]* CostModel )
122+
102123 for lang , model := range genesis .CostModels {
103- var langKey uint
104- switch lang {
105- case "plutus:v1" :
106- langKey = 0
107- case "plutus:v2" :
108- langKey = 1
109- default :
110- // Skip unknown language
124+ version , ok := toPlutusVersion (lang )
125+ if ! ok {
111126 continue
112127 }
113128
114- // Get sorted keys to determine indexes
115- keys := make ([]string , 0 , len (model ))
116- for k := range model {
117- keys = append (keys , k )
129+ params := make (map [string ]int64 )
130+ order := make ([]string , 0 , len (model ))
131+
132+ // Since model is now map[string]int, we don't need type assertions
133+ for name , val := range model {
134+ params [name ] = int64 (val ) // Convert int to int64
135+ order = append (order , name )
118136 }
119- sort .Strings (keys )
120137
121- // Create slice with values in alphabetical order
122- costs := make ([]int64 , len (keys ))
123- for i , key := range keys {
124- costs [i ] = int64 (model [key ])
138+ // Sort keys alphabetically (maintains consistency with original behavior)
139+ sort .Strings (order )
140+
141+ p .CostModels [version ] = & CostModel {
142+ Version : version ,
143+ Parameters : params ,
144+ Order : order ,
125145 }
146+ }
147+ }
148+ return nil
149+ }
150+
151+ func (p * AlonzoProtocolParameters ) convertLegacyCostModels (legacyModels map [uint ][]int64 ) {
152+ p .CostModels = make (map [PlutusVersion ]* CostModel )
126153
127- p .CostModels [langKey ] = costs
154+ for langKey , values := range legacyModels {
155+ var version PlutusVersion
156+ switch langKey {
157+ case 0 :
158+ version = PlutusV1
159+ case 1 :
160+ version = PlutusV2
161+ case 2 :
162+ version = PlutusV3
163+ default :
164+ continue
165+ }
166+
167+ params := make (map [string ]int64 )
168+ order := make ([]string , len (values ))
169+ for i , val := range values {
170+ name := fmt .Sprintf ("param%d" , i )
171+ params [name ] = val
172+ order [i ] = name
173+ }
174+
175+ p .CostModels [version ] = & CostModel {
176+ Version : version ,
177+ Parameters : params ,
178+ Order : order ,
128179 }
129180 }
130181}
131182
183+ func toPlutusVersion (key string ) (PlutusVersion , bool ) {
184+ switch strings .ToLower (key ) {
185+ case "plutus:v1" , "plutusv1" :
186+ return PlutusV1 , true
187+ case "plutus:v2" , "plutusv2" :
188+ return PlutusV2 , true
189+ case "plutus:v3" , "plutusv3" :
190+ return PlutusV3 , true
191+ default :
192+ return "" , false
193+ }
194+ }
195+
196+ func (p * AlonzoProtocolParameters ) ToLegacyCostModels () map [uint ][]int64 {
197+ legacyModels := make (map [uint ][]int64 )
198+
199+ for version , model := range p .CostModels {
200+ var langKey uint
201+ switch version {
202+ case PlutusV1 :
203+ langKey = 0
204+ case PlutusV2 :
205+ langKey = 1
206+ case PlutusV3 :
207+ langKey = 2
208+ default :
209+ continue
210+ }
211+
212+ // Convert ordered parameters back to list format
213+ values := make ([]int64 , len (model .Order ))
214+ for i , name := range model .Order {
215+ values [i ] = model .Parameters [name ]
216+ }
217+ legacyModels [langKey ] = values
218+ }
219+
220+ return legacyModels
221+ }
222+
132223type AlonzoProtocolParameterUpdate struct {
133224 mary.MaryProtocolParameterUpdate
134225 MinPoolCost * uint64 `cbor:"16,keyasint"`
@@ -147,33 +238,63 @@ func (u *AlonzoProtocolParameterUpdate) UnmarshalCBOR(data []byte) error {
147238}
148239
149240func (p * AlonzoProtocolParameters ) Utxorpc () * cardano.PParams {
150- // sanity check
151- if p .A0 .Num ().Int64 () > math .MaxInt32 ||
152- p .A0 .Denom ().Int64 () < 0 ||
153- p .A0 .Denom ().Int64 () > math .MaxUint32 {
241+ if p == nil {
154242 return nil
155243 }
156- if p .Rho .Num ().Int64 () > math .MaxInt32 ||
157- p .Rho .Denom ().Int64 () < 0 ||
158- p .Rho .Denom ().Int64 () > math .MaxUint32 {
159- return nil
244+
245+ // Helper function to safely check rational number bounds
246+ safeRatCheck := func (rat * cbor.Rat ) bool {
247+ if rat == nil || rat .Rat == nil {
248+ return false
249+ }
250+ num := rat .Num ().Int64 ()
251+ denom := rat .Denom ().Int64 ()
252+ return num <= math .MaxInt32 && denom > 0 && denom <= math .MaxUint32
160253 }
161- if p . Tau . Num (). Int64 () > math . MaxInt32 ||
162- p . Tau . Denom (). Int64 () < 0 ||
163- p . Tau . Denom (). Int64 () > math . MaxUint32 {
254+
255+ // Validate all rational numbers
256+ if ! safeRatCheck ( p . A0 ) || ! safeRatCheck ( p . Rho ) || ! safeRatCheck ( p . Tau ) {
164257 return nil
165258 }
166- if p .ExecutionCosts .MemPrice .Num ().Int64 () > math .MaxInt32 ||
167- p .ExecutionCosts .MemPrice .Denom ().Int64 () < 0 ||
168- p .ExecutionCosts .MemPrice .Denom ().Int64 () > math .MaxUint32 {
259+ if p .ExecutionCosts .MemPrice == nil || p .ExecutionCosts .StepPrice == nil ||
260+ ! safeRatCheck (p .ExecutionCosts .MemPrice ) || ! safeRatCheck (p .ExecutionCosts .StepPrice ) {
169261 return nil
170262 }
171- if p .ExecutionCosts .StepPrice .Num ().Int64 () > math .MaxInt32 ||
172- p .ExecutionCosts .StepPrice .Denom ().Int64 () < 0 ||
173- p .ExecutionCosts .StepPrice .Denom ().Int64 () > math .MaxUint32 {
174- return nil
263+
264+ // Convert cost models with proper version handling
265+ costModels := & cardano.CostModels {}
266+ if p .CostModels != nil {
267+ // Initialize all Plutus versions to empty models first
268+ costModels .PlutusV1 = & cardano.CostModel {Values : []int64 {}}
269+ costModels .PlutusV2 = & cardano.CostModel {Values : []int64 {}}
270+ costModels .PlutusV3 = & cardano.CostModel {Values : []int64 {}}
271+
272+ // Convert each version that exists in our parameters
273+ for version , model := range p .CostModels {
274+ var values []int64
275+ switch version {
276+ case PlutusV1 :
277+ values = make ([]int64 , len (model .Order ))
278+ for i , name := range model .Order {
279+ values [i ] = model .Parameters [name ]
280+ }
281+ costModels .PlutusV1 .Values = values
282+ case PlutusV2 :
283+ values = make ([]int64 , len (model .Order ))
284+ for i , name := range model .Order {
285+ values [i ] = model .Parameters [name ]
286+ }
287+ costModels .PlutusV2 .Values = values
288+ case PlutusV3 :
289+ values = make ([]int64 , len (model .Order ))
290+ for i , name := range model .Order {
291+ values [i ] = model .Parameters [name ]
292+ }
293+ costModels .PlutusV3 .Values = values
294+ }
295+ }
175296 }
176- // #nosec G115
297+
177298 return & cardano.PParams {
178299 CoinsPerUtxoByte : p .AdaPerUtxoByte ,
179300 MaxTxSize : uint64 (p .MaxTxSize ),
@@ -205,9 +326,7 @@ func (p *AlonzoProtocolParameters) Utxorpc() *cardano.PParams {
205326 MaxValueSize : uint64 (p .MaxValueSize ),
206327 CollateralPercentage : uint64 (p .CollateralPercentage ),
207328 MaxCollateralInputs : uint64 (p .MaxCollateralInputs ),
208- CostModels : common .ConvertToUtxorpcCardanoCostModels (
209- p .CostModels ,
210- ),
329+ CostModels : costModels ,
211330 Prices : & cardano.ExPrices {
212331 Memory : & cardano.RationalNumber {
213332 Numerator : int32 (p .ExecutionCosts .MemPrice .Num ().Int64 ()),
0 commit comments