@@ -21,6 +21,7 @@ import (
2121
2222 "github.com/blinklabs-io/gouroboros/cbor"
2323 "github.com/blinklabs-io/gouroboros/ledger/common"
24+ "github.com/blinklabs-io/gouroboros/ledger/conway"
2425 "github.com/stretchr/testify/assert"
2526)
2627
@@ -109,6 +110,332 @@ func TestExtractGovActionId(t *testing.T) {
109110 })
110111}
111112
113+ func TestExtractTreasuryWithdrawalAction (t * testing.T ) {
114+ t .Run ("extracts withdrawals with multiple destinations" , func (t * testing.T ) {
115+ addr1 := common.Address {}
116+ addr2 := common.Address {}
117+
118+ action := & common.TreasuryWithdrawalGovAction {
119+ Type : 2 ,
120+ Withdrawals : map [* common.Address ]uint64 {
121+ & addr1 : 1000000 ,
122+ & addr2 : 2000000 ,
123+ },
124+ PolicyHash : []byte {0xab , 0xcd , 0xef },
125+ }
126+
127+ result := extractGovActionData (action )
128+
129+ assert .NotNil (t , result .TreasuryWithdrawal )
130+ assert .Len (t , result .TreasuryWithdrawal .Withdrawals , 2 )
131+ assert .Equal (t , "abcdef" , result .TreasuryWithdrawal .PolicyHash )
132+
133+ // Verify total amount matches (order may vary due to map iteration)
134+ var total uint64
135+ for _ , w := range result .TreasuryWithdrawal .Withdrawals {
136+ total += w .Amount
137+ }
138+ assert .Equal (t , uint64 (3000000 ), total )
139+ })
140+
141+ t .Run ("extracts withdrawals without policy hash" , func (t * testing.T ) {
142+ addr := common.Address {}
143+
144+ action := & common.TreasuryWithdrawalGovAction {
145+ Type : 2 ,
146+ Withdrawals : map [* common.Address ]uint64 {
147+ & addr : 5000000 ,
148+ },
149+ }
150+
151+ result := extractGovActionData (action )
152+
153+ assert .NotNil (t , result .TreasuryWithdrawal )
154+ assert .Len (t , result .TreasuryWithdrawal .Withdrawals , 1 )
155+ assert .Equal (t , "" , result .TreasuryWithdrawal .PolicyHash )
156+ })
157+ }
158+
159+ func TestExtractUpdateCommitteeAction (t * testing.T ) {
160+ t .Run ("extracts committee members to add and remove" , func (t * testing.T ) {
161+ // Create credentials with proper Blake2b224 hashes
162+ var credHash1 , credHash2 , credHash3 common.Blake2b224
163+ copy (credHash1 [:], []byte ("credential1hash12345678" ))
164+ copy (credHash2 [:], []byte ("credential2hash12345678" ))
165+ copy (credHash3 [:], []byte ("credential3hash12345678" ))
166+
167+ cred1 := common.Credential {
168+ CredType : common .CredentialTypeAddrKeyHash ,
169+ Credential : credHash1 ,
170+ }
171+ cred2 := & common.Credential {
172+ CredType : common .CredentialTypeAddrKeyHash ,
173+ Credential : credHash2 ,
174+ }
175+ cred3 := & common.Credential {
176+ CredType : common .CredentialTypeAddrKeyHash ,
177+ Credential : credHash3 ,
178+ }
179+
180+ action := & common.UpdateCommitteeGovAction {
181+ Type : 4 ,
182+ Credentials : []common.Credential {
183+ cred1 ,
184+ },
185+ CredEpochs : map [* common.Credential ]uint {
186+ cred2 : 500 ,
187+ cred3 : 600 ,
188+ },
189+ Quorum : cbor.Rat {Rat : big .NewRat (2 , 3 )},
190+ }
191+
192+ result := extractGovActionData (action )
193+
194+ assert .NotNil (t , result .UpdateCommittee )
195+ assert .Len (t , result .UpdateCommittee .MembersToRemove , 1 )
196+ assert .Len (t , result .UpdateCommittee .MembersToAdd , 2 )
197+ assert .Equal (t , uint64 (2 ), result .UpdateCommittee .QuorumNumerator )
198+ assert .Equal (t , uint64 (3 ), result .UpdateCommittee .QuorumDenominator )
199+ })
200+
201+ t .Run ("extracts with previous action ID" , func (t * testing.T ) {
202+ txId := [32 ]byte {}
203+ copy (txId [:], []byte ("committeeactiontxhash12345678" ))
204+
205+ action := & common.UpdateCommitteeGovAction {
206+ Type : 4 ,
207+ ActionId : & common.GovActionId {
208+ TransactionId : txId ,
209+ GovActionIdx : 3 ,
210+ },
211+ Quorum : cbor.Rat {Rat : big .NewRat (1 , 2 )},
212+ }
213+
214+ result := extractGovActionData (action )
215+
216+ assert .NotNil (t , result .UpdateCommittee )
217+ assert .NotNil (t , result .UpdateCommittee .PrevActionId )
218+ assert .Equal (t , hex .EncodeToString (txId [:]), result .UpdateCommittee .PrevActionId .TransactionId )
219+ assert .Equal (t , uint32 (3 ), result .UpdateCommittee .PrevActionId .GovActionIdx )
220+ })
221+ }
222+
223+ func TestExtractNewConstitutionAction (t * testing.T ) {
224+ t .Run ("extracts constitution with anchor" , func (t * testing.T ) {
225+ dataHash := [32 ]byte {}
226+ copy (dataHash [:], []byte ("constitutionhash123456789012" ))
227+
228+ action := & common.NewConstitutionGovAction {
229+ Type : 5 ,
230+ Constitution : struct {
231+ cbor.StructAsArray
232+ Anchor common.GovAnchor
233+ ScriptHash []byte
234+ }{
235+ Anchor : common.GovAnchor {
236+ Url : "https://example.com/constitution.json" ,
237+ DataHash : dataHash ,
238+ },
239+ },
240+ }
241+
242+ result := extractGovActionData (action )
243+
244+ assert .NotNil (t , result .NewConstitution )
245+ assert .Equal (t , "https://example.com/constitution.json" , result .NewConstitution .Anchor .Url )
246+ assert .Equal (t , hex .EncodeToString (dataHash [:]), result .NewConstitution .Anchor .DataHash )
247+ assert .Equal (t , "" , result .NewConstitution .ScriptHash )
248+ })
249+
250+ t .Run ("extracts constitution with script hash" , func (t * testing.T ) {
251+ dataHash := [32 ]byte {}
252+ copy (dataHash [:], []byte ("constitutionhash123456789012" ))
253+
254+ action := & common.NewConstitutionGovAction {
255+ Type : 5 ,
256+ Constitution : struct {
257+ cbor.StructAsArray
258+ Anchor common.GovAnchor
259+ ScriptHash []byte
260+ }{
261+ Anchor : common.GovAnchor {
262+ Url : "https://example.com/constitution.json" ,
263+ DataHash : dataHash ,
264+ },
265+ ScriptHash : []byte {0xde , 0xad , 0xbe , 0xef },
266+ },
267+ }
268+
269+ result := extractGovActionData (action )
270+
271+ assert .NotNil (t , result .NewConstitution )
272+ assert .Equal (t , "deadbeef" , result .NewConstitution .ScriptHash )
273+ })
274+
275+ t .Run ("extracts constitution with previous action ID" , func (t * testing.T ) {
276+ txId := [32 ]byte {}
277+ copy (txId [:], []byte ("prevconstitutionactiontx12345" ))
278+ dataHash := [32 ]byte {}
279+
280+ action := & common.NewConstitutionGovAction {
281+ Type : 5 ,
282+ ActionId : & common.GovActionId {
283+ TransactionId : txId ,
284+ GovActionIdx : 7 ,
285+ },
286+ Constitution : struct {
287+ cbor.StructAsArray
288+ Anchor common.GovAnchor
289+ ScriptHash []byte
290+ }{
291+ Anchor : common.GovAnchor {
292+ Url : "https://example.com/new-constitution.json" ,
293+ DataHash : dataHash ,
294+ },
295+ },
296+ }
297+
298+ result := extractGovActionData (action )
299+
300+ assert .NotNil (t , result .NewConstitution )
301+ assert .NotNil (t , result .NewConstitution .PrevActionId )
302+ assert .Equal (t , uint32 (7 ), result .NewConstitution .PrevActionId .GovActionIdx )
303+ })
304+ }
305+
306+ func TestExtractParameterChangeAction (t * testing.T ) {
307+ t .Run ("extracts parameter change with basic params" , func (t * testing.T ) {
308+ minFeeA := uint (44 )
309+ minFeeB := uint (155381 )
310+ maxTxSize := uint (16384 )
311+
312+ action := & conway.ConwayParameterChangeGovAction {
313+ Type : 0 ,
314+ ParamUpdate : conway.ConwayProtocolParameterUpdate {
315+ MinFeeA : & minFeeA ,
316+ MinFeeB : & minFeeB ,
317+ MaxTxSize : & maxTxSize ,
318+ },
319+ PolicyHash : []byte {0xca , 0xfe , 0xba , 0xbe },
320+ }
321+
322+ result := extractGovActionData (action )
323+
324+ assert .NotNil (t , result .ParameterChange )
325+ assert .Equal (t , "cafebabe" , result .ParameterChange .PolicyHash )
326+ assert .NotNil (t , result .ParameterChange .ParamUpdate )
327+ assert .NotNil (t , result .ParameterChange .ParamUpdate .MinFeeA )
328+ assert .Equal (t , uint (44 ), * result .ParameterChange .ParamUpdate .MinFeeA )
329+ assert .NotNil (t , result .ParameterChange .ParamUpdate .MinFeeB )
330+ assert .Equal (t , uint (155381 ), * result .ParameterChange .ParamUpdate .MinFeeB )
331+ assert .NotNil (t , result .ParameterChange .ParamUpdate .MaxTxSize )
332+ assert .Equal (t , uint (16384 ), * result .ParameterChange .ParamUpdate .MaxTxSize )
333+ })
334+
335+ t .Run ("extracts parameter change with previous action ID" , func (t * testing.T ) {
336+ txId := [32 ]byte {}
337+ copy (txId [:], []byte ("paramchangeactiontxhash12345" ))
338+ minFeeA := uint (50 )
339+
340+ action := & conway.ConwayParameterChangeGovAction {
341+ Type : 0 ,
342+ ActionId : & common.GovActionId {
343+ TransactionId : txId ,
344+ GovActionIdx : 2 ,
345+ },
346+ ParamUpdate : conway.ConwayProtocolParameterUpdate {
347+ MinFeeA : & minFeeA ,
348+ },
349+ }
350+
351+ result := extractGovActionData (action )
352+
353+ assert .NotNil (t , result .ParameterChange )
354+ assert .NotNil (t , result .ParameterChange .PrevActionId )
355+ assert .Equal (t , hex .EncodeToString (txId [:]), result .ParameterChange .PrevActionId .TransactionId )
356+ assert .Equal (t , uint32 (2 ), result .ParameterChange .PrevActionId .GovActionIdx )
357+ })
358+
359+ t .Run ("extracts parameter change with execution units" , func (t * testing.T ) {
360+ action := & conway.ConwayParameterChangeGovAction {
361+ Type : 0 ,
362+ ParamUpdate : conway.ConwayProtocolParameterUpdate {
363+ MaxTxExUnits : & common.ExUnits {
364+ Memory : 10000000 ,
365+ Steps : 10000000000 ,
366+ },
367+ MaxBlockExUnits : & common.ExUnits {
368+ Memory : 50000000 ,
369+ Steps : 40000000000 ,
370+ },
371+ },
372+ }
373+
374+ result := extractGovActionData (action )
375+
376+ assert .NotNil (t , result .ParameterChange )
377+ assert .NotNil (t , result .ParameterChange .ParamUpdate )
378+ assert .NotNil (t , result .ParameterChange .ParamUpdate .MaxTxExUnits )
379+ assert .Equal (t , int64 (10000000 ), result .ParameterChange .ParamUpdate .MaxTxExUnits .Mem )
380+ assert .Equal (t , int64 (10000000000 ), result .ParameterChange .ParamUpdate .MaxTxExUnits .Steps )
381+ assert .NotNil (t , result .ParameterChange .ParamUpdate .MaxBlockExUnits )
382+ assert .Equal (t , int64 (50000000 ), result .ParameterChange .ParamUpdate .MaxBlockExUnits .Mem )
383+ assert .Equal (t , int64 (40000000000 ), result .ParameterChange .ParamUpdate .MaxBlockExUnits .Steps )
384+ })
385+
386+ t .Run ("extracts parameter change with rational values" , func (t * testing.T ) {
387+ action := & conway.ConwayParameterChangeGovAction {
388+ Type : 0 ,
389+ ParamUpdate : conway.ConwayProtocolParameterUpdate {
390+ A0 : & cbor.Rat {Rat : big .NewRat (1 , 10 )}, // 0.1
391+ Rho : & cbor.Rat {Rat : big .NewRat (3 , 1000 )}, // 0.003
392+ Tau : & cbor.Rat {Rat : big .NewRat (2 , 10 )}, // 0.2
393+ },
394+ }
395+
396+ result := extractGovActionData (action )
397+
398+ assert .NotNil (t , result .ParameterChange )
399+ assert .NotNil (t , result .ParameterChange .ParamUpdate )
400+ assert .NotNil (t , result .ParameterChange .ParamUpdate .A0 )
401+ assert .InDelta (t , 0.1 , * result .ParameterChange .ParamUpdate .A0 , 0.0001 )
402+ assert .NotNil (t , result .ParameterChange .ParamUpdate .Rho )
403+ assert .InDelta (t , 0.003 , * result .ParameterChange .ParamUpdate .Rho , 0.0001 )
404+ assert .NotNil (t , result .ParameterChange .ParamUpdate .Tau )
405+ assert .InDelta (t , 0.2 , * result .ParameterChange .ParamUpdate .Tau , 0.0001 )
406+ })
407+
408+ t .Run ("extracts parameter change with governance parameters" , func (t * testing.T ) {
409+ minCommitteeSize := uint (5 )
410+ committeeTermLimit := uint64 (146 )
411+ govActionDeposit := uint64 (100000000000 )
412+ drepDeposit := uint64 (500000000 )
413+
414+ action := & conway.ConwayParameterChangeGovAction {
415+ Type : 0 ,
416+ ParamUpdate : conway.ConwayProtocolParameterUpdate {
417+ MinCommitteeSize : & minCommitteeSize ,
418+ CommitteeTermLimit : & committeeTermLimit ,
419+ GovActionDeposit : & govActionDeposit ,
420+ DRepDeposit : & drepDeposit ,
421+ },
422+ }
423+
424+ result := extractGovActionData (action )
425+
426+ assert .NotNil (t , result .ParameterChange )
427+ assert .NotNil (t , result .ParameterChange .ParamUpdate )
428+ assert .NotNil (t , result .ParameterChange .ParamUpdate .MinCommitteeSize )
429+ assert .Equal (t , uint (5 ), * result .ParameterChange .ParamUpdate .MinCommitteeSize )
430+ assert .NotNil (t , result .ParameterChange .ParamUpdate .CommitteeTermLimit )
431+ assert .Equal (t , uint64 (146 ), * result .ParameterChange .ParamUpdate .CommitteeTermLimit )
432+ assert .NotNil (t , result .ParameterChange .ParamUpdate .GovActionDeposit )
433+ assert .Equal (t , uint64 (100000000000 ), * result .ParameterChange .ParamUpdate .GovActionDeposit )
434+ assert .NotNil (t , result .ParameterChange .ParamUpdate .DRepDeposit )
435+ assert .Equal (t , uint64 (500000000 ), * result .ParameterChange .ParamUpdate .DRepDeposit )
436+ })
437+ }
438+
112439func TestRationalToFloat (t * testing.T ) {
113440 t .Run ("converts rational to float" , func (t * testing.T ) {
114441 // 1/2 = 0.5
0 commit comments