Skip to content

Commit 0745027

Browse files
authored
feat: localstatequery stake-related queries (#557)
This adds support for the StakeDistribution, StakePools, and StakePoolParams queries. Fixes #553
1 parent b57bfc5 commit 0745027

File tree

4 files changed

+155
-41
lines changed

4 files changed

+155
-41
lines changed

cmd/gouroboros/query.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,40 @@ func testQuery(f *globalFlags) {
163163
os.Exit(1)
164164
}
165165
fmt.Printf("stake-distribution: %#v\n", *stakeDistribution)
166+
case "stake-pools":
167+
stakePools, err := o.LocalStateQuery().Client.GetStakePools()
168+
if err != nil {
169+
fmt.Printf("ERROR: failure querying stake pools: %s\n", err)
170+
os.Exit(1)
171+
}
172+
fmt.Printf("stake-pools: %#v\n", *stakePools)
166173
case "genesis-config":
167174
genesisConfig, err := o.LocalStateQuery().Client.GetGenesisConfig()
168175
if err != nil {
169176
fmt.Printf("ERROR: failure querying genesis config: %s\n", err)
170177
os.Exit(1)
171178
}
172179
fmt.Printf("genesis-config: %#v\n", *genesisConfig)
180+
case "pool-params":
181+
var tmpPools []ledger.PoolId
182+
if len(queryFlags.flagset.Args()) <= 1 {
183+
fmt.Println("No pools specified")
184+
os.Exit(1)
185+
}
186+
for _, pool := range queryFlags.flagset.Args()[1:] {
187+
tmpPoolId, err := ledger.NewPoolIdFromBech32(pool)
188+
if err != nil {
189+
fmt.Printf("Invalid bech32 pool ID %q: %s", pool, err)
190+
os.Exit(1)
191+
}
192+
tmpPools = append(tmpPools, tmpPoolId)
193+
}
194+
poolParams, err := o.LocalStateQuery().Client.GetStakePoolParams(tmpPools)
195+
if err != nil {
196+
fmt.Printf("ERROR: failure querying stake pool params: %s\n", err)
197+
os.Exit(1)
198+
}
199+
fmt.Printf("pool-params: %#v\n", *poolParams)
173200
case "utxos-by-address":
174201
var tmpAddrs []ledger.Address
175202
if len(queryFlags.flagset.Args()) <= 1 {

ledger/common.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,40 @@ func (a AssetFingerprint) String() string {
187187
return encoded
188188
}
189189

190+
type PoolId [28]byte
191+
192+
func NewPoolIdFromBech32(poolId string) (PoolId, error) {
193+
var p PoolId
194+
_, data, err := bech32.DecodeNoLimit(poolId)
195+
if err != nil {
196+
return p, err
197+
}
198+
decoded, err := bech32.ConvertBits(data, 5, 8, false)
199+
if err != nil {
200+
return p, err
201+
}
202+
if len(decoded) != len(p) {
203+
return p, fmt.Errorf("invalid pool ID length: %d", len(decoded))
204+
}
205+
p = PoolId(decoded)
206+
return p, err
207+
}
208+
209+
func (p PoolId) String() string {
210+
// Convert data to base32 and encode as bech32
211+
convData, err := bech32.ConvertBits(p[:], 8, 5, true)
212+
if err != nil {
213+
panic(
214+
fmt.Sprintf("unexpected error converting data to base32: %s", err),
215+
)
216+
}
217+
encoded, err := bech32.Encode("pool", convData)
218+
if err != nil {
219+
panic(fmt.Sprintf("unexpected error encoding data as bech32: %s", err))
220+
}
221+
return encoded
222+
}
223+
190224
const (
191225
AddressHeaderTypeMask = 0xF0
192226
AddressHeaderNetworkMask = 0x0F

protocol/localstatequery/client.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -590,12 +590,8 @@ func (c *Client) GetStakePools() (*StakePoolsResult, error) {
590590
return &result, nil
591591
}
592592

593-
// TODO
594-
/*
595-
query [17 #6.258([*poolid])
596-
*/
597593
func (c *Client) GetStakePoolParams(
598-
poolIds []interface{},
594+
poolIds []ledger.PoolId,
599595
) (*StakePoolParamsResult, error) {
600596
c.busyMutex.Lock()
601597
defer c.busyMutex.Unlock()
@@ -606,7 +602,10 @@ func (c *Client) GetStakePoolParams(
606602
query := buildShelleyQuery(
607603
currentEra,
608604
QueryTypeShelleyStakePoolParams,
609-
// TODO: add params
605+
cbor.Tag{
606+
Number: cbor.CborTagSet,
607+
Content: poolIds,
608+
},
610609
)
611610
var result StakePoolParamsResult
612611
if err := c.runQuery(query, &result); err != nil {

protocol/localstatequery/queries.go

Lines changed: 89 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package localstatequery
1616

1717
import (
1818
"fmt"
19+
"net"
1920

2021
"github.com/blinklabs-io/gouroboros/cbor"
2122
"github.com/blinklabs-io/gouroboros/ledger"
@@ -156,11 +157,14 @@ type CurrentProtocolParamsResult interface {
156157
// TODO
157158
type ProposedProtocolParamsUpdatesResult interface{}
158159

159-
// TODO
160-
/*
161-
result [{ *poolid => [[num den] vrf-hash]}] num/den is the quotient representing the stake fractions
162-
*/
163-
type StakeDistributionResult interface{}
160+
type StakeDistributionResult struct {
161+
cbor.StructAsArray
162+
Results map[ledger.PoolId]struct {
163+
cbor.StructAsArray
164+
StakeFraction *cbor.Rat
165+
VrfHash ledger.Blake2b256
166+
}
167+
}
164168

165169
type UTxOByAddressResult struct {
166170
cbor.StructAsArray
@@ -295,37 +299,87 @@ type UTxOByTxInResult struct {
295299
Results map[UtxoId]ledger.BabbageTransactionOutput
296300
}
297301

298-
// TODO
299-
/*
300-
result [#6.258([ *poolid ])]
301-
*/
302-
type StakePoolsResult interface{}
302+
type StakePoolsResult struct {
303+
cbor.StructAsArray
304+
Results []ledger.PoolId
305+
}
303306

304-
// TODO
305-
/*
306-
result [{ *poolid => [ *pool_param ] }]
307-
pool_param CDDL Comment
308-
operator keyhash
309-
vrf_keyhash keyhash
310-
pledge coin
311-
margin #6.30([num den])
312-
reward_account
313-
pool_owners set<addr_keyhash>
314-
relays [ *relay ]
315-
pool_metadata pool_metadata/null
316-
relay CDDL Comment
317-
single_host_addr [0 port/null ipv4/null ipv6/null]
318-
single_host_name [1 port/null dns_name] An A or AAAA DNS
319-
multi_host_name [2 dns_name] A SRV DNS record
320-
Type CDDL Comment
321-
port uint .le 65535
322-
ipv4 bytes .size 4
323-
ipv6 bytes .size 16
324-
dns_name tstr .size (0..64)
325-
pool_metadata [url metadata_hash]
326-
url tstr .size (0..64)
327-
*/
328-
type StakePoolParamsResult interface{}
307+
type StakePoolParamsResult struct {
308+
cbor.StructAsArray
309+
Results map[ledger.PoolId]struct {
310+
cbor.StructAsArray
311+
Operator ledger.Blake2b224
312+
VrfKeyHash ledger.Blake2b256
313+
Pledge uint
314+
FixedCost uint
315+
Margin *cbor.Rat
316+
RewardAccount ledger.Address
317+
PoolOwners []ledger.Blake2b224
318+
Relays []StakePoolParamsResultRelay
319+
PoolMetadata *struct {
320+
cbor.StructAsArray
321+
Url string
322+
MetadataHash ledger.Blake2b256
323+
}
324+
}
325+
}
326+
327+
type StakePoolParamsResultRelay struct {
328+
Type int
329+
Port *uint
330+
Ipv4 *net.IP
331+
Ipv6 *net.IP
332+
Hostname *string
333+
}
334+
335+
func (s *StakePoolParamsResultRelay) UnmarshalCBOR(data []byte) error {
336+
tmpId, err := cbor.DecodeIdFromList(data)
337+
if err != nil {
338+
return err
339+
}
340+
s.Type = tmpId
341+
switch tmpId {
342+
case 0:
343+
var tmpData struct {
344+
cbor.StructAsArray
345+
Type uint
346+
Port *uint
347+
Ipv4 *net.IP
348+
Ipv6 *net.IP
349+
}
350+
if _, err := cbor.Decode(data, &tmpData); err != nil {
351+
return err
352+
}
353+
s.Port = tmpData.Port
354+
s.Ipv4 = tmpData.Ipv4
355+
s.Ipv6 = tmpData.Ipv6
356+
case 1:
357+
var tmpData struct {
358+
cbor.StructAsArray
359+
Type uint
360+
Port *uint
361+
Hostname *string
362+
}
363+
if _, err := cbor.Decode(data, &tmpData); err != nil {
364+
return err
365+
}
366+
s.Port = tmpData.Port
367+
s.Hostname = tmpData.Hostname
368+
case 2:
369+
var tmpData struct {
370+
cbor.StructAsArray
371+
Type uint
372+
Hostname *string
373+
}
374+
if _, err := cbor.Decode(data, &tmpData); err != nil {
375+
return err
376+
}
377+
s.Hostname = tmpData.Hostname
378+
default:
379+
return fmt.Errorf("invalid relay type: %d", tmpId)
380+
}
381+
return nil
382+
}
329383

330384
// TODO
331385
type RewardInfoPoolsResult interface{}

0 commit comments

Comments
 (0)