diff --git a/database/plugin/metadata/sqlite/certs.go b/database/plugin/metadata/sqlite/certs.go index fd64bc8b..f433e955 100644 --- a/database/plugin/metadata/sqlite/certs.go +++ b/database/plugin/metadata/sqlite/certs.go @@ -16,97 +16,10 @@ package sqlite import ( "github.com/blinklabs-io/dingo/database/plugin/metadata/sqlite/models" - "github.com/blinklabs-io/dingo/database/types" - "github.com/blinklabs-io/gouroboros/cbor" lcommon "github.com/blinklabs-io/gouroboros/ledger/common" "gorm.io/gorm" ) -// GetPoolRegistrations returns pool registration certificates -func (d *MetadataStoreSqlite) GetPoolRegistrations( - pkh lcommon.PoolKeyHash, - txn *gorm.DB, -) ([]lcommon.PoolRegistrationCertificate, error) { - ret := []lcommon.PoolRegistrationCertificate{} - certs := []models.PoolRegistration{} - if txn != nil { - result := txn.Where("pool_key_hash = ?", lcommon.Blake2b224(pkh).Bytes()). - Order("id DESC"). - Find(&certs) - if result.Error != nil { - return ret, result.Error - } - } else { - result := d.DB().Where("pool_key_hash = ?", lcommon.Blake2b224(pkh).Bytes()). - Order("id DESC"). - Find(&certs) - if result.Error != nil { - return ret, result.Error - } - } - var addrKeyHash lcommon.AddrKeyHash - var tmpCert lcommon.PoolRegistrationCertificate - var tmpMargin cbor.Rat - var tmpRelay lcommon.PoolRelay - for _, cert := range certs { - tmpMargin = cbor.Rat{Rat: cert.Margin.Rat} - tmpCert = lcommon.PoolRegistrationCertificate{ - CertType: lcommon.CertificateTypePoolRegistration, - Operator: lcommon.PoolKeyHash( - lcommon.NewBlake2b224(cert.PoolKeyHash), - ), - VrfKeyHash: lcommon.VrfKeyHash( - lcommon.NewBlake2b256(cert.VrfKeyHash), - ), - Pledge: uint64(cert.Pledge), - Cost: uint64(cert.Cost), - Margin: tmpMargin, - RewardAccount: lcommon.AddrKeyHash( - lcommon.NewBlake2b224(cert.RewardAccount), - ), - } - for _, owner := range cert.Owners { - addrKeyHash = lcommon.AddrKeyHash( - lcommon.NewBlake2b224(owner.KeyHash), - ) - tmpCert.PoolOwners = append(tmpCert.PoolOwners, addrKeyHash) - } - for _, relay := range cert.Relays { - tmpRelay = lcommon.PoolRelay{} - // Determine type - if relay.Port != 0 { - port := uint32(relay.Port) // #nosec G115 - tmpRelay.Port = &port - if relay.Hostname != "" { - hostname := relay.Hostname - tmpRelay.Type = lcommon.PoolRelayTypeSingleHostName - tmpRelay.Hostname = &hostname - } else { - tmpRelay.Type = lcommon.PoolRelayTypeSingleHostAddress - tmpRelay.Ipv4 = relay.Ipv4 - tmpRelay.Ipv6 = relay.Ipv6 - } - } else { - hostname := relay.Hostname - tmpRelay.Type = lcommon.PoolRelayTypeMultiHostName - tmpRelay.Hostname = &hostname - } - tmpCert.Relays = append(tmpCert.Relays, tmpRelay) - } - if cert.MetadataUrl != "" { - poolMetadata := &lcommon.PoolMetadata{ - Url: cert.MetadataUrl, - Hash: lcommon.PoolMetadataHash( - lcommon.NewBlake2b256(cert.MetadataHash), - ), - } - tmpCert.PoolMetadata = poolMetadata - } - ret = append(ret, tmpCert) - } - return ret, nil -} - // GetStakeRegistrations returns stake registration certificates func (d *MetadataStoreSqlite) GetStakeRegistrations( stakingKey []byte, @@ -144,80 +57,6 @@ func (d *MetadataStoreSqlite) GetStakeRegistrations( return ret, nil } -// SetPoolRegistration saves a pool registration certificate -func (d *MetadataStoreSqlite) SetPoolRegistration( - cert *lcommon.PoolRegistrationCertificate, - slot, deposit uint64, - txn *gorm.DB, -) error { - tmpItem := models.PoolRegistration{ - PoolKeyHash: cert.Operator[:], - VrfKeyHash: cert.VrfKeyHash[:], - Pledge: types.Uint64(cert.Pledge), - Cost: types.Uint64(cert.Cost), - Margin: &types.Rat{Rat: cert.Margin.Rat}, - AddedSlot: slot, - DepositAmount: deposit, - } - if cert.PoolMetadata != nil { - tmpItem.MetadataUrl = cert.PoolMetadata.Url - tmpItem.MetadataHash = cert.PoolMetadata.Hash[:] - } - for _, owner := range cert.PoolOwners { - tmpItem.Owners = append( - tmpItem.Owners, - models.PoolRegistrationOwner{KeyHash: owner[:]}, - ) - } - var tmpRelay models.PoolRegistrationRelay - for _, relay := range cert.Relays { - tmpRelay = models.PoolRegistrationRelay{ - Ipv4: relay.Ipv4, - Ipv6: relay.Ipv6, - } - if relay.Port != nil { - tmpRelay.Port = uint(*relay.Port) - } - if relay.Hostname != nil { - tmpRelay.Hostname = *relay.Hostname - } - tmpItem.Relays = append(tmpItem.Relays, tmpRelay) - } - if txn != nil { - if result := txn.Create(&tmpItem); result.Error != nil { - return result.Error - } - } else { - if result := d.DB().Create(&tmpItem); result.Error != nil { - return result.Error - } - } - return nil -} - -// SetPoolRetirement saves a pool retirement certificate -func (d *MetadataStoreSqlite) SetPoolRetirement( - cert *lcommon.PoolRetirementCertificate, - slot uint64, - txn *gorm.DB, -) error { - tmpItem := models.PoolRetirement{ - PoolKeyHash: cert.PoolKeyHash[:], - Epoch: cert.Epoch, - AddedSlot: slot, - } - if txn != nil { - if result := txn.Create(&tmpItem); result.Error != nil { - return result.Error - } - } else { - if result := d.DB().Create(&tmpItem); result.Error != nil { - return result.Error - } - } - return nil -} - // SetStakeDelegation saves a stake delegation certificate func (d *MetadataStoreSqlite) SetStakeDelegation( cert *lcommon.StakeDelegationCertificate, diff --git a/database/plugin/metadata/sqlite/models/models.go b/database/plugin/metadata/sqlite/models/models.go index 7f740cd3..b4062695 100644 --- a/database/plugin/metadata/sqlite/models/models.go +++ b/database/plugin/metadata/sqlite/models/models.go @@ -21,6 +21,7 @@ var MigrateModels = []any{ &Deregistration{}, &Drep{}, &Epoch{}, + &Pool{}, &PoolRegistration{}, &PoolRegistrationOwner{}, &PoolRegistrationRelay{}, diff --git a/database/plugin/metadata/sqlite/models/pool_registration.go b/database/plugin/metadata/sqlite/models/pool.go similarity index 69% rename from database/plugin/metadata/sqlite/models/pool_registration.go rename to database/plugin/metadata/sqlite/models/pool.go index 032d62e7..c795d89d 100644 --- a/database/plugin/metadata/sqlite/models/pool_registration.go +++ b/database/plugin/metadata/sqlite/models/pool.go @@ -20,7 +20,7 @@ import ( "github.com/blinklabs-io/dingo/database/types" ) -type PoolRegistration struct { +type Pool struct { ID uint `gorm:"primarykey"` PoolKeyHash []byte `gorm:"index"` VrfKeyHash []byte @@ -30,6 +30,25 @@ type PoolRegistration struct { RewardAccount []byte Owners []PoolRegistrationOwner Relays []PoolRegistrationRelay + Registration []PoolRegistration + Retirement []PoolRetirement +} + +func (p *Pool) TableName() string { + return "pool" +} + +type PoolRegistration struct { + ID uint `gorm:"primarykey"` + PoolID uint + PoolKeyHash []byte `gorm:"index"` + VrfKeyHash []byte + Pledge types.Uint64 + Cost types.Uint64 + Margin *types.Rat + RewardAccount []byte + Owners []PoolRegistrationOwner + Relays []PoolRegistrationRelay MetadataUrl string MetadataHash []byte AddedSlot uint64 @@ -43,6 +62,7 @@ func (PoolRegistration) TableName() string { type PoolRegistrationOwner struct { ID uint `gorm:"primarykey"` PoolRegistrationID uint + PoolID uint KeyHash []byte } @@ -53,6 +73,7 @@ func (PoolRegistrationOwner) TableName() string { type PoolRegistrationRelay struct { ID uint `gorm:"primarykey"` PoolRegistrationID uint + PoolID uint Port uint Ipv4 *net.IP Ipv6 *net.IP @@ -62,3 +83,15 @@ type PoolRegistrationRelay struct { func (PoolRegistrationRelay) TableName() string { return "pool_registration_relay" } + +type PoolRetirement struct { + ID uint `gorm:"primarykey"` + PoolID uint + PoolKeyHash []byte `gorm:"index"` + Epoch uint64 + AddedSlot uint64 +} + +func (PoolRetirement) TableName() string { + return "pool_retirement" +} diff --git a/database/plugin/metadata/sqlite/models/pool_retirement.go b/database/plugin/metadata/sqlite/models/pool_retirement.go deleted file mode 100644 index e683fdf7..00000000 --- a/database/plugin/metadata/sqlite/models/pool_retirement.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2025 Blink Labs Software -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package models - -type PoolRetirement struct { - ID uint `gorm:"primarykey"` - PoolKeyHash []byte `gorm:"index"` - Epoch uint64 - AddedSlot uint64 -} - -func (PoolRetirement) TableName() string { - return "pool_retirement" -} diff --git a/database/plugin/metadata/sqlite/pool.go b/database/plugin/metadata/sqlite/pool.go new file mode 100644 index 00000000..0cdbbde4 --- /dev/null +++ b/database/plugin/metadata/sqlite/pool.go @@ -0,0 +1,236 @@ +// Copyright 2025 Blink Labs Software +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sqlite + +import ( + "errors" + + "github.com/blinklabs-io/dingo/database/plugin/metadata/sqlite/models" + "github.com/blinklabs-io/dingo/database/types" + "github.com/blinklabs-io/gouroboros/cbor" + lcommon "github.com/blinklabs-io/gouroboros/ledger/common" + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +// GetPool gets a pool +func (d *MetadataStoreSqlite) GetPool( + // pkh lcommon.PoolKeyHash, + pkh []byte, + txn *gorm.DB, +) (models.Pool, error) { + ret := models.Pool{} + tmpPool := models.Pool{} + if txn != nil { + result := txn.First(&tmpPool, "pool_key_hash = ?", lcommon.Blake2b224(pkh).Bytes()) + if result.Error != nil { + return ret, result.Error + } + } else { + result := d.DB().First(&tmpPool, "pool_key_hash = ?", lcommon.Blake2b224(pkh).Bytes()) + if result.Error != nil { + return ret, result.Error + } + } + ret = tmpPool + return ret, nil +} + +// GetPoolRegistrations returns pool registration certificates +func (d *MetadataStoreSqlite) GetPoolRegistrations( + pkh lcommon.PoolKeyHash, + txn *gorm.DB, +) ([]lcommon.PoolRegistrationCertificate, error) { + ret := []lcommon.PoolRegistrationCertificate{} + certs := []models.PoolRegistration{} + if txn != nil { + result := txn.Where("pool_key_hash = ?", lcommon.Blake2b224(pkh).Bytes()). + Order("id DESC"). + Find(&certs) + if result.Error != nil { + return ret, result.Error + } + } else { + result := d.DB().Where("pool_key_hash = ?", lcommon.Blake2b224(pkh).Bytes()). + Order("id DESC"). + Find(&certs) + if result.Error != nil { + return ret, result.Error + } + } + var addrKeyHash lcommon.AddrKeyHash + var tmpCert lcommon.PoolRegistrationCertificate + var tmpMargin cbor.Rat + var tmpRelay lcommon.PoolRelay + for _, cert := range certs { + tmpMargin = cbor.Rat{Rat: cert.Margin.Rat} + tmpCert = lcommon.PoolRegistrationCertificate{ + CertType: lcommon.CertificateTypePoolRegistration, + Operator: lcommon.PoolKeyHash( + lcommon.NewBlake2b224(cert.PoolKeyHash), + ), + VrfKeyHash: lcommon.VrfKeyHash( + lcommon.NewBlake2b256(cert.VrfKeyHash), + ), + Pledge: uint64(cert.Pledge), + Cost: uint64(cert.Cost), + Margin: tmpMargin, + RewardAccount: lcommon.AddrKeyHash( + lcommon.NewBlake2b224(cert.RewardAccount), + ), + } + for _, owner := range cert.Owners { + addrKeyHash = lcommon.AddrKeyHash( + lcommon.NewBlake2b224(owner.KeyHash), + ) + tmpCert.PoolOwners = append(tmpCert.PoolOwners, addrKeyHash) + } + for _, relay := range cert.Relays { + tmpRelay = lcommon.PoolRelay{} + // Determine type + if relay.Port != 0 { + port := uint32(relay.Port) // #nosec G115 + tmpRelay.Port = &port + if relay.Hostname != "" { + hostname := relay.Hostname + tmpRelay.Type = lcommon.PoolRelayTypeSingleHostName + tmpRelay.Hostname = &hostname + } else { + tmpRelay.Type = lcommon.PoolRelayTypeSingleHostAddress + tmpRelay.Ipv4 = relay.Ipv4 + tmpRelay.Ipv6 = relay.Ipv6 + } + } else { + hostname := relay.Hostname + tmpRelay.Type = lcommon.PoolRelayTypeMultiHostName + tmpRelay.Hostname = &hostname + } + tmpCert.Relays = append(tmpCert.Relays, tmpRelay) + } + if cert.MetadataUrl != "" { + poolMetadata := &lcommon.PoolMetadata{ + Url: cert.MetadataUrl, + Hash: lcommon.PoolMetadataHash( + lcommon.NewBlake2b256(cert.MetadataHash), + ), + } + tmpCert.PoolMetadata = poolMetadata + } + ret = append(ret, tmpCert) + } + return ret, nil +} + +// SetPoolRegistration saves a pool registration certificate and pool +func (d *MetadataStoreSqlite) SetPoolRegistration( + cert *lcommon.PoolRegistrationCertificate, + slot, deposit uint64, + txn *gorm.DB, +) error { + var tmpItem models.Pool + tmpPool, err := d.GetPool(cert.Operator[:], txn) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + tmpItem = models.Pool{ + PoolKeyHash: cert.Operator[:], + VrfKeyHash: cert.VrfKeyHash[:], + } + } else { + return err + } + } else { + // Store our fetched pool + tmpItem = tmpPool + } + tmpItem.Pledge = types.Uint64(cert.Pledge) + tmpItem.Cost = types.Uint64(cert.Cost) + tmpItem.Margin = &types.Rat{Rat: cert.Margin.Rat} + tmpItem.RewardAccount = cert.RewardAccount[:] + + tmpReg := models.PoolRegistration{ + PoolKeyHash: cert.Operator[:], + VrfKeyHash: cert.VrfKeyHash[:], + Pledge: types.Uint64(cert.Pledge), + Cost: types.Uint64(cert.Cost), + Margin: &types.Rat{Rat: cert.Margin.Rat}, + AddedSlot: slot, + DepositAmount: deposit, + } + if cert.PoolMetadata != nil { + tmpReg.MetadataUrl = cert.PoolMetadata.Url + tmpReg.MetadataHash = cert.PoolMetadata.Hash[:] + } + for _, owner := range cert.PoolOwners { + tmpReg.Owners = append( + tmpReg.Owners, + models.PoolRegistrationOwner{KeyHash: owner[:]}, + ) + } + tmpItem.Owners = tmpReg.Owners + var tmpRelay models.PoolRegistrationRelay + for _, relay := range cert.Relays { + tmpRelay = models.PoolRegistrationRelay{ + Ipv4: relay.Ipv4, + Ipv6: relay.Ipv6, + } + if relay.Port != nil { + tmpRelay.Port = uint(*relay.Port) + } + if relay.Hostname != nil { + tmpRelay.Hostname = *relay.Hostname + } + tmpItem.Relays = append(tmpReg.Relays, tmpRelay) + } + tmpItem.Registration = append(tmpItem.Registration, tmpReg) + tmpItem.Relays = tmpReg.Relays + if txn != nil { + if result := txn.Clauses(clause.OnConflict{UpdateAll: true}).Create(&tmpItem); result.Error != nil { + return result.Error + } + } else { + if result := d.DB().Clauses(clause.OnConflict{UpdateAll: true}).Create(&tmpItem); result.Error != nil { + return result.Error + } + } + return nil +} + +// SetPoolRetirement saves a pool retirement certificate +func (d *MetadataStoreSqlite) SetPoolRetirement( + cert *lcommon.PoolRetirementCertificate, + slot uint64, + txn *gorm.DB, +) error { + tmpPool, err := d.GetPool(cert.PoolKeyHash[:], txn) + if err != nil { + return err + } + tmpItem := models.PoolRetirement{ + PoolKeyHash: cert.PoolKeyHash[:], + Epoch: cert.Epoch, + AddedSlot: slot, + } + tmpPool.Retirement = append(tmpPool.Retirement, tmpItem) + if txn != nil { + if result := txn.Save(&tmpPool); result.Error != nil { + return result.Error + } + } else { + if result := d.DB().Save(&tmpPool); result.Error != nil { + return result.Error + } + } + return nil +}