Fantom-foundation/go-lachesis

View on GitHub
ethapi/sfc_api.go

Summary

Maintainability
B
4 hrs
Test Coverage
package ethapi

import (
    "context"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/common/hexutil"

    "github.com/Fantom-foundation/go-lachesis/inter/idx"
    "github.com/Fantom-foundation/go-lachesis/inter/sfctype"
)

// PublicSfcAPI provides an API to access SFC related information.
// It offers only methods that operate on public data that is freely available to anyone.
type PublicSfcAPI struct {
    b Backend
}

// NewPublicSfcAPI creates a new SFC protocol API.
func NewPublicSfcAPI(b Backend) *PublicSfcAPI {
    return &PublicSfcAPI{b}
}

// GetValidationScore returns staker's ValidationScore.
func (s *PublicSfcAPI) GetValidationScore(ctx context.Context, stakerID hexutil.Uint) (*hexutil.Big, error) {
    v, err := s.b.GetValidationScore(ctx, idx.StakerID(stakerID))
    if err != nil {
        return nil, err
    }
    return (*hexutil.Big)(v), err
}

// GetOriginationScore returns staker's OriginationScore.
func (s *PublicSfcAPI) GetOriginationScore(ctx context.Context, stakerID hexutil.Uint) (*hexutil.Big, error) {
    v, err := s.b.GetOriginationScore(ctx, idx.StakerID(stakerID))
    if err != nil {
        return nil, err
    }
    return (*hexutil.Big)(v), err
}

// GetRewardWeights returns staker's reward weights.
func (s *PublicSfcAPI) GetRewardWeights(ctx context.Context, stakerID hexutil.Uint) (map[string]interface{}, error) {
    baseRewardWeight, txRewardWeight, err := s.b.GetRewardWeights(ctx, idx.StakerID(stakerID))
    if err != nil {
        return nil, err
    }
    if baseRewardWeight == nil || txRewardWeight == nil {
        return nil, nil
    }
    return map[string]interface{}{
        "baseRewardWeight": (*hexutil.Big)(baseRewardWeight),
        "txRewardWeight":   (*hexutil.Big)(txRewardWeight),
    }, nil
}

// GetStakerPoI returns staker's PoI.
func (s *PublicSfcAPI) GetStakerPoI(ctx context.Context, stakerID hexutil.Uint) (*hexutil.Big, error) {
    v, err := s.b.GetStakerPoI(ctx, idx.StakerID(stakerID))
    if err != nil {
        return nil, err
    }
    return (*hexutil.Big)(v), err
}

// GetDowntime returns staker's Downtime.
func (s *PublicSfcAPI) GetDowntime(ctx context.Context, stakerID hexutil.Uint) (map[string]interface{}, error) {
    blocks, period, err := s.b.GetDowntime(ctx, idx.StakerID(stakerID))
    if err != nil {
        return nil, err
    }
    return map[string]interface{}{
        "missedBlocks": hexutil.Uint64(blocks),
        "downtime":     hexutil.Uint64(period),
    }, nil
}

// GetDelegationClaimedRewards returns sum of claimed rewards in past, by this delegation
func (s *PublicSfcAPI) GetDelegationClaimedRewards(ctx context.Context, addr common.Address, stakerID hexutil.Uint) (*hexutil.Big, error) {
    v, err := s.b.GetDelegationClaimedRewards(ctx, sfctype.DelegationID{addr, idx.StakerID(stakerID)})
    if err != nil {
        return nil, err
    }
    return (*hexutil.Big)(v), err
}

// GetStakerClaimedRewards returns sum of claimed rewards in past, by this staker
func (s *PublicSfcAPI) GetStakerClaimedRewards(ctx context.Context, stakerID hexutil.Uint64) (*hexutil.Big, error) {
    v, err := s.b.GetStakerClaimedRewards(ctx, idx.StakerID(stakerID))
    if err != nil {
        return nil, err
    }
    return (*hexutil.Big)(v), err
}

// GetStakerDelegationsClaimedRewards returns sum of claimed rewards in past, by this delegations of this staker
func (s *PublicSfcAPI) GetStakerDelegationsClaimedRewards(ctx context.Context, stakerID hexutil.Uint64) (*hexutil.Big, error) {
    v, err := s.b.GetStakerDelegationsClaimedRewards(ctx, idx.StakerID(stakerID))
    if err != nil {
        return nil, err
    }
    return (*hexutil.Big)(v), err
}

