Skip to content

Commit 9be8367

Browse files
authored
feat: support for voting in script context (#1173)
Signed-off-by: Aurora Gaffney <[email protected]>
1 parent c8cb60b commit 9be8367

File tree

5 files changed

+104
-15
lines changed

5 files changed

+104
-15
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ toolchain go1.24.1
77
require (
88
filippo.io/edwards25519 v1.1.0
99
github.com/blinklabs-io/ouroboros-mock v0.3.8
10-
github.com/blinklabs-io/plutigo v0.0.8
10+
github.com/blinklabs-io/plutigo v0.0.9
1111
github.com/btcsuite/btcd/btcutil v1.1.6
1212
github.com/fxamacker/cbor/v2 v2.9.0
1313
github.com/jinzhu/copier v0.4.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3M
55
github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
66
github.com/blinklabs-io/ouroboros-mock v0.3.8 h1:+DAt2rx0ouZUxee5DBMgZq3I1+ZdxFSHG9g3tYl/FKU=
77
github.com/blinklabs-io/ouroboros-mock v0.3.8/go.mod h1:UwQIf4KqZwO13P9d90fbi3UL/X7JaJfeEbqk+bEeFQA=
8-
github.com/blinklabs-io/plutigo v0.0.8 h1:p0agbMDZ00skO1yx3Mg0S55phz/mCo8nYuixOaAAGJs=
9-
github.com/blinklabs-io/plutigo v0.0.8/go.mod h1:L639Q8i2cSRuBhjgCHttPR0nnYwwsYVT4Btz7KpQjSw=
8+
github.com/blinklabs-io/plutigo v0.0.9 h1:GPMJNfaiT6/wYs8MGZUfJYQ8tSHT9y+dpdvFprPOYYg=
9+
github.com/blinklabs-io/plutigo v0.0.9/go.mod h1:L639Q8i2cSRuBhjgCHttPR0nnYwwsYVT4Btz7KpQjSw=
1010
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
1111
github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
1212
github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A=

ledger/common/script/context.go

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ type TxInfoV3 struct {
130130
Redeemers KeyValuePairs[ScriptInfo, Redeemer]
131131
Data KeyValuePairs[lcommon.Blake2b256, data.PlutusData]
132132
Id lcommon.Blake2b256
133-
Votes KeyValuePairs[lcommon.Voter, KeyValuePairs[lcommon.GovActionId, lcommon.VotingProcedure]]
133+
Votes KeyValuePairs[*lcommon.Voter, KeyValuePairs[*lcommon.GovActionId, lcommon.VotingProcedure]]
134134
ProposalProcedures []lcommon.ProposalProcedure
135135
CurrentTreasuryAmount Option[Coin]
136136
TreasuryDonation Option[PositiveCoin]
@@ -160,8 +160,7 @@ func (t TxInfoV3) ToPlutusData() data.PlutusData {
160160
tmpRedeemers.ToPlutusData(),
161161
t.Data.ToPlutusData(),
162162
data.NewByteString(t.Id.Bytes()),
163-
// TODO: votes
164-
data.NewMap([][2]data.PlutusData{}),
163+
t.Votes.ToPlutusData(),
165164
// TODO: proposal procedures
166165
toPlutusData([]any{}),
167166
t.CurrentTreasuryAmount.ToPlutusData(),
@@ -188,6 +187,7 @@ func NewTxInfoV3FromTransaction(
188187
}
189188
inputs := sortInputs(tx.Inputs())
190189
withdrawals := withdrawalsInfo(tx.Withdrawals())
190+
votes := votingInfo(tx.VotingProcedures())
191191
redeemers := redeemersInfo(
192192
tx.Witnesses(),
193193
scriptPurposeBuilder(
@@ -197,7 +197,7 @@ func NewTxInfoV3FromTransaction(
197197
tx.Certificates(),
198198
withdrawals,
199199
// TODO: proposal procedures
200-
// TODO: votes
200+
votes,
201201
),
202202
)
203203
tmpData := dataInfo(tx.Witnesses())
@@ -217,7 +217,7 @@ func NewTxInfoV3FromTransaction(
217217
Redeemers: redeemers,
218218
Data: tmpData,
219219
Id: tx.Hash(),
220-
// TODO: Votes
220+
Votes: votes,
221221
// TODO: ProposalProcedures
222222
}
223223
if amt := tx.CurrentTreasuryValue(); amt > 0 {
@@ -454,6 +454,80 @@ func signatoriesInfo(
454454
return tmp
455455
}
456456

457+
func votingInfo(
458+
votingProcedures lcommon.VotingProcedures,
459+
) KeyValuePairs[*lcommon.Voter, KeyValuePairs[*lcommon.GovActionId, lcommon.VotingProcedure]] {
460+
var ret KeyValuePairs[*lcommon.Voter, KeyValuePairs[*lcommon.GovActionId, lcommon.VotingProcedure]]
461+
for voter, voterData := range votingProcedures {
462+
voterPairs := make(KeyValuePairs[*lcommon.GovActionId, lcommon.VotingProcedure], 0, len(votingProcedures))
463+
for govActionId, votingProcedure := range voterData {
464+
voterPairs = append(
465+
voterPairs,
466+
KeyValuePair[*lcommon.GovActionId, lcommon.VotingProcedure]{
467+
Key: govActionId,
468+
Value: votingProcedure,
469+
},
470+
)
471+
}
472+
// Sort voter pairs by gov action ID
473+
slices.SortFunc(
474+
voterPairs,
475+
func(a, b KeyValuePair[*lcommon.GovActionId, lcommon.VotingProcedure]) int {
476+
// Compare TX ID
477+
x := bytes.Compare(a.Key.TransactionId[:], b.Key.TransactionId[:])
478+
if x != 0 {
479+
return x
480+
}
481+
// Compare index
482+
if a.Key.GovActionIdx < b.Key.GovActionIdx {
483+
return -1
484+
} else if a.Key.GovActionIdx > b.Key.GovActionIdx {
485+
return 1
486+
}
487+
return 0
488+
},
489+
)
490+
ret = append(
491+
ret,
492+
KeyValuePair[*lcommon.Voter, KeyValuePairs[*lcommon.GovActionId, lcommon.VotingProcedure]]{
493+
Key: voter,
494+
Value: voterPairs,
495+
},
496+
)
497+
}
498+
// Sort by voter ID
499+
slices.SortFunc(
500+
ret,
501+
func(a, b KeyValuePair[*lcommon.Voter, KeyValuePairs[*lcommon.GovActionId, lcommon.VotingProcedure]]) int {
502+
voterTag := func(v *lcommon.Voter) int {
503+
switch v.Type {
504+
case lcommon.VoterTypeConstitutionalCommitteeHotScriptHash:
505+
return 0
506+
case lcommon.VoterTypeConstitutionalCommitteeHotKeyHash:
507+
return 1
508+
case lcommon.VoterTypeDRepScriptHash:
509+
return 2
510+
case lcommon.VoterTypeDRepKeyHash:
511+
return 3
512+
case lcommon.VoterTypeStakingPoolKeyHash:
513+
return 4
514+
}
515+
return -1
516+
}
517+
tagA := voterTag(a.Key)
518+
tagB := voterTag(b.Key)
519+
if tagA == tagB {
520+
return bytes.Compare(a.Key.Hash[:], b.Key.Hash[:])
521+
}
522+
if tagA < tagB {
523+
return -1
524+
}
525+
return 1
526+
},
527+
)
528+
return ret
529+
}
530+
457531
func certificatesToPlutusData(
458532
certificates []lcommon.Certificate,
459533
) data.PlutusData {

ledger/common/script/context_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,18 @@ var scriptContextV3TestDefs = []struct {
235235
slotState: preprodSlotState,
236236
expectedCbor: "d8799fd8799f9fd8799fd8799f5820000000000000000000000000000000000000000000000000000000000000000000ffd8799fd8799fd87a9f581c04036eecadc2f19e95f831b4bc08919cde1d1088d74602bd3dcd78a2ffd8799fd8799fd87a9f581c04036eecadc2f19e95f831b4bc08919cde1d1088d74602bd3dcd78a2ffffffffa140a1401a000f4240d87b9fd87980ffd8799f581c04036eecadc2f19e95f831b4bc08919cde1d1088d74602bd3dcd78a2ffffffff809fd8799fd8799fd8799f581c00000000000000000000000000000000000000000000000000000000ffd8799fd8799fd87a9f581c11111111111111111111111111111111111111111111111111111111ffffffffa140a1401a000f4240d87980d87a80ffd8799fd8799fd8799f581c00000000000000000000000000000000000000000000000000000000ffd8799fd87a9f1a00261ec3181b03ffffffa140a1401a000f4240d87980d87a80ffd8799fd8799fd87a9f581c11111111111111111111111111111111111111111111111111111111ffd8799fd87a9f1a00261ec3181b03ffffffa140a1401a000f4240d87980d87a80ffff182aa080a1d87a9f581c04036eecadc2f19e95f831b4bc08919cde1d1088d74602bd3dcd78a2ff00d8799fd8799fd87980d87a80ffd8799fd87a9f1b000001739c890420ffd87980ffff9f581c00000000000000000000000000000000000000000000000000000000ffa2d87a9fd8799f5820000000000000000000000000000000000000000000000000000000000000000000ffffd87a81d87980d87b9fd87a9f581c04036eecadc2f19e95f831b4bc08919cde1d1088d74602bd3dcd78a2ffffd87980a0582040bee3b25a585a854fe73f3448a6f6b417fe669c813a1881e665971f34a9e984a080d87a80d8799f01ffffd87980d87b9fd87a9f581c04036eecadc2f19e95f831b4bc08919cde1d1088d74602bd3dcd78a2ffffff",
237237
},
238+
{
239+
name: "Voting",
240+
txHex: "84a40081825820000000000000000000000000000000000000000000000000000000000000000000018002182a13a58200581c00000000000000000000000000000000000000000000000000000000a1825820999999999999999999999999999999999999999999999999999999999999999918988200827668747470733a2f2f61696b656e2d6c616e672e6f7267582000000000000000000000000000000000000000000000000000000000000000008202581c00000000000000000000000000000000000000000000000000000000a38258209999999999999999999999999999999999999999999999999999999999999999008202f68258208888888888888888888888888888888888888888888888888888888888888888018202f68258207777777777777777777777777777777777777777777777777777777777777777028202f68203581c43fa47afc68a7913fbe2f400e3849cb492d9a2610c85966de0f2ba1ea18258209999999999999999999999999999999999999999999999999999999999999999038200f68204581c00000000000000000000000000000000000000000000000000000000a18258209999999999999999999999999999999999999999999999999999999999999999048201f68201581c43fa47afc68a7913fbe2f400e3849cb492d9a2610c85966de0f2ba1ea18258209999999999999999999999999999999999999999999999999999999999999999018201f6a20582840402d87980821a000f42401a05f5e100840400d87981182a821a000f42401a05f5e1000781587d587b0101003232323232323225333333008001153330033370e900018029baa001153330073006375400224a66600894452615330054911856616c696461746f722072657475726e65642066616c73650013656002002002002002002153300249010b5f746d70303a20566f696400165734ae7155ceaab9e5573eae91f5f6",
241+
inputsHex: "81825820000000000000000000000000000000000000000000000000000000000000000000",
242+
outputsHex: "81a200581d6000000000000000000000000000000000000000000000000000000000011a000f4240",
243+
redeemerTag: lcommon.RedeemerTagVoting,
244+
redeemerIndex: 0,
245+
// NOTE: this is slightly different than what's produced by the Aiken tests
246+
// The Aiken test is "wrong" because the redeemer datum redefined in the test definition
247+
// uses indef-length encoding while the in-TX redeemer datum uses def-length encoding
248+
expectedCbor: "d8799fd8799f9fd8799fd8799f5820000000000000000000000000000000000000000000000000000000000000000000ffd8799fd8799fd8799f581c00000000000000000000000000000000000000000000000000000000ffd87a80ffa140a1401a000f4240d87980d87a80ffffff8080182aa080a0d8799fd8799fd87980d87a80ffd8799fd87b80d87a80ffff80a2d87d9fd8799fd87a9f581c43fa47afc68a7913fbe2f400e3849cb492d9a2610c85966de0f2ba1effffffd87981182ad87d9fd87a9fd87a9f581c43fa47afc68a7913fbe2f400e3849cb492d9a2610c85966de0f2ba1effffffd87980a05820e042ea3e95cf8156842e35639778441becaf3c862cb9459ee74b44aeeb88c673a5d8799fd87a9f581c43fa47afc68a7913fbe2f400e3849cb492d9a2610c85966de0f2ba1effffa1d8799f5820999999999999999999999999999999999999999999999999999999999999999901ffd87a80d8799fd8799f581c00000000000000000000000000000000000000000000000000000000ffffa1d8799f582099999999999999999999999999999999999999999999999999999999999999991898ffd87980d87a9fd87a9f581c43fa47afc68a7913fbe2f400e3849cb492d9a2610c85966de0f2ba1effffa1d8799f5820999999999999999999999999999999999999999999999999999999999999999903ffd87980d87a9fd8799f581c00000000000000000000000000000000000000000000000000000000ffffa3d8799f5820777777777777777777777777777777777777777777777777777777777777777702ffd87b80d8799f5820888888888888888888888888888888888888888888888888888888888888888801ffd87b80d8799f5820999999999999999999999999999999999999999999999999999999999999999900ffd87b80d87b9f581c00000000000000000000000000000000000000000000000000000000ffa1d8799f5820999999999999999999999999999999999999999999999999999999999999999904ffd87a8080d87a80d87a80ffd87981182ad87d9fd8799fd87a9f581c43fa47afc68a7913fbe2f400e3849cb492d9a2610c85966de0f2ba1effffffff",
249+
},
238250
}
239251

240252
func TestScriptContextV3(t *testing.T) {

ledger/common/script/purpose.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -154,17 +154,18 @@ type ScriptInfoVoting struct {
154154
func (ScriptInfoVoting) isScriptInfo() {}
155155

156156
func (s ScriptInfoVoting) ScriptHash() lcommon.ScriptHash {
157-
// TODO
158-
return lcommon.ScriptHash{}
157+
return lcommon.ScriptHash(s.Voter.Hash[:])
159158
}
160159

161160
func (s ScriptInfoVoting) ToPlutusData() data.PlutusData {
162-
// TODO
163-
return nil
161+
return data.NewConstr(
162+
4,
163+
s.Voter.ToPlutusData(),
164+
)
164165
}
165166

166167
type ScriptInfoProposing struct {
167-
Size uint64
168+
Index uint32
168169
ProposalProcedure lcommon.ProposalProcedure
169170
}
170171

@@ -189,8 +190,8 @@ func scriptPurposeBuilder(
189190
mint lcommon.MultiAsset[lcommon.MultiAssetTypeMint],
190191
certificates []lcommon.Certificate,
191192
withdrawals KeyValuePairs[*lcommon.Address, uint64],
193+
votes KeyValuePairs[*lcommon.Voter, KeyValuePairs[*lcommon.GovActionId, lcommon.VotingProcedure]],
192194
// TODO: proposal procedures
193-
// TODO: votes
194195
) toScriptPurposeFunc {
195196
return func(redeemerKey lcommon.RedeemerKey) ScriptInfo {
196197
// TODO: implement additional redeemer tags
@@ -236,7 +237,9 @@ func scriptPurposeBuilder(
236237
},
237238
}
238239
case lcommon.RedeemerTagVoting:
239-
return nil
240+
return ScriptInfoVoting{
241+
Voter: *(votes[redeemerKey.Index].Key),
242+
}
240243
case lcommon.RedeemerTagProposing:
241244
return nil
242245
}

0 commit comments

Comments
 (0)