Skip to content

Commit c44fbd3

Browse files
author
Jenita
committed
feat: created genesis pools from shelly genesis
Signed-off-by: Jenita <[email protected]>
1 parent 4f59553 commit c44fbd3

File tree

3 files changed

+340
-199
lines changed

3 files changed

+340
-199
lines changed

ledger/common/certs.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
package common
1616

1717
import (
18+
"encoding/hex"
19+
"encoding/json"
1820
"errors"
1921
"fmt"
22+
"math/big"
2023
"net"
2124

2225
"github.com/blinklabs-io/gouroboros/cbor"
@@ -387,6 +390,58 @@ type PoolRegistrationCertificate struct {
387390
PoolMetadata *PoolMetadata
388391
}
389392

393+
func (p *PoolRegistrationCertificate) UnmarshalJSON(data []byte) error {
394+
type Alias PoolRegistrationCertificate
395+
aux := &struct {
396+
Margin interface{} `json:"margin"`
397+
RewardAccount struct {
398+
Credential struct {
399+
KeyHash string `json:"key hash"`
400+
} `json:"credential"`
401+
Network string `json:"network"`
402+
} `json:"rewardAccount"`
403+
*Alias
404+
}{
405+
Alias: (*Alias)(p),
406+
}
407+
408+
if err := json.Unmarshal(data, &aux); err != nil {
409+
return err
410+
}
411+
412+
// Handle margin field
413+
switch v := aux.Margin.(type) {
414+
case float64:
415+
p.Margin.Rat = new(big.Rat).SetFloat64(v)
416+
case []interface{}:
417+
if len(v) == 2 {
418+
if num, ok := v[0].(float64); ok {
419+
if den, ok := v[1].(float64); ok {
420+
p.Margin.Rat = new(big.Rat).SetFrac64(int64(num), int64(den))
421+
}
422+
}
423+
}
424+
default:
425+
p.Margin.Rat = new(big.Rat).SetInt64(0)
426+
}
427+
428+
// Handle reward account
429+
if aux.RewardAccount.Credential.KeyHash != "" {
430+
hashBytes, err := hex.DecodeString(aux.RewardAccount.Credential.KeyHash)
431+
if err != nil {
432+
return fmt.Errorf("failed to decode reward account key hash: %w", err)
433+
}
434+
if len(hashBytes) != 28 {
435+
return fmt.Errorf("invalid key hash length: expected 28, got %d", len(hashBytes))
436+
}
437+
var hash Blake2b224
438+
copy(hash[:], hashBytes)
439+
p.RewardAccount = hash
440+
}
441+
442+
return nil
443+
}
444+
390445
func (c PoolRegistrationCertificate) isCertificate() {}
391446

392447
func (c *PoolRegistrationCertificate) UnmarshalCBOR(cborData []byte) error {

ledger/shelley/genesis.go

Lines changed: 170 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ import (
1818
"encoding/hex"
1919
"encoding/json"
2020
"errors"
21+
"fmt"
2122
"io"
2223
"math/big"
2324
"os"
25+
"reflect"
2426
"time"
2527

2628
"github.com/blinklabs-io/gouroboros/cbor"
@@ -47,36 +49,8 @@ type ShelleyGenesis struct {
4749
}
4850

4951
type GenesisStaking struct {
50-
Pools map[string]GenesisPool `json:"pools"`
51-
Stake map[string]string `json:"stake"`
52-
}
53-
54-
type GenesisPool struct {
55-
Cost int64 `json:"cost"`
56-
Margin float64 `json:"margin"`
57-
Metadata interface{} `json:"metadata"`
58-
Owners []string `json:"owners"`
59-
Pledge int64 `json:"pledge"`
60-
PublicKey string `json:"publicKey"`
61-
Relays []GenesisRelay `json:"relays"`
62-
RewardAccount GenesisReward `json:"rewardAccount"`
63-
Vrf string `json:"vrf"`
64-
}
65-
66-
type GenesisRelay struct {
67-
SingleHostName *SingleHostName `json:"single host name,omitempty"`
68-
}
69-
70-
type SingleHostName struct {
71-
DNSName string `json:"dnsName"`
72-
Port int `json:"port"`
73-
}
74-
75-
type GenesisReward struct {
76-
Credential struct {
77-
KeyHash string `json:"key hash"`
78-
} `json:"credential"`
79-
Network string `json:"network"`
52+
Pools map[string]common.PoolRegistrationCertificate `json:"pools"`
53+
Stake map[string]string `json:"stake"`
8054
}
8155

8256
func (g ShelleyGenesis) MarshalCBOR() ([]byte, error) {
@@ -107,27 +81,21 @@ func (g ShelleyGenesis) MarshalCBOR() ([]byte, error) {
10781
if err != nil {
10882
return nil, err
10983
}
110-
vrfBytes, err := hex.DecodeString(pool.Vrf)
111-
if err != nil {
112-
return nil, err
113-
}
114-
rewardAccountBytes, err := hex.DecodeString(pool.RewardAccount.Credential.KeyHash)
115-
if err != nil {
116-
return nil, err
117-
}
84+
vrfBytes := pool.VrfKeyHash.Bytes()
85+
rewardAccountBytes := pool.RewardAccount.Bytes()
11886
cborPools[cbor.NewByteString(poolIdBytes)] = []any{
11987
pool.Cost,
12088
pool.Margin,
12189
pool.Pledge,
122-
pool.PublicKey,
90+
pool.Operator.Bytes(),
12391
[]any{
12492
[]byte{0},
12593
rewardAccountBytes,
12694
},
127-
pool.Owners,
128-
pool.Relays,
95+
convertAddrKeyHashesToBytes(pool.PoolOwners),
96+
convertPoolRelays(pool.Relays),
12997
vrfBytes,
130-
pool.Metadata,
98+
pool.PoolMetadata,
13199
}
132100
}
133101

@@ -176,6 +144,67 @@ func (g ShelleyGenesis) MarshalCBOR() ([]byte, error) {
176144
return cbor.Encode(tmpData)
177145
}
178146

147+
func convertAddrKeyHashesToBytes(hashes []common.AddrKeyHash) [][]byte {
148+
result := make([][]byte, len(hashes))
149+
for i, h := range hashes {
150+
result[i] = h.Bytes()
151+
}
152+
return result
153+
}
154+
155+
func convertPoolRelays(relays []common.PoolRelay) []any {
156+
result := make([]any, len(relays))
157+
for i, relay := range relays {
158+
switch relay.Type {
159+
case 0: // SingleHostAddr
160+
var ipv4, ipv6 []byte
161+
var port uint32
162+
if relay.Ipv4 != nil {
163+
ipv4 = relay.Ipv4.To4()
164+
}
165+
if relay.Ipv6 != nil {
166+
ipv6 = relay.Ipv6.To16()
167+
}
168+
if relay.Port != nil {
169+
port = *relay.Port
170+
}
171+
result[i] = map[string]any{
172+
"single host addr": []any{
173+
ipv4,
174+
ipv6,
175+
port,
176+
},
177+
}
178+
case 1: // SingleHostName
179+
var hostname string
180+
var port uint32
181+
if relay.Hostname != nil {
182+
hostname = *relay.Hostname
183+
}
184+
if relay.Port != nil {
185+
port = *relay.Port
186+
}
187+
result[i] = map[string]any{
188+
"single host name": []any{
189+
hostname,
190+
port,
191+
},
192+
}
193+
case 2: // MultiHostName
194+
var hostname string
195+
if relay.Hostname != nil {
196+
hostname = *relay.Hostname
197+
}
198+
result[i] = map[string]any{
199+
"multi host name": hostname,
200+
}
201+
default:
202+
result[i] = nil
203+
}
204+
}
205+
return result
206+
}
207+
179208
func (g *ShelleyGenesis) GenesisUtxos() ([]common.Utxo, error) {
180209
ret := []common.Utxo{}
181210
for address, amount := range g.InitialFunds {
@@ -204,30 +233,119 @@ func (g *ShelleyGenesis) GenesisUtxos() ([]common.Utxo, error) {
204233
return ret, nil
205234
}
206235

207-
// GetInitialPools returns all initial stake pools with their delegators
208-
func (g *ShelleyGenesis) GetInitialPools() (map[string]GenesisPool, map[string][]string, error) {
209-
poolStake := make(map[string][]string)
236+
// InitialPools returns all pools and their delegators from the genesis data
237+
func (g *ShelleyGenesis) InitialPools() (map[string]common.PoolRegistrationCertificate, map[string][]common.Address, error) {
238+
239+
pools := make(map[string]common.PoolRegistrationCertificate)
240+
poolStake := make(map[string][]common.Address)
241+
242+
// Check for empty staking data
243+
if reflect.DeepEqual(g.Staking, GenesisStaking{}) {
244+
return pools, poolStake, nil
245+
}
246+
247+
// Process stake delegations first
210248
for stakeAddr, poolId := range g.Staking.Stake {
211-
poolStake[poolId] = append(poolStake[poolId], stakeAddr)
249+
// Validate stake address format
250+
if len(stakeAddr) != 56 {
251+
return nil, nil, fmt.Errorf("invalid stake address length: %d (expected 56 hex chars)", len(stakeAddr))
252+
}
253+
254+
stakeKeyBytes, err := hex.DecodeString(stakeAddr)
255+
if err != nil {
256+
return nil, nil, fmt.Errorf("failed to decode stake key %s: %w", stakeAddr, err)
257+
}
258+
259+
// Create stake address
260+
stakeAddrBytes := append([]byte{0xE1}, stakeKeyBytes...)
261+
addr, err := common.NewAddressFromBytes(stakeAddrBytes)
262+
if err != nil {
263+
return nil, nil, fmt.Errorf("failed to create stake address: %w", err)
264+
}
265+
266+
poolStake[poolId] = append(poolStake[poolId], addr)
267+
}
268+
269+
// Process pools
270+
for poolId, pool := range g.Staking.Pools {
271+
// Validate pool ID format
272+
if len(poolId) != 56 {
273+
return nil, nil, fmt.Errorf("invalid pool ID length: %d (expected 56 hex chars)", len(poolId))
274+
}
275+
276+
operatorBytes, err := hex.DecodeString(poolId)
277+
if err != nil {
278+
return nil, nil, fmt.Errorf("failed to decode pool operator key: %w", err)
279+
}
280+
281+
pools[poolId] = common.PoolRegistrationCertificate{
282+
Operator: common.Blake2b224(operatorBytes),
283+
VrfKeyHash: pool.VrfKeyHash,
284+
Pledge: pool.Pledge,
285+
Cost: pool.Cost,
286+
Margin: pool.Margin,
287+
RewardAccount: pool.RewardAccount,
288+
PoolOwners: pool.PoolOwners,
289+
Relays: pool.Relays,
290+
PoolMetadata: pool.PoolMetadata,
291+
}
212292
}
213-
return g.Staking.Pools, poolStake, nil
293+
294+
return pools, poolStake, nil
214295
}
215296

216-
// GetPoolById returns a specific pool by its ID along with its delegators
217-
func (g *ShelleyGenesis) GetPoolById(poolId string) (*GenesisPool, []string, error) {
297+
// PoolById returns a specific pool by its ID along with its delegators
298+
func (g *ShelleyGenesis) PoolById(poolId string) (*common.PoolRegistrationCertificate, []common.Address, error) {
299+
// Validate input
300+
if len(poolId) != 56 {
301+
return nil, nil, errors.New("invalid pool ID length")
302+
}
303+
218304
pool, exists := g.Staking.Pools[poolId]
219305
if !exists {
220306
return nil, nil, errors.New("pool not found")
221307
}
222308

223-
var delegators []string
309+
// Decode pool operator key
310+
operatorBytes, err := hex.DecodeString(poolId)
311+
if err != nil {
312+
return nil, nil, errors.New("failed to decode pool operator key")
313+
}
314+
315+
var delegators []common.Address
224316
for stakeAddr, pId := range g.Staking.Stake {
225317
if pId == poolId {
226-
delegators = append(delegators, stakeAddr)
318+
if len(stakeAddr) != 56 {
319+
return nil, nil, errors.New("invalid stake address length")
320+
}
321+
322+
stakeKeyBytes, err := hex.DecodeString(stakeAddr)
323+
if err != nil {
324+
return nil, nil, errors.New("failed to decode stake key")
325+
}
326+
327+
stakeAddrBytes := append([]byte{0xE1}, stakeKeyBytes...)
328+
addr, err := common.NewAddressFromBytes(stakeAddrBytes)
329+
if err != nil {
330+
return nil, nil, errors.New("failed to create stake address")
331+
}
332+
333+
delegators = append(delegators, addr)
227334
}
228335
}
229336

230-
return &pool, delegators, nil
337+
// Return pool with delegators
338+
return &common.PoolRegistrationCertificate{
339+
Operator: common.Blake2b224(operatorBytes),
340+
VrfKeyHash: pool.VrfKeyHash,
341+
Pledge: pool.Pledge,
342+
Cost: pool.Cost,
343+
Margin: pool.Margin,
344+
RewardAccount: pool.RewardAccount,
345+
PoolOwners: pool.PoolOwners,
346+
Relays: pool.Relays,
347+
PoolMetadata: pool.PoolMetadata,
348+
}, delegators, nil
231349
}
232350

233351
type ShelleyGenesisProtocolParams struct {

0 commit comments

Comments
 (0)