// RPCMarshalStaker converts the given staker to the RPC output .
func RPCMarshalStaker(it sfctype.SfcStakerAndID) map[string]interface{} {
    return map[string]interface{}{
        "id":               hexutil.Uint64(it.StakerID),
        "totalStake":       (*hexutil.Big)(it.Staker.CalcTotalStake()),
        "stake":            (*hexutil.Big)(it.Staker.StakeAmount),
        "delegatedMe":      (*hexutil.Big)(it.Staker.DelegatedMe),
        "isValidator":      it.Staker.IsValidator,
        "isActive":         it.Staker.Ok(),
        "isCheater":        it.Staker.IsCheater(),
        "isOffline":        it.Staker.Offline(),
        "address":          it.Staker.Address,
        "createdEpoch":     hexutil.Uint64(it.Staker.CreatedEpoch),
        "createdTime":      hexutil.Uint64(it.Staker.CreatedTime),
        "deactivatedEpoch": hexutil.Uint64(it.Staker.DeactivatedEpoch),
        "deactivatedTime":  hexutil.Uint64(it.Staker.DeactivatedTime),
    }
}

func (s *PublicSfcAPI) addStakerMetricFields(ctx context.Context, res map[string]interface{}, stakerID idx.StakerID) (map[string]interface{}, error) {
    blocks, period, err := s.b.GetDowntime(ctx, idx.StakerID(stakerID))
    if err != nil {
        return nil, err
    }
    res["missedBlocks"] = hexutil.Uint64(blocks)
    res["downtime"] = hexutil.Uint64(period)

    poi, err := s.b.GetStakerPoI(ctx, stakerID)
    if err != nil {
        return nil, err
    }
    res["poi"] = (*hexutil.Big)(poi)

    baseRewardWeight, txRewardWeight, err := s.b.GetRewardWeights(ctx, stakerID)
    if err != nil {
        return nil, err
    }
    res["baseRewardWeight"] = (*hexutil.Big)(baseRewardWeight)
    res["txRewardWeight"] = (*hexutil.Big)(txRewardWeight)

    validationScore, err := s.b.GetValidationScore(ctx, stakerID)
    if err != nil {
        return nil, err
    }
    res["validationScore"] = (*hexutil.Big)(validationScore)

    originationScore, err := s.b.GetOriginationScore(ctx, stakerID)
    if err != nil {
        return nil, err
    }
    res["originationScore"] = (*hexutil.Big)(originationScore)

    claimedRewards, err := s.b.GetStakerClaimedRewards(ctx, stakerID)
    if err != nil {
        return nil, err
    }
    res["claimedRewards"] = (*hexutil.Big)(claimedRewards)

    delegationsClaimedRewards, err := s.b.GetStakerDelegationsClaimedRewards(ctx, stakerID)
    if err != nil {
        return nil, err
    }
    res["delegationsClaimedRewards"] = (*hexutil.Big)(delegationsClaimedRewards)

    return res, nil
}

func (s *PublicSfcAPI) addDelegationMetricFields(ctx context.Context, res map[string]interface{}, id sfctype.DelegationID) (map[string]interface{}, error) {
    claimedRewards, err := s.b.GetDelegationClaimedRewards(ctx, id)
    if err != nil {
        return nil, err
    }
    res["claimedRewards"] = (*hexutil.Big)(claimedRewards)

    return res, nil
}

// GetStaker returns SFC staker's info
// Verbosity. Number. If >= 1, then include base field. If >= 2, then include metrics.
func (s *PublicSfcAPI) GetStaker(ctx context.Context, stakerID hexutil.Uint, verbosity hexutil.Uint64) (map[string]interface{}, error) {
    staker, err := s.b.GetStaker(ctx, idx.StakerID(stakerID))
    if err != nil {
        return nil, err
    }
    if staker == nil {
        return nil, nil
    }
    it := sfctype.SfcStakerAndID{
        StakerID: idx.StakerID(stakerID),
        Staker:   staker,
    }
    stakerRPC := RPCMarshalStaker(it)
    if verbosity <= 1 {
        return stakerRPC, nil
    }
    return s.addStakerMetricFields(ctx, stakerRPC, idx.StakerID(stakerID))
}

// GetStakerByAddress returns SFC staker's info by address
// Verbosity. Number. If 0, then include only stakerID. If >= 1, then include base field. If >= 2, then include metrics.
func (s *PublicSfcAPI) GetStakerByAddress(ctx context.Context, address common.Address, verbosity hexutil.Uint64) (map[string]interface{}, error) {
    stakerID, err := s.b.GetStakerID(ctx, address)
    if err != nil {
        return nil, err
    }
    if stakerID == 0 {
        return nil, nil
    }
    if verbosity == 0 {
        // ID only
        return map[string]interface{}{
            "id": hexutil.Uint64(stakerID),
        }, nil
    }
    return s.GetStaker(ctx, hexutil.Uint(stakerID), verbosity)
}

