Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update core API endpoints #476

Merged
merged 15 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions block.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ var (
type BlockType byte

const (
BlockTypeBasic BlockType = 1
BlockTypeValidation BlockType = 2
BlockTypeBasic BlockType = 0
BlockTypeValidation BlockType = 1
)

// EmptyBlockID returns an empty BlockID.
Expand Down
22 changes: 14 additions & 8 deletions nodeclient/apimodels/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,21 +315,27 @@ type (
ValidatorStake iotago.BaseToken `serix:"3,mapKey=validatorStake"`
// FixedCost is the fixed cost that the validator receives from the total pool reward.
FixedCost iotago.Mana `serix:"4,mapKey=fixedCost"`
// Active indicates whether the validator was active recently, and would be considered during committee selection.
Active bool `serix:"5,mapKey=active"`
// LatestSupportedProtocolVersion is the latest supported protocol version of the validator.
LatestSupportedProtocolVersion iotago.Version `serix:"5,mapKey=latestSupportedProtocolVersion"`
LatestSupportedProtocolVersion iotago.Version `serix:"6,mapKey=latestSupportedProtocolVersion"`
}

// AccountStakingListResponse defines the response for the staking REST API call.
AccountStakingListResponse struct {
Stakers []*ValidatorResponse `serix:"0,mapKey=stakers"`
// ValidatorsResponse defines the response for the staking REST API call.
ValidatorsResponse struct {
Validators []*ValidatorResponse `serix:"0,mapKey=stakers"`
PageSize uint32 `serix:"1,mapKey=pageSize"`
Cursor string `serix:"2,mapKey=cursor,omitempty"`
}

// ManaRewardsResponse defines the response for the mana rewards REST API call.
ManaRewardsResponse struct {
// EpochIndex is the epoch index for which the mana rewards are returned.
EpochIndex iotago.EpochIndex `serix:"0,mapKey=epochIndex"`
// The amount of totally available rewards the requested output may claim.
Rewards iotago.Mana `serix:"1,mapKey=rewards"`
// EpochStart is the starting epoch for the range for which the mana rewards are returned.
EpochStart iotago.EpochIndex `serix:"0,mapKey=epochIndexStart"`
// EpochEnd is the ending epoch for the range for which the mana rewards are returned, also the decay is only applied up to this point.
EpochEnd iotago.EpochIndex `serix:"1,mapKey=epochIndexEnd"`
// The amount of totally available rewards the requested output may claim, decayed up to EpochEnd (including).
Rewards iotago.Mana `serix:"2,mapKey=rewards"`
}

// CommitteeMemberResponse defines the response used in committee and staking response REST API calls.
Expand Down
17 changes: 10 additions & 7 deletions nodeclient/apimodels/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,26 +282,28 @@ func Test_CongestionResponse(t *testing.T) {
func Test_AccountStakingListResponse(t *testing.T) {
api := testAPI()

response := &apimodels.AccountStakingListResponse{
Stakers: []*apimodels.ValidatorResponse{
response := &apimodels.ValidatorsResponse{
Validators: []*apimodels.ValidatorResponse{
{
AccountID: iotago.AccountID{0xFF},
StakingEpochEnd: 0,
PoolStake: 123,
ValidatorStake: 456,
FixedCost: 69,
Active: true,
LatestSupportedProtocolVersion: 9,
},
},
Cursor: "0,1",
PageSize: 50,
}

jsonResponse, err := api.JSONEncode(response)
require.NoError(t, err)

expected := "{\"stakers\":[{\"accountId\":\"0xff00000000000000000000000000000000000000000000000000000000000000\",\"stakingEpochEnd\":\"0\",\"poolStake\":\"123\",\"validatorStake\":\"456\",\"fixedCost\":\"69\",\"latestSupportedProtocolVersion\":9}]}"
expected := "{\"stakers\":[{\"accountId\":\"0xff00000000000000000000000000000000000000000000000000000000000000\",\"stakingEpochEnd\":\"0\",\"poolStake\":\"123\",\"validatorStake\":\"456\",\"fixedCost\":\"69\",\"active\":true,\"latestSupportedProtocolVersion\":9}],\"pageSize\":50,\"cursor\":\"0,1\"}"
require.Equal(t, expected, string(jsonResponse))

decoded := new(apimodels.AccountStakingListResponse)
decoded := new(apimodels.ValidatorsResponse)
require.NoError(t, api.JSONDecode(jsonResponse, decoded))
require.EqualValues(t, response, decoded)
}
Expand All @@ -310,14 +312,15 @@ func Test_ManaRewardsResponse(t *testing.T) {
api := testAPI()

response := &apimodels.ManaRewardsResponse{
EpochIndex: 123,
EpochStart: 123,
EpochEnd: 133,
Rewards: 456,
}

jsonResponse, err := api.JSONEncode(response)
require.NoError(t, err)

expected := "{\"epochIndex\":\"123\",\"rewards\":\"456\"}"
expected := "{\"epochIndexStart\":\"123\",\"epochIndexEnd\":\"133\",\"rewards\":\"456\"}"
require.Equal(t, expected, string(jsonResponse))

decoded := new(apimodels.ManaRewardsResponse)
Expand Down
94 changes: 94 additions & 0 deletions nodeclient/http_api_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,41 @@ const (
// GET returns the node info.
RouteInfo = "/api/core/v3/info"

// RouteCongestion is the route for getting congestion details for the account.
// GET returns the congestion details for the account.
// MIMEApplicationJSON => json.
// MIMEVendorIOTASerializer => bytes.
RouteCongestion = "/api/core/v3/accounts/%s/congestion"

// RouteRewards is the route for getting the rewards for staking or delegation based on the provided output.
// Rewards are decayed up to returned epochEnd index.
// GET returns the rewards for the output.
// MIMEApplicationJSON => json.
// MIMEVendorIOTASerializer => bytes.
RouteRewards = "/api/core/v3/rewards/%s"

// RouteValidators is the route for getting the information about current registered validators.
// GET returns the paginated information about about registered validators.
// MIMEApplicationJSON => json.
// MIMEVendorIOTASerializer => bytes.
RouteValidators = "/api/core/v3/validators"

// RouteValidatorsAccount is the route for getting validator by its accountID.
// GET returns the account details.
// MIMEApplicationJSON => json.
// MIMEVendorIOTASerializer => bytes.
RouteValidatorsAccount = "/api/core/v3/validators/%s"

// RouteCommittee is the route for getting the information about the current committee.
// GET returns the information about the current committee.
// MIMEApplicationJSON => json.
// MIMEVendorIOTASerializer => bytes.
RouteCommittee = "/api/core/v3/committee"

// RouteBlockIssuance is the route for getting all needed information for block creation.
// GET returns the data needed toa attach block.
// MIMEApplicationJSON => json.
// MIMEVendorIOTASerializer => bytes.
RouteBlockIssuance = "/api/core/v3/blocks/issuance"

// RouteBlock is the route for getting a block by its ID.
Expand Down Expand Up @@ -64,6 +97,8 @@ const (

// RouteTransactionsIncludedBlockMetadata is the route for getting the block metadata that was first confirmed in the ledger for a given transaction ID.
// GET returns block metadata (including info about "promotion/reattachment needed").
// MIMEApplicationJSON => json.
// MIMEVendorIOTASerializer => bytes.
RouteTransactionsIncludedBlockMetadata = "/api/core/v3/transactions/%s/included-block/metadata"

// RouteCommitmentByID is the route for getting a commitment by its ID.
Expand Down Expand Up @@ -321,6 +356,65 @@ func (client *Client) BlockIssuance(ctx context.Context) (*apimodels.IssuanceBlo
return res, nil
}

func (client *Client) Congestion(ctx context.Context, accountID iotago.AccountID) (*apimodels.CongestionResponse, error) {
res := new(apimodels.CongestionResponse)
query := fmt.Sprintf(RouteCongestion, hexutil.EncodeHex(accountID[:]))
//nolint:bodyclose
if _, err := client.Do(ctx, http.MethodGet, query, nil, res); err != nil {
return nil, err
}

return res, nil
}

func (client *Client) Rewards(ctx context.Context, outputID iotago.OutputID) (*apimodels.ManaRewardsResponse, error) {
res := &apimodels.ManaRewardsResponse{}
query := fmt.Sprintf(RouteRewards, hexutil.EncodeHex(outputID[:]))
//nolint:bodyclose
if _, err := client.Do(ctx, http.MethodGet, query, nil, res); err != nil {
return nil, err
}

return res, nil
}

func (client *Client) Validators(ctx context.Context) (*apimodels.ValidatorsResponse, error) {
res := &apimodels.ValidatorsResponse{}
//nolint:bodyclose
if _, err := client.Do(ctx, http.MethodGet, RouteValidators, nil, res); err != nil {
return nil, err
}

return res, nil
}

func (client *Client) StakingAccount(ctx context.Context, accountID iotago.AccountID) (*apimodels.ValidatorResponse, error) {
res := &apimodels.ValidatorResponse{}
query := fmt.Sprintf(RouteValidatorsAccount, hexutil.EncodeHex(accountID[:]))
//nolint:bodyclose
if _, err := client.Do(ctx, http.MethodGet, query, nil, res); err != nil {
return nil, err
}

return res, nil
}

func (client *Client) Committee(ctx context.Context, optEpochIndex ...iotago.EpochIndex) (*apimodels.CommitteeResponse, error) {
query := RouteCommittee
if len(optEpochIndex) > 0 {
query += fmt.Sprintf("?epochIndex=%d", optEpochIndex[0])
}
fmt.Printf("query: %s\n", query)

res := &apimodels.CommitteeResponse{}
//nolint:bodyclose
if _, err := client.Do(ctx, http.MethodGet, query, nil, res); err != nil {
return nil, err
}

return res, nil
}

// NodeSupportsRoute gets the routes of the node and checks if the given route is enabled.
func (client *Client) NodeSupportsRoute(ctx context.Context, route string) (bool, error) {
routes, err := client.Routes(ctx)
Expand Down
117 changes: 117 additions & 0 deletions nodeclient/http_api_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,123 @@ func TestClient_BlockIssuance(t *testing.T) {
require.EqualValues(t, originRes, res)
}

func TestClient_Congestion(t *testing.T) {
defer gock.Off()

accID := tpkg.RandAccountID()

originRes := &apimodels.CongestionResponse{
SlotIndex: iotago.SlotIndex(20),
Ready: true,
ReferenceManaCost: iotago.Mana(1000),
BlockIssuanceCredits: iotago.BlockIssuanceCredits(1000),
}

mockGetJSON(fmt.Sprintf(nodeclient.RouteCongestion, accID.ToHex()), 200, originRes)

nodeAPI := nodeClient(t)
res, err := nodeAPI.Congestion(context.Background(), accID)
require.NoError(t, err)
require.EqualValues(t, originRes, res)
}

func TestClient_Rewards(t *testing.T) {
defer gock.Off()

outID := tpkg.RandOutputID(1)

originRes := &apimodels.ManaRewardsResponse{
EpochStart: iotago.EpochIndex(20),
EpochEnd: iotago.EpochIndex(30),
Rewards: iotago.Mana(1000),
}

mockGetJSON(fmt.Sprintf(nodeclient.RouteRewards, outID.ToHex()), 200, originRes)

nodeAPI := nodeClient(t)
res, err := nodeAPI.Rewards(context.Background(), outID)
require.NoError(t, err)
require.EqualValues(t, originRes, res)
}

func TestClient_Validators(t *testing.T) {
defer gock.Off()

originRes := &apimodels.ValidatorsResponse{Validators: []*apimodels.ValidatorResponse{
{
AccountID: tpkg.RandAccountID(),
StakingEpochEnd: iotago.EpochIndex(123),
PoolStake: iotago.BaseToken(100),
ValidatorStake: iotago.BaseToken(10),
FixedCost: iotago.Mana(10),
Active: true,
LatestSupportedProtocolVersion: 1,
},
{
AccountID: tpkg.RandAccountID(),
StakingEpochEnd: iotago.EpochIndex(124),
PoolStake: iotago.BaseToken(1000),
ValidatorStake: iotago.BaseToken(100),
FixedCost: iotago.Mana(20),
Active: true,
LatestSupportedProtocolVersion: 1,
},
}}

mockGetJSON(nodeclient.RouteValidators, 200, originRes)

nodeAPI := nodeClient(t)
res, err := nodeAPI.Validators(context.Background())
require.NoError(t, err)
require.EqualValues(t, originRes, res)
}

func TestClient_StakingByAccountID(t *testing.T) {
defer gock.Off()

accID := tpkg.RandAccountID()
originRes := &apimodels.ValidatorResponse{
AccountID: accID,
StakingEpochEnd: iotago.EpochIndex(123),
PoolStake: iotago.BaseToken(100),
ValidatorStake: iotago.BaseToken(10),
FixedCost: iotago.Mana(10),
Active: true,
LatestSupportedProtocolVersion: 1,
}

mockGetJSON(fmt.Sprintf(nodeclient.RouteValidatorsAccount, accID.ToHex()), 200, originRes)

nodeAPI := nodeClient(t)
res, err := nodeAPI.StakingAccount(context.Background(), accID)
require.NoError(t, err)
require.EqualValues(t, originRes, res)
}

func TestClient_Committee(t *testing.T) {
defer gock.Off()

originRes := &apimodels.CommitteeResponse{
EpochIndex: iotago.EpochIndex(123),
TotalStake: 1000_1000,
TotalValidatorStake: 100_000,
Committee: []*apimodels.CommitteeMemberResponse{
{
AccountID: tpkg.RandAccountID(),
PoolStake: 1000_000,
ValidatorStake: 100_000,
FixedCost: iotago.Mana(100),
},
},
}

mockGetJSON(nodeclient.RouteCommittee, 200, originRes)
nodeAPI := nodeClient(t)
res, err := nodeAPI.Committee(context.Background())
require.NoError(t, err)
require.EqualValues(t, originRes, res)
}

func TestClient_SubmitBlock(t *testing.T) {
defer gock.Off()

Expand Down
Loading