@@ -16,6 +16,7 @@ package sqlite
1616
1717import (
1818 "bytes"
19+ "encoding/hex"
1920 "errors"
2021 "fmt"
2122 "strings"
@@ -1654,6 +1655,166 @@ func (d *MetadataStoreSqlite) SetGenesisTransaction(
16541655 return nil
16551656}
16561657
1658+ // SetGenesisStaking stores genesis pool registrations and stake delegations
1659+ // from the shelley-genesis.json staking section. It creates Pool,
1660+ // PoolRegistration, and Account records at slot 0.
1661+ func (d * MetadataStoreSqlite ) SetGenesisStaking (
1662+ pools map [string ]lcommon.PoolRegistrationCertificate ,
1663+ stakeDelegations map [string ]string ,
1664+ blockHash []byte ,
1665+ txn types.Txn ,
1666+ ) error {
1667+ db , err := d .resolveDB (txn )
1668+ if err != nil {
1669+ return err
1670+ }
1671+
1672+ // Batch fetch all existing pools to avoid N+1 queries
1673+ poolKeyHashes := make ([][]byte , 0 , len (pools ))
1674+ for _ , cert := range pools {
1675+ poolKeyHashes = append (poolKeyHashes , cert .Operator [:])
1676+ }
1677+ var existingPools []models.Pool
1678+ if len (poolKeyHashes ) > 0 {
1679+ if result := db .Where (
1680+ "pool_key_hash IN ?" ,
1681+ poolKeyHashes ,
1682+ ).Find (& existingPools ); result .Error != nil {
1683+ return fmt .Errorf (
1684+ "batch fetch genesis pools: %w" ,
1685+ result .Error ,
1686+ )
1687+ }
1688+ }
1689+ existingPoolMap := make (map [string ]* models.Pool , len (existingPools ))
1690+ for i := range existingPools {
1691+ key := hex .EncodeToString (existingPools [i ].PoolKeyHash )
1692+ existingPoolMap [key ] = & existingPools [i ]
1693+ }
1694+
1695+ for _ , cert := range pools {
1696+ poolKey := hex .EncodeToString (cert .Operator [:])
1697+ tmpPool := existingPoolMap [poolKey ]
1698+ if tmpPool == nil {
1699+ tmpPool = & models.Pool {
1700+ PoolKeyHash : cert .Operator [:],
1701+ VrfKeyHash : cert .VrfKeyHash [:],
1702+ }
1703+ }
1704+ tmpPool .Pledge = types .Uint64 (cert .Pledge )
1705+ tmpPool .Cost = types .Uint64 (cert .Cost )
1706+ tmpPool .Margin = & types.Rat {Rat : cert .Margin .Rat }
1707+ tmpPool .RewardAccount = cert .RewardAccount [:]
1708+
1709+ tmpReg := models.PoolRegistration {
1710+ PoolKeyHash : cert .Operator [:],
1711+ VrfKeyHash : cert .VrfKeyHash [:],
1712+ Pledge : types .Uint64 (cert .Pledge ),
1713+ Cost : types .Uint64 (cert .Cost ),
1714+ Margin : & types.Rat {Rat : cert .Margin .Rat },
1715+ RewardAccount : cert .RewardAccount [:],
1716+ AddedSlot : 0 ,
1717+ }
1718+ if cert .PoolMetadata != nil {
1719+ tmpReg .MetadataUrl = cert .PoolMetadata .Url
1720+ tmpReg .MetadataHash = cert .PoolMetadata .Hash [:]
1721+ }
1722+ for _ , owner := range cert .PoolOwners {
1723+ tmpReg .Owners = append (
1724+ tmpReg .Owners ,
1725+ models.PoolRegistrationOwner {KeyHash : owner [:]},
1726+ )
1727+ }
1728+ tmpPool .Owners = tmpReg .Owners
1729+
1730+ for _ , relay := range cert .Relays {
1731+ tmpRelay := models.PoolRegistrationRelay {
1732+ Ipv4 : relay .Ipv4 ,
1733+ Ipv6 : relay .Ipv6 ,
1734+ }
1735+ if relay .Port != nil {
1736+ tmpRelay .Port = uint (* relay .Port )
1737+ }
1738+ if relay .Hostname != nil {
1739+ tmpRelay .Hostname = * relay .Hostname
1740+ }
1741+ tmpReg .Relays = append (tmpReg .Relays , tmpRelay )
1742+ }
1743+ tmpPool .Relays = tmpReg .Relays
1744+
1745+ if tmpPool .ID == 0 {
1746+ result := db .Omit (clause .Associations ).Create (tmpPool )
1747+ if result .Error != nil {
1748+ return fmt .Errorf (
1749+ "create genesis pool: %w" ,
1750+ result .Error ,
1751+ )
1752+ }
1753+ } else {
1754+ result := db .Omit (clause .Associations ).Save (tmpPool )
1755+ if result .Error != nil {
1756+ return fmt .Errorf (
1757+ "save genesis pool: %w" ,
1758+ result .Error ,
1759+ )
1760+ }
1761+ }
1762+ tmpReg .PoolID = tmpPool .ID
1763+ for i := range tmpReg .Owners {
1764+ tmpReg .Owners [i ].PoolID = tmpPool .ID
1765+ }
1766+ for i := range tmpReg .Relays {
1767+ tmpReg .Relays [i ].PoolID = tmpPool .ID
1768+ }
1769+
1770+ result := db .Create (& tmpReg )
1771+ if result .Error != nil {
1772+ return fmt .Errorf (
1773+ "create genesis pool registration: %w" ,
1774+ result .Error ,
1775+ )
1776+ }
1777+ }
1778+
1779+ for stakerHex , poolHex := range stakeDelegations {
1780+ stakerBytes , err := hex .DecodeString (stakerHex )
1781+ if err != nil {
1782+ return fmt .Errorf (
1783+ "decode staker hash %s: %w" ,
1784+ stakerHex ,
1785+ err ,
1786+ )
1787+ }
1788+ poolBytes , err := hex .DecodeString (poolHex )
1789+ if err != nil {
1790+ return fmt .Errorf (
1791+ "decode pool hash %s: %w" ,
1792+ poolHex ,
1793+ err ,
1794+ )
1795+ }
1796+
1797+ account := & models.Account {
1798+ StakingKey : stakerBytes ,
1799+ Pool : poolBytes ,
1800+ Active : true ,
1801+ AddedSlot : 0 ,
1802+ }
1803+ result := db .Clauses (clause.OnConflict {
1804+ Columns : []clause.Column {{Name : "staking_key" }},
1805+ DoNothing : true ,
1806+ }).Create (account )
1807+ if result .Error != nil {
1808+ return fmt .Errorf (
1809+ "create genesis account: %w" ,
1810+ result .Error ,
1811+ )
1812+ }
1813+ }
1814+
1815+ return nil
1816+ }
1817+
16571818// Traverse each utxo and check for inline datum & calls storeDatum
16581819func (d * MetadataStoreSqlite ) storeTransactionDatums (
16591820 tx lcommon.Transaction ,
0 commit comments