// GetStakers returns SFC stakers info
// Verbosity. Number. If 0, then include only stakerIDs. If >= 1, then include base field. If >= 2, then include metrics (including downtime if validator).
func (s *PublicSfcAPI) GetStakers(ctx context.Context, verbosity hexutil.Uint64) ([]interface{}, error) {
    stakers, err := s.b.GetStakers(ctx)
    if err != nil {
        return nil, err
    }

    if verbosity == 0 {
        // IDs only
        ids := make([]interface{}, len(stakers))
        for i, it := range stakers {
            ids[i] = hexutil.Uint64(it.StakerID).String()
        }
        return ids, nil
    }

    stakersRPC := make([]interface{}, len(stakers))
    for i, it := range stakers {
        stakerRPC := RPCMarshalStaker(it)
        if verbosity >= 2 {
            stakerRPC, err = s.addStakerMetricFields(ctx, stakerRPC, it.StakerID)
            if err != nil {
                return nil, err
            }
        }
        stakersRPC[i] = stakerRPC
    }

    return stakersRPC, err
}

// RPCMarshalDelegation converts the given delegation to the RPC output .
func RPCMarshalDelegation(it sfctype.SfcDelegationAndID) map[string]interface{} {
    return map[string]interface{}{
        "address":          it.ID.Delegator,
        "toStakerID":       hexutil.Uint64(it.ID.StakerID),
        "amount":           (*hexutil.Big)(it.Delegation.Amount),
        "createdEpoch":     hexutil.Uint64(it.Delegation.CreatedEpoch),
        "createdTime":      hexutil.Uint64(it.Delegation.CreatedTime),
        "deactivatedEpoch": hexutil.Uint64(it.Delegation.DeactivatedEpoch),
        "deactivatedTime":  hexutil.Uint64(it.Delegation.DeactivatedTime),
    }
}

// GetDelegationsOf returns SFC delegations who delegated to a staker
// Verbosity. Number. If 0, then include only addresses. If >= 1, then include base fields. If >= 2, then include metrics.
func (s *PublicSfcAPI) GetDelegationsOf(ctx context.Context, stakerID hexutil.Uint64, verbosity hexutil.Uint64) ([]interface{}, error) {
    delegations, err := s.b.GetDelegationsOf(ctx, idx.StakerID(stakerID))
    if err != nil {
        return nil, err
    }

    if verbosity == 0 {
        // Addresses only
        addresses := make([]interface{}, len(delegations))
        for i, it := range delegations {
            addresses[i] = it.ID.Delegator.String()
        }
        return addresses, nil
    }

    delegationsRPC := make([]interface{}, len(delegations))
    for i, it := range delegations {
        delegationRPC := RPCMarshalDelegation(it)
        if verbosity >= 2 {
            delegationRPC, err = s.addDelegationMetricFields(ctx, delegationRPC, it.ID)
            if err != nil {
                return nil, err
            }
        }
        delegationsRPC[i] = delegationRPC
    }

    return delegationsRPC, err
}

// GetDelegation returns SFC delegation info
// Verbosity. Number. If >= 1, then include base fields. If >= 2, then include metrics.
func (s *PublicSfcAPI) GetDelegation(ctx context.Context, addr common.Address, stakerID hexutil.Uint, verbosity hexutil.Uint64) (map[string]interface{}, error) {
    id := sfctype.DelegationID{addr, idx.StakerID(stakerID)}
    delegation, err := s.b.GetDelegation(ctx, id)
    if err != nil {
        return nil, err
    }
    if delegation == nil {
        return nil, nil
    }
    it := sfctype.SfcDelegationAndID{
        ID:         id,
        Delegation: delegation,
    }
    delegationRPC := RPCMarshalDelegation(it)
    if verbosity <= 1 {
        return delegationRPC, nil
    }
    return s.addDelegationMetricFields(ctx, delegationRPC, id)
}

// GetDelegationsByAddress returns SFC delegations by address
// Verbosity. Number. If >= 1, then include base fields. If >= 2, then include metrics.
func (s *PublicSfcAPI) GetDelegationsByAddress(ctx context.Context, addr common.Address, verbosity hexutil.Uint64) ([]interface{}, error) {
    delegations, err := s.b.GetDelegationsByAddress(ctx, addr)
    if err != nil {
        return nil, err
    }

    delegationsRPC := make([]interface{}, len(delegations))
    for i, it := range delegations {
        delegationRPC := RPCMarshalDelegation(it)
        if verbosity >= 2 {
            delegationRPC, err = s.addDelegationMetricFields(ctx, delegationRPC, it.ID)
            if err != nil {
                return nil, err
            }
        }
        delegationsRPC[i] = delegationRPC
    }

    return delegationsRPC, err
}