15
15
package alonzo
16
16
17
17
import (
18
+ //"encoding/json"
19
+ "fmt"
18
20
"math"
19
21
"sort"
20
22
23
+ //"strconv"
24
+ "strings"
25
+
21
26
"github.com/blinklabs-io/gouroboros/cbor"
22
27
"github.com/blinklabs-io/gouroboros/ledger/common"
23
28
"github.com/blinklabs-io/gouroboros/ledger/mary"
24
29
cardano "github.com/utxorpc/go-codegen/utxorpc/v1alpha/cardano"
25
30
)
26
31
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
+
27
46
type AlonzoProtocolParameters struct {
28
47
mary.MaryProtocolParameters
29
48
MinPoolCost uint64
30
49
AdaPerUtxoByte uint64
31
- CostModels map [uint ][] int64
50
+ CostModels map [PlutusVersion ] * CostModel
32
51
ExecutionCosts common.ExUnitPrice
33
52
MaxTxExUnits common.ExUnits
34
53
MaxBlockExUnits common.ExUnits
@@ -50,7 +69,7 @@ func (p *AlonzoProtocolParameters) Update(
50
69
p .AdaPerUtxoByte = * paramUpdate .AdaPerUtxoByte
51
70
}
52
71
if paramUpdate .CostModels != nil {
53
- p .CostModels = paramUpdate .CostModels
72
+ p .convertLegacyCostModels ( paramUpdate .CostModels )
54
73
}
55
74
if paramUpdate .ExecutionCosts != nil {
56
75
p .ExecutionCosts = * paramUpdate .ExecutionCosts
@@ -72,10 +91,12 @@ func (p *AlonzoProtocolParameters) Update(
72
91
}
73
92
}
74
93
75
- func (p * AlonzoProtocolParameters ) UpdateFromGenesis (genesis * AlonzoGenesis ) {
94
+ func (p * AlonzoProtocolParameters ) UpdateFromGenesis (genesis * AlonzoGenesis ) error {
76
95
if genesis == nil {
77
- return
96
+ return nil
78
97
}
98
+
99
+ // Common parameter updates
79
100
p .AdaPerUtxoByte = genesis .LovelacePerUtxoWord / 8
80
101
p .MaxValueSize = genesis .MaxValueSize
81
102
p .CollateralPercentage = genesis .CollateralPercentage
@@ -88,47 +109,117 @@ func (p *AlonzoProtocolParameters) UpdateFromGenesis(genesis *AlonzoGenesis) {
88
109
Memory : uint64 (genesis .MaxBlockExUnits .Mem ),
89
110
Steps : uint64 (genesis .MaxBlockExUnits .Steps ),
90
111
}
91
- if genesis . ExecutionPrices . Mem != nil &&
92
- genesis .ExecutionPrices .Steps != nil {
112
+
113
+ if genesis . ExecutionPrices . Mem != nil && genesis .ExecutionPrices .Steps != nil {
93
114
p .ExecutionCosts = common.ExUnitPrice {
94
115
MemPrice : & cbor.Rat {Rat : genesis .ExecutionPrices .Mem .Rat },
95
116
StepPrice : & cbor.Rat {Rat : genesis .ExecutionPrices .Steps .Rat },
96
117
}
97
118
}
98
119
99
- // Process cost models
100
120
if genesis .CostModels != nil {
101
- p .CostModels = make (map [uint ][]int64 )
121
+ p .CostModels = make (map [PlutusVersion ]* CostModel )
122
+
102
123
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 {
111
126
continue
112
127
}
113
128
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 )
118
136
}
119
- sort .Strings (keys )
120
137
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 ,
125
145
}
146
+ }
147
+ }
148
+ return nil
149
+ }
150
+
151
+ func (p * AlonzoProtocolParameters ) convertLegacyCostModels (legacyModels map [uint ][]int64 ) {
152
+ p .CostModels = make (map [PlutusVersion ]* CostModel )
126
153
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 ,
128
179
}
129
180
}
130
181
}
131
182
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
+
132
223
type AlonzoProtocolParameterUpdate struct {
133
224
mary.MaryProtocolParameterUpdate
134
225
MinPoolCost * uint64 `cbor:"16,keyasint"`
@@ -147,33 +238,63 @@ func (u *AlonzoProtocolParameterUpdate) UnmarshalCBOR(data []byte) error {
147
238
}
148
239
149
240
func (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 {
154
242
return nil
155
243
}
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
160
253
}
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 ) {
164
257
return nil
165
258
}
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 ) {
169
261
return nil
170
262
}
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
+ }
175
296
}
176
- // #nosec G115
297
+
177
298
return & cardano.PParams {
178
299
CoinsPerUtxoByte : p .AdaPerUtxoByte ,
179
300
MaxTxSize : uint64 (p .MaxTxSize ),
@@ -205,9 +326,7 @@ func (p *AlonzoProtocolParameters) Utxorpc() *cardano.PParams {
205
326
MaxValueSize : uint64 (p .MaxValueSize ),
206
327
CollateralPercentage : uint64 (p .CollateralPercentage ),
207
328
MaxCollateralInputs : uint64 (p .MaxCollateralInputs ),
208
- CostModels : common .ConvertToUtxorpcCardanoCostModels (
209
- p .CostModels ,
210
- ),
329
+ CostModels : costModels ,
211
330
Prices : & cardano.ExPrices {
212
331
Memory : & cardano.RationalNumber {
213
332
Numerator : int32 (p .ExecutionCosts .MemPrice .Num ().Int64 ()),
0 commit comments