2021-01-26 10:33:28 +00:00
|
|
|
package innerring
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-05-31 08:55:38 +00:00
|
|
|
"crypto/ecdsa"
|
|
|
|
"crypto/elliptic"
|
2022-05-12 16:37:46 +00:00
|
|
|
"crypto/sha256"
|
2021-02-01 16:20:33 +00:00
|
|
|
"encoding/hex"
|
2021-05-18 08:12:51 +00:00
|
|
|
"fmt"
|
2021-01-26 10:33:28 +00:00
|
|
|
"math/big"
|
|
|
|
|
2023-03-07 13:38:26 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/settlement/audit"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/settlement/basic"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/settlement/common"
|
|
|
|
auditClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/audit"
|
|
|
|
balanceClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/balance"
|
|
|
|
containerClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
|
|
|
|
netmapClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
|
|
|
auditAPI "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/audit"
|
|
|
|
containerAPI "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
|
|
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
|
|
netmapAPI "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
|
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/storagegroup"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
2021-05-31 08:55:38 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2021-01-26 10:33:28 +00:00
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
2022-05-20 08:25:10 +00:00
|
|
|
const (
|
|
|
|
auditSettlementContext = "audit"
|
|
|
|
basicIncomeSettlementContext = "basic income"
|
|
|
|
)
|
|
|
|
|
2021-02-01 12:40:07 +00:00
|
|
|
type settlementDeps struct {
|
2021-01-26 10:33:28 +00:00
|
|
|
log *logger.Logger
|
|
|
|
|
|
|
|
cnrSrc container.Source
|
|
|
|
|
2022-01-31 12:32:51 +00:00
|
|
|
auditClient *auditClient.Client
|
2021-01-26 10:33:28 +00:00
|
|
|
|
2022-07-13 14:45:33 +00:00
|
|
|
nmClient *netmapClient.Client
|
2021-01-26 10:33:28 +00:00
|
|
|
|
|
|
|
clientCache *ClientCache
|
|
|
|
|
2022-01-31 09:24:25 +00:00
|
|
|
balanceClient *balanceClient.Client
|
2022-05-20 08:25:10 +00:00
|
|
|
|
|
|
|
settlementCtx string
|
2021-01-26 10:33:28 +00:00
|
|
|
}
|
|
|
|
|
2021-02-01 12:40:07 +00:00
|
|
|
type auditSettlementDeps struct {
|
2022-05-20 08:25:10 +00:00
|
|
|
settlementDeps
|
2021-02-01 12:40:07 +00:00
|
|
|
}
|
|
|
|
|
2021-02-01 16:20:33 +00:00
|
|
|
type basicIncomeSettlementDeps struct {
|
2022-05-20 08:25:10 +00:00
|
|
|
settlementDeps
|
2022-01-31 13:34:01 +00:00
|
|
|
cnrClient *containerClient.Client
|
2021-02-01 16:20:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type basicSettlementConstructor struct {
|
|
|
|
dep *basicIncomeSettlementDeps
|
|
|
|
}
|
|
|
|
|
2021-01-29 07:42:40 +00:00
|
|
|
type auditSettlementCalculator audit.Calculator
|
|
|
|
|
2021-01-26 10:33:28 +00:00
|
|
|
type containerWrapper containerAPI.Container
|
|
|
|
|
|
|
|
type nodeInfoWrapper struct {
|
2022-06-08 23:18:26 +00:00
|
|
|
ni netmapAPI.NodeInfo
|
2021-01-26 10:33:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type sgWrapper storagegroup.StorageGroup
|
|
|
|
|
|
|
|
func (s *sgWrapper) Size() uint64 {
|
|
|
|
return (*storagegroup.StorageGroup)(s).ValidationDataSize()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n nodeInfoWrapper) PublicKey() []byte {
|
|
|
|
return n.ni.PublicKey()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n nodeInfoWrapper) Price() *big.Int {
|
2022-06-08 23:18:26 +00:00
|
|
|
return big.NewInt(int64(n.ni.Price()))
|
2021-01-26 10:33:28 +00:00
|
|
|
}
|
|
|
|
|
2022-06-28 07:01:05 +00:00
|
|
|
func (c containerWrapper) Owner() user.ID {
|
|
|
|
return (containerAPI.Container)(c).Owner()
|
2021-01-26 10:33:28 +00:00
|
|
|
}
|
|
|
|
|
2021-02-01 12:40:07 +00:00
|
|
|
func (s settlementDeps) AuditResultsForEpoch(epoch uint64) ([]*auditAPI.Result, error) {
|
|
|
|
idList, err := s.auditClient.ListAuditResultIDByEpoch(epoch)
|
2021-01-26 10:33:28 +00:00
|
|
|
if err != nil {
|
2021-05-18 08:12:51 +00:00
|
|
|
return nil, fmt.Errorf("could not list audit results in sidechain: %w", err)
|
2021-01-26 10:33:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
res := make([]*auditAPI.Result, 0, len(idList))
|
|
|
|
|
|
|
|
for i := range idList {
|
2021-02-01 12:40:07 +00:00
|
|
|
r, err := s.auditClient.GetAuditResult(idList[i])
|
2021-01-26 10:33:28 +00:00
|
|
|
if err != nil {
|
2021-05-18 08:12:51 +00:00
|
|
|
return nil, fmt.Errorf("could not get audit result: %w", err)
|
2021-01-26 10:33:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
res = append(res, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
2022-05-31 17:00:41 +00:00
|
|
|
func (s settlementDeps) ContainerInfo(cid cid.ID) (common.ContainerInfo, error) {
|
2021-02-01 12:40:07 +00:00
|
|
|
cnr, err := s.cnrSrc.Get(cid)
|
2021-01-26 10:33:28 +00:00
|
|
|
if err != nil {
|
2021-05-18 08:12:51 +00:00
|
|
|
return nil, fmt.Errorf("could not get container from storage: %w", err)
|
2021-01-26 10:33:28 +00:00
|
|
|
}
|
|
|
|
|
2022-06-28 07:01:05 +00:00
|
|
|
return (containerWrapper)(cnr.Value), nil
|
2021-01-26 10:33:28 +00:00
|
|
|
}
|
|
|
|
|
2022-06-08 23:18:26 +00:00
|
|
|
func (s settlementDeps) buildContainer(e uint64, cid cid.ID) ([][]netmapAPI.NodeInfo, *netmapAPI.NetMap, error) {
|
2021-01-26 10:33:28 +00:00
|
|
|
var (
|
2022-06-08 23:18:26 +00:00
|
|
|
nm *netmapAPI.NetMap
|
2021-01-26 10:33:28 +00:00
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
if e > 0 {
|
2022-07-13 14:45:33 +00:00
|
|
|
nm, err = s.nmClient.GetNetMapByEpoch(e)
|
2021-01-26 10:33:28 +00:00
|
|
|
} else {
|
2022-07-13 14:45:33 +00:00
|
|
|
nm, err = netmap.GetLatestNetworkMap(s.nmClient)
|
2021-01-26 10:33:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
2021-05-18 08:12:51 +00:00
|
|
|
return nil, nil, fmt.Errorf("could not get network map from storage: %w", err)
|
2021-01-26 10:33:28 +00:00
|
|
|
}
|
|
|
|
|
2021-02-01 12:40:07 +00:00
|
|
|
cnr, err := s.cnrSrc.Get(cid)
|
2021-01-26 10:33:28 +00:00
|
|
|
if err != nil {
|
2021-05-18 08:12:51 +00:00
|
|
|
return nil, nil, fmt.Errorf("could not get container from sidechain: %w", err)
|
2021-01-26 10:33:28 +00:00
|
|
|
}
|
|
|
|
|
2022-05-12 16:37:46 +00:00
|
|
|
binCnr := make([]byte, sha256.Size)
|
|
|
|
cid.Encode(binCnr)
|
|
|
|
|
2022-06-08 23:18:26 +00:00
|
|
|
cn, err := nm.ContainerNodes(
|
2022-06-28 07:01:05 +00:00
|
|
|
cnr.Value.PlacementPolicy(),
|
2022-12-23 17:35:35 +00:00
|
|
|
binCnr, // may be replace pivot calculation to frostfs-api-go
|
2021-01-26 10:33:28 +00:00
|
|
|
)
|
|
|
|
if err != nil {
|
2021-05-18 08:12:51 +00:00
|
|
|
return nil, nil, fmt.Errorf("could not calculate container nodes: %w", err)
|
2021-01-26 10:33:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return cn, nm, nil
|
|
|
|
}
|
|
|
|
|
2022-05-31 17:00:41 +00:00
|
|
|
func (s settlementDeps) ContainerNodes(e uint64, cid cid.ID) ([]common.NodeInfo, error) {
|
2021-02-01 12:40:07 +00:00
|
|
|
cn, _, err := s.buildContainer(e, cid)
|
2021-01-26 10:33:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-06-16 22:00:38 +00:00
|
|
|
var sz int
|
2021-01-26 10:33:28 +00:00
|
|
|
|
2022-06-16 22:00:38 +00:00
|
|
|
for i := range cn {
|
|
|
|
sz += len(cn[i])
|
|
|
|
}
|
|
|
|
|
|
|
|
res := make([]common.NodeInfo, 0, sz)
|
|
|
|
|
|
|
|
for i := range cn {
|
|
|
|
for j := range cn[i] {
|
|
|
|
res = append(res, nodeInfoWrapper{
|
|
|
|
ni: cn[i][j],
|
|
|
|
})
|
|
|
|
}
|
2021-01-26 10:33:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
2022-03-17 08:03:58 +00:00
|
|
|
// SGInfo returns audit.SGInfo by object address.
|
|
|
|
//
|
2022-03-17 13:26:17 +00:00
|
|
|
// Returns an error of type apistatus.ObjectNotFound if storage group is missing.
|
2022-05-31 17:00:41 +00:00
|
|
|
func (s settlementDeps) SGInfo(addr oid.Address) (audit.SGInfo, error) {
|
|
|
|
cnr := addr.Container()
|
2022-05-12 16:37:46 +00:00
|
|
|
|
2022-05-31 17:00:41 +00:00
|
|
|
cn, nm, err := s.buildContainer(0, cnr)
|
2021-01-26 10:33:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-02-01 12:40:07 +00:00
|
|
|
sg, err := s.clientCache.getSG(context.Background(), addr, nm, cn)
|
2021-01-26 10:33:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return (*sgWrapper)(sg), nil
|
|
|
|
}
|
|
|
|
|
2022-05-17 13:59:46 +00:00
|
|
|
func (s settlementDeps) ResolveKey(ni common.NodeInfo) (*user.ID, error) {
|
2021-05-31 08:55:38 +00:00
|
|
|
pub, err := keys.NewPublicKeyFromBytes(ni.PublicKey(), elliptic.P256())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-05-17 13:59:46 +00:00
|
|
|
var id user.ID
|
|
|
|
user.IDFromKey(&id, (ecdsa.PublicKey)(*pub))
|
|
|
|
|
|
|
|
return &id, nil
|
2021-01-26 10:33:28 +00:00
|
|
|
}
|
|
|
|
|
2022-05-31 17:00:41 +00:00
|
|
|
func (s settlementDeps) Transfer(sender, recipient user.ID, amount *big.Int, details []byte) {
|
2022-05-20 08:25:10 +00:00
|
|
|
if s.settlementCtx == "" {
|
|
|
|
panic("unknown settlement deps context")
|
|
|
|
}
|
|
|
|
|
2021-02-01 12:40:07 +00:00
|
|
|
log := s.log.With(
|
2021-01-28 19:53:59 +00:00
|
|
|
zap.Stringer("sender", sender),
|
|
|
|
zap.Stringer("recipient", recipient),
|
|
|
|
zap.Stringer("amount (GASe-12)", amount),
|
2021-04-07 12:12:36 +00:00
|
|
|
zap.String("details", hex.EncodeToString(details)),
|
2021-01-28 19:53:59 +00:00
|
|
|
)
|
|
|
|
|
2021-01-26 10:33:28 +00:00
|
|
|
if !amount.IsInt64() {
|
2021-02-01 12:40:07 +00:00
|
|
|
s.log.Error("amount can not be represented as an int64")
|
2021-01-26 10:33:28 +00:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-04-29 13:50:46 +00:00
|
|
|
params := balanceClient.TransferPrm{
|
2021-01-28 17:05:52 +00:00
|
|
|
Amount: amount.Int64(),
|
2021-01-26 10:33:28 +00:00
|
|
|
From: sender,
|
|
|
|
To: recipient,
|
2021-02-01 12:40:07 +00:00
|
|
|
Details: details,
|
2021-04-29 13:50:46 +00:00
|
|
|
}
|
|
|
|
|
2021-06-02 16:24:30 +00:00
|
|
|
err := s.balanceClient.TransferX(params)
|
2021-04-29 13:50:46 +00:00
|
|
|
if err != nil {
|
2022-05-20 08:25:10 +00:00
|
|
|
log.Error(fmt.Sprintf("%s: could not send transfer", s.settlementCtx),
|
2021-01-26 10:33:28 +00:00
|
|
|
zap.String("error", err.Error()),
|
|
|
|
)
|
2021-01-28 19:53:59 +00:00
|
|
|
|
|
|
|
return
|
2021-01-26 10:33:28 +00:00
|
|
|
}
|
2021-01-28 19:53:59 +00:00
|
|
|
|
2022-05-20 08:25:10 +00:00
|
|
|
log.Debug(fmt.Sprintf("%s: transfer was successfully sent", s.settlementCtx))
|
2021-01-26 10:33:28 +00:00
|
|
|
}
|
2021-01-29 07:42:40 +00:00
|
|
|
|
2021-02-02 11:12:41 +00:00
|
|
|
func (b basicIncomeSettlementDeps) BasicRate() (uint64, error) {
|
2022-07-13 14:45:33 +00:00
|
|
|
return b.nmClient.BasicIncomeRate()
|
2021-02-01 16:20:33 +00:00
|
|
|
}
|
|
|
|
|
2021-08-24 10:53:35 +00:00
|
|
|
func (b basicIncomeSettlementDeps) Estimations(epoch uint64) ([]*containerClient.Estimations, error) {
|
2021-02-01 16:20:33 +00:00
|
|
|
estimationIDs, err := b.cnrClient.ListLoadEstimationsByEpoch(epoch)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-08-24 10:53:35 +00:00
|
|
|
result := make([]*containerClient.Estimations, 0, len(estimationIDs))
|
2021-02-01 16:20:33 +00:00
|
|
|
|
|
|
|
for i := range estimationIDs {
|
|
|
|
estimation, err := b.cnrClient.GetUsedSpaceEstimations(estimationIDs[i])
|
|
|
|
if err != nil {
|
|
|
|
b.log.Warn("can't get used space estimation",
|
|
|
|
zap.String("estimation_id", hex.EncodeToString(estimationIDs[i])),
|
|
|
|
zap.String("error", err.Error()))
|
|
|
|
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
result = append(result, estimation)
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2022-05-31 17:00:41 +00:00
|
|
|
func (b basicIncomeSettlementDeps) Balance(id user.ID) (*big.Int, error) {
|
2021-02-02 13:45:13 +00:00
|
|
|
return b.balanceClient.BalanceOf(id)
|
|
|
|
}
|
|
|
|
|
2021-01-29 07:42:40 +00:00
|
|
|
func (s *auditSettlementCalculator) ProcessAuditSettlements(epoch uint64) {
|
|
|
|
(*audit.Calculator)(s).Calculate(&audit.CalculatePrm{
|
|
|
|
Epoch: epoch,
|
|
|
|
})
|
|
|
|
}
|
2021-02-01 16:20:33 +00:00
|
|
|
|
|
|
|
func (b *basicSettlementConstructor) CreateContext(epoch uint64) (*basic.IncomeSettlementContext, error) {
|
|
|
|
return basic.NewIncomeSettlementContext(&basic.IncomeSettlementContextPrms{
|
|
|
|
Log: b.dep.log,
|
|
|
|
Epoch: epoch,
|
|
|
|
Rate: b.dep,
|
|
|
|
Estimations: b.dep,
|
2021-02-02 13:45:13 +00:00
|
|
|
Balances: b.dep,
|
2021-02-01 16:20:33 +00:00
|
|
|
Container: b.dep,
|
|
|
|
Placement: b.dep,
|
|
|
|
Exchange: b.dep,
|
2021-02-02 14:17:38 +00:00
|
|
|
Accounts: b.dep,
|
2022-05-17 13:59:46 +00:00
|
|
|
}), nil
|
2021-02-01 16:20:33 +00:00
|
|
|
}
|