Skip to content

Commit 61a22c8

Browse files
author
Jenita
committed
feat: created genesis pools from shelly genesis
Signed-off-by: Jenita <[email protected]>
1 parent 2a5b082 commit 61a22c8

File tree

3 files changed

+121
-103
lines changed

3 files changed

+121
-103
lines changed

ledger/common/address.go

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -107,24 +107,47 @@ func NewAddressFromParts(
107107
paymentAddr []byte,
108108
stakingAddr []byte,
109109
) (Address, error) {
110+
// Validate network ID
111+
if networkId != AddressNetworkTestnet && networkId != AddressNetworkMainnet {
112+
return Address{}, errors.New("invalid network ID")
113+
}
114+
115+
// Handle stake-only addresses
116+
if addrType == AddressTypeNoneKey || addrType == AddressTypeNoneScript {
117+
if len(paymentAddr) > 0 {
118+
return Address{}, errors.New("payment address must be empty for stake-only addresses")
119+
}
120+
if len(stakingAddr) != AddressHashSize {
121+
return Address{}, fmt.Errorf("staking key must be exactly %d bytes", AddressHashSize)
122+
}
123+
124+
if addrType == AddressTypeNoneScript && networkId == AddressNetworkTestnet {
125+
header := byte(0xF1)
126+
addrBytes := append([]byte{header}, stakingAddr...)
127+
return NewAddressFromBytes(addrBytes)
128+
}
129+
130+
header := addrType<<4 | networkId
131+
addrBytes := append([]byte{header}, stakingAddr...)
132+
return NewAddressFromBytes(addrBytes)
133+
}
134+
135+
// Handle regular addresses
110136
if len(paymentAddr) != AddressHashSize {
111-
return Address{}, fmt.Errorf(
112-
"invalid payment address hash length: %d",
113-
len(paymentAddr),
114-
)
137+
return Address{}, fmt.Errorf("payment address must be exactly %d bytes", AddressHashSize)
115138
}
139+
116140
if len(stakingAddr) > 0 && len(stakingAddr) != AddressHashSize {
117-
return Address{}, fmt.Errorf(
118-
"invalid staking address hash length: %d",
119-
len(stakingAddr),
120-
)
141+
return Address{}, fmt.Errorf("staking address must be empty or exactly %d bytes", AddressHashSize)
121142
}
122-
return Address{
123-
addressType: addrType,
124-
networkId: networkId,
125-
paymentAddress: paymentAddr[:],
126-
stakingAddress: stakingAddr[:],
127-
}, nil
143+
144+
header := addrType<<4 | networkId
145+
addrBytes := append([]byte{header}, paymentAddr...)
146+
if len(stakingAddr) > 0 {
147+
addrBytes = append(addrBytes, stakingAddr...)
148+
}
149+
150+
return NewAddressFromBytes(addrBytes)
128151
}
129152

130153
func NewByronAddressFromParts(

ledger/shelley/genesis.go

Lines changed: 35 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -22,49 +22,12 @@ import (
2222
"math/big"
2323
"os"
2424
"reflect"
25-
"sync"
2625
"time"
2726

2827
"github.com/blinklabs-io/gouroboros/cbor"
2928
"github.com/blinklabs-io/gouroboros/ledger/common"
3029
)
3130

32-
// Network stake address headers
33-
var (
34-
stakeHeaderRegistry = map[string]struct {
35-
Base, Script byte
36-
}{
37-
"Mainnet": {0xE0, 0xE1},
38-
"Testnet": {0xF0, 0xF1},
39-
}
40-
stakeHeaderMutex sync.RWMutex
41-
)
42-
43-
// RegisterStakeHeaders allows runtime registration of network address headers
44-
func RegisterStakeHeaders(networkId string, baseHeader, scriptHeader byte) {
45-
stakeHeaderMutex.Lock()
46-
defer stakeHeaderMutex.Unlock()
47-
stakeHeaderRegistry[networkId] = struct{ Base, Script byte }{
48-
Base: baseHeader,
49-
Script: scriptHeader,
50-
}
51-
}
52-
53-
func getStakeAddressHeader(networkId string, isScript bool) (byte, error) {
54-
stakeHeaderMutex.RLock()
55-
defer stakeHeaderMutex.RUnlock()
56-
57-
headers, exists := stakeHeaderRegistry[networkId]
58-
if !exists {
59-
return 0, errors.New("network not registered in stake header registry")
60-
}
61-
62-
if isScript {
63-
return headers.Script, nil
64-
}
65-
return headers.Base, nil
66-
}
67-
6831
type ShelleyGenesis struct {
6932
cbor.StructAsArray
7033
SystemStart time.Time `json:"systemStart"`
@@ -269,6 +232,17 @@ func (g *ShelleyGenesis) GenesisUtxos() ([]common.Utxo, error) {
269232
return ret, nil
270233
}
271234

235+
func (g *ShelleyGenesis) getNetworkId() (uint8, error) {
236+
switch g.NetworkId {
237+
case "Mainnet":
238+
return common.AddressNetworkMainnet, nil
239+
case "Testnet":
240+
return common.AddressNetworkTestnet, nil
241+
default:
242+
return 0, errors.New("unknown network ID")
243+
}
244+
}
245+
272246
func (g *ShelleyGenesis) InitialPools() (map[string]common.PoolRegistrationCertificate, map[string][]common.Address, error) {
273247
pools := make(map[string]common.PoolRegistrationCertificate)
274248
poolStake := make(map[string][]common.Address)
@@ -277,38 +251,36 @@ func (g *ShelleyGenesis) InitialPools() (map[string]common.PoolRegistrationCerti
277251
return pools, poolStake, nil
278252
}
279253

280-
headerByte, err := getStakeAddressHeader(g.NetworkId, true)
254+
networkId, err := g.getNetworkId()
281255
if err != nil {
282-
return nil, nil, errors.New("failed to get stake address header")
256+
return nil, nil, err
283257
}
284258

259+
// Process all stake addresses
285260
for stakeAddr, poolId := range g.Staking.Stake {
286-
if len(stakeAddr) != 56 {
287-
return nil, nil, errors.New("invalid stake address length")
288-
}
289-
290-
stakeKeyBytes, err := hex.DecodeString(stakeAddr)
261+
stakeKey, err := hex.DecodeString(stakeAddr)
291262
if err != nil {
292263
return nil, nil, errors.New("failed to decode stake key")
293264
}
294265

295-
stakeAddrBytes := append([]byte{headerByte}, stakeKeyBytes...)
296-
addr, err := common.NewAddressFromBytes(stakeAddrBytes)
266+
addr, err := common.NewAddressFromParts(
267+
common.AddressTypeNoneScript, // Script stake address
268+
networkId,
269+
nil,
270+
stakeKey,
271+
)
297272
if err != nil {
298-
return nil, nil, errors.New("failed to create stake address")
273+
return nil, nil, errors.New("failed to create address")
299274
}
300275

301276
poolStake[poolId] = append(poolStake[poolId], addr)
302277
}
303278

279+
// Process all stake pools
304280
for poolId, pool := range g.Staking.Pools {
305-
if len(poolId) != 56 {
306-
return nil, nil, errors.New("invalid pool ID length")
307-
}
308-
309281
operatorBytes, err := hex.DecodeString(poolId)
310282
if err != nil {
311-
return nil, nil, errors.New("failed to decode pool operator key")
283+
return nil, nil, errors.New("failed to decode pool ID")
312284
}
313285

314286
pools[poolId] = common.PoolRegistrationCertificate{
@@ -334,30 +306,30 @@ func (g *ShelleyGenesis) PoolById(poolId string) (*common.PoolRegistrationCertif
334306

335307
pool, exists := g.Staking.Pools[poolId]
336308
if !exists {
337-
return nil, nil, errors.New("pool not found")
309+
return nil, nil, errors.New("pool not found")
338310
}
339311

340-
headerByte, err := getStakeAddressHeader(g.NetworkId, true)
312+
networkId, err := g.getNetworkId()
341313
if err != nil {
342-
return nil, nil, errors.New("failed to get stake address header")
314+
return nil, nil, err
343315
}
344316

345317
var delegators []common.Address
346318
for stakeAddr, pId := range g.Staking.Stake {
347319
if pId == poolId {
348-
if len(stakeAddr) != 56 {
349-
return nil, nil, errors.New("invalid stake address length")
350-
}
351-
352-
stakeKeyBytes, err := hex.DecodeString(stakeAddr)
320+
stakeKey, err := hex.DecodeString(stakeAddr)
353321
if err != nil {
354322
return nil, nil, errors.New("failed to decode stake key")
355323
}
356324

357-
stakeAddrBytes := append([]byte{headerByte}, stakeKeyBytes...)
358-
addr, err := common.NewAddressFromBytes(stakeAddrBytes)
325+
addr, err := common.NewAddressFromParts(
326+
common.AddressTypeNoneScript,
327+
networkId,
328+
nil,
329+
stakeKey,
330+
)
359331
if err != nil {
360-
return nil, nil, errors.New("failed to create stake address")
332+
return nil, nil, errors.New("failed to create address")
361333
}
362334

363335
delegators = append(delegators, addr)

ledger/shelley/genesis_test.go

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,6 @@ import (
2727
"github.com/blinklabs-io/gouroboros/ledger/shelley"
2828
)
2929

30-
func init() {
31-
// Initialize network headers for tests
32-
shelley.RegisterStakeHeaders("Mainnet", 0xE0, 0xE1)
33-
shelley.RegisterStakeHeaders("Testnet", 0xF0, 0xF1)
34-
}
35-
3630
const shelleyGenesisConfig = `
3731
{
3832
"activeSlotsCoeff": 0.05,
@@ -103,6 +97,9 @@ const shelleyGenesisConfig = `
10397
"securityParam": 2160
10498
}
10599
`
100+
const (
101+
expectedTestnetScriptStakeHeader = 0xF1
102+
)
106103

107104
var expectedGenesisObj = shelley.ShelleyGenesis{
108105
SystemStart: time.Date(
@@ -332,16 +329,29 @@ func TestGenesisStaking(t *testing.T) {
332329
if len(delegs) != 1 {
333330
t.Errorf("Expected 1 delegator, got %d", len(delegs))
334331
} else {
335-
// Extract stake key from address
336-
addrBytes, _ := delegs[0].Bytes()
337-
if len(addrBytes) != 29 || addrBytes[0] != 0xF1 { // Testnet script stake address
338-
t.Errorf("Delegator address is not in expected stake address format: got %x", addrBytes[0])
339-
} else {
340-
stakeKey := hex.EncodeToString(addrBytes[1:])
341-
if stakeKey != expectedStakeKey {
342-
t.Errorf("Delegator key mismatch:\nExpected: %s\nActual: %s",
343-
expectedStakeKey, stakeKey)
344-
}
332+
// Verify address format
333+
addrBytes, err := delegs[0].Bytes()
334+
if err != nil {
335+
t.Fatalf("Failed to get address bytes: %v", err)
336+
}
337+
338+
// Should be 29 bytes (1 header + 28 stake key)
339+
if len(addrBytes) != 29 {
340+
t.Errorf("Expected address length 29, got %d", len(addrBytes))
341+
}
342+
343+
// Verify testnet script stake address header
344+
// In TestInitialPools and TestPoolById, replace the header check with:
345+
if addrBytes[0] != expectedTestnetScriptStakeHeader {
346+
t.Errorf("Expected header byte %x, got %x",
347+
expectedTestnetScriptStakeHeader, addrBytes[0])
348+
}
349+
350+
// Verify stake key matches
351+
stakeKey := hex.EncodeToString(addrBytes[1:])
352+
if stakeKey != expectedStakeKey {
353+
t.Errorf("Delegator key mismatch:\nExpected: %s\nActual: %s",
354+
expectedStakeKey, stakeKey)
345355
}
346356
}
347357
})
@@ -369,16 +379,29 @@ func TestGenesisStaking(t *testing.T) {
369379
if len(delegators) != 1 {
370380
t.Errorf("Expected 1 delegator, got %d", len(delegators))
371381
} else {
372-
// Extract stake key from address
373-
addrBytes, _ := delegators[0].Bytes()
374-
if len(addrBytes) != 29 || addrBytes[0] != 0xF1 { // Testnet script stake address
375-
t.Errorf("Delegator address is not in expected stake address format: got %x", addrBytes[0])
376-
} else {
377-
stakeKey := hex.EncodeToString(addrBytes[1:])
378-
if stakeKey != expectedStakeKey {
379-
t.Errorf("Delegator key mismatch:\nExpected: %s\nActual: %s",
380-
expectedStakeKey, stakeKey)
381-
}
382+
// Verify address format
383+
addrBytes, err := delegators[0].Bytes()
384+
if err != nil {
385+
t.Fatalf("Failed to get address bytes: %v", err)
386+
}
387+
388+
// Should be 29 bytes (1 header + 28 stake key)
389+
if len(addrBytes) != 29 {
390+
t.Errorf("Expected address length 29, got %d", len(addrBytes))
391+
}
392+
393+
// Verify testnet script stake address header
394+
// In TestInitialPools and TestPoolById, replace the header check with:
395+
if addrBytes[0] != expectedTestnetScriptStakeHeader {
396+
t.Errorf("Expected header byte %x, got %x",
397+
expectedTestnetScriptStakeHeader, addrBytes[0])
398+
}
399+
400+
// Verify stake key matches
401+
stakeKey := hex.EncodeToString(addrBytes[1:])
402+
if stakeKey != expectedStakeKey {
403+
t.Errorf("Delegator key mismatch:\nExpected: %s\nActual: %s",
404+
expectedStakeKey, stakeKey)
382405
}
383406
}
384407

0 commit comments

Comments
 (0)