Skip to content

Commit f9101a6

Browse files
arepala-umlwolf31o2cubic-dev-ai[bot]
authored
feat(ledger): String method for Voter (#1287)
* feat(aws): Added string method for Voter by following CIP-129 specifications to generate bech32 encodings for each voter type Signed-off-by: Akhil Repala <[email protected]> * feat(ledger): Reused existing constants in voter bech32 encoding Signed-off-by: Akhil Repala <[email protected]> * chore: comment typo Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> Signed-off-by: Chris Gianelloni <[email protected]> --------- Signed-off-by: Akhil Repala <[email protected]> Signed-off-by: Chris Gianelloni <[email protected]> Co-authored-by: Chris Gianelloni <[email protected]> Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
1 parent 76174df commit f9101a6

File tree

2 files changed

+127
-0
lines changed

2 files changed

+127
-0
lines changed

ledger/common/gov.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
package common
1616

1717
import (
18+
"fmt"
1819
"math/big"
1920

2021
"github.com/blinklabs-io/gouroboros/cbor"
2122
"github.com/blinklabs-io/plutigo/data"
23+
"github.com/btcsuite/btcd/btcutil/bech32"
2224
)
2325

2426
// VotingProcedures is a convenience type to avoid needing to duplicate the full type definition everywhere
@@ -38,6 +40,69 @@ type Voter struct {
3840
Hash [28]byte
3941
}
4042

43+
func encodeCip129Voter(
44+
prefix string,
45+
keyType uint8,
46+
credentialType uint8,
47+
hash []byte,
48+
) string {
49+
// Header packs the 4-bit key type (per CIP-129) in the high nibble and the credential semantics in the low nibble.
50+
// Since CIP-129 reserves values 0 and 1, we offset the existing credential constants (0 = key hash, 1 = script hash) by 2
51+
// so the output nibble matches the spec's 0x2/0x3 tags.
52+
header := byte((keyType << 4) | ((credentialType + 2) & 0x0f))
53+
data := make([]byte, 1+len(hash))
54+
data[0] = header
55+
copy(data[1:], hash)
56+
convData, err := bech32.ConvertBits(data, 8, 5, true)
57+
if err != nil {
58+
panic(fmt.Sprintf("unexpected error converting voter data to base32: %s", err))
59+
}
60+
encoded, err := bech32.Encode(prefix, convData)
61+
if err != nil {
62+
panic(fmt.Sprintf("unexpected error encoding voter data as bech32: %s", err))
63+
}
64+
return encoded
65+
}
66+
67+
// Generates bech32-encoded identifier for the voter credential.
68+
func (v Voter) String() string {
69+
switch v.Type {
70+
case VoterTypeConstitutionalCommitteeHotKeyHash:
71+
return encodeCip129Voter(
72+
"cc_hot",
73+
VoterTypeConstitutionalCommitteeHotKeyHash,
74+
CredentialTypeAddrKeyHash,
75+
v.Hash[:],
76+
)
77+
case VoterTypeConstitutionalCommitteeHotScriptHash:
78+
return encodeCip129Voter(
79+
"cc_hot",
80+
VoterTypeConstitutionalCommitteeHotKeyHash,
81+
CredentialTypeScriptHash,
82+
v.Hash[:],
83+
)
84+
case VoterTypeDRepKeyHash:
85+
return encodeCip129Voter(
86+
"drep",
87+
VoterTypeDRepKeyHash,
88+
CredentialTypeAddrKeyHash,
89+
v.Hash[:],
90+
)
91+
case VoterTypeDRepScriptHash:
92+
return encodeCip129Voter(
93+
"drep",
94+
VoterTypeDRepKeyHash,
95+
CredentialTypeScriptHash,
96+
v.Hash[:],
97+
)
98+
case VoterTypeStakingPoolKeyHash:
99+
poolId := PoolId(v.Hash)
100+
return poolId.String()
101+
default:
102+
panic(fmt.Sprintf("unknown voter type: %d", v.Type))
103+
}
104+
}
105+
41106
func (v Voter) ToPlutusData() data.PlutusData {
42107
switch v.Type {
43108
case VoterTypeConstitutionalCommitteeHotScriptHash:

ledger/common/gov_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,68 @@ func TestVoterToPlutusData(t *testing.T) {
168168
}
169169
}
170170

171+
func TestVoterString(t *testing.T) {
172+
var zeroHash [28]byte
173+
var sequentialHash [28]byte
174+
for i := range sequentialHash {
175+
sequentialHash[i] = byte(i)
176+
}
177+
testCases := []struct {
178+
name string
179+
voter Voter
180+
want string
181+
}{
182+
{
183+
name: "CIP129CcHotKeyHash",
184+
voter: Voter{
185+
Type: VoterTypeConstitutionalCommitteeHotKeyHash,
186+
Hash: zeroHash,
187+
},
188+
want: "cc_hot1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqvcdjk7",
189+
},
190+
{
191+
name: "CIP129CcHotScriptHash",
192+
voter: Voter{
193+
Type: VoterTypeConstitutionalCommitteeHotScriptHash,
194+
Hash: zeroHash,
195+
},
196+
want: "cc_hot1qvqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqv2arke",
197+
},
198+
{
199+
name: "CIP129DRepKeyHash",
200+
voter: Voter{
201+
Type: VoterTypeDRepKeyHash,
202+
Hash: zeroHash,
203+
},
204+
want: "drep1ygqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq7vlc9n",
205+
},
206+
{
207+
name: "CIP129DRepScriptHash",
208+
voter: Voter{
209+
Type: VoterTypeDRepScriptHash,
210+
Hash: zeroHash,
211+
},
212+
want: "drep1yvqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq770f95",
213+
},
214+
{
215+
name: "StakingPoolKeyHash",
216+
voter: Voter{
217+
Type: VoterTypeStakingPoolKeyHash,
218+
Hash: sequentialHash,
219+
},
220+
want: "pool1qqqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk35lkuk",
221+
},
222+
}
223+
for _, tc := range testCases {
224+
t.Run(tc.name, func(t *testing.T) {
225+
assert.Equal(t, tc.want, tc.voter.String())
226+
})
227+
}
228+
assert.Panics(t, func() {
229+
_ = Voter{Type: 99}.String()
230+
})
231+
}
232+
171233
// Tests the ToPlutusData method for Vote types
172234
func TestVoteToPlutusData(t *testing.T) {
173235
testCases := []struct {

0 commit comments

Comments
 (0)