Skip to content

Commit

Permalink
Merge pull request hyperledger#46 from kaleido-io/get-address-balance
Browse files Browse the repository at this point in the history
Add AddressBalance() to FFCAPI and a new REST API route to call it
  • Loading branch information
matthew1001 authored Dec 20, 2022
2 parents 6c78411 + 258fccd commit 1bd9c85
Show file tree
Hide file tree
Showing 10 changed files with 309 additions and 1 deletion.
3 changes: 3 additions & 0 deletions internal/tmmsgs/en_api_descriptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ var (
APIEndpointPostEventStreamListenerReset = ffm("api.endpoints.post.eventstream.listener.reset", "Reset an event stream listener, to redeliver all events since the specified block")
APIEndpointPatchEventStreamListener = ffm("api.endpoints.patch.eventstream.listener", "Update event stream listener")
APIEndpointDeleteEventStreamListener = ffm("api.endpoints.delete.eventstream.listener", "Delete event stream listener")
APIEndpointGetAddressBalance = ffm("api.endpoints.get.address.balance", "Get gas token balance for a signer address")

APIParamStreamID = ffm("api.params.streamId", "Event Stream ID")
APIParamListenerID = ffm("api.params.listenerId", "Listener ID")
Expand All @@ -60,4 +61,6 @@ var (
APIParamTXSigner = ffm("api.params.txSigner", "Return only transactions for a specific signing address, in reverse nonce order")
APIParamTXPending = ffm("api.params.txPending", "Return only pending transactions, in reverse submission sequence (a 'sequenceId' is assigned to each transaction to determine its sequence")
APIParamSortDirection = ffm("api.params.sortDirection", "Sort direction: 'asc'/'ascending' or 'desc'/'descending'")
APIParamSignerAddress = ffm("api.params.signerAddress", "A signing address, for example to get the gas token balance for")
APIParamBlocktag = ffm("api.params.blocktag", "The optional block tag to use when making a gas token balance query")
)
47 changes: 46 additions & 1 deletion mocks/ffcapimocks/api.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pkg/apitypes/api_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ type ReadyStatus struct {
ffcapi.ReadyResponse
}

type LiveAddressBalance struct {
ffcapi.AddressBalanceResponse
}

// CheckUpdateString helper merges supplied configuration, with a base, and applies a default if unset
func CheckUpdateString(changed bool, merged **string, old *string, new *string, defValue string) bool {
if new != nil {
Expand Down
28 changes: 28 additions & 0 deletions pkg/ffcapi/address_balance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright © 2022 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// 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 ffcapi

import "github.com/hyperledger/firefly-common/pkg/fftypes"

type AddressBalanceRequest struct {
Address string `json:"address"`
BlockTag string `json:"blockTag"`
}

type AddressBalanceResponse struct {
Balance *fftypes.FFBigInt `json:"balance"`
}
2 changes: 2 additions & 0 deletions pkg/ffcapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
// The functions follow a consistent pattern of request/response objects, to allow extensibility of the
// inputs/outputs with minimal code change to existing connector implementations.
type API interface {
// AddressBalance gets the balance of the specified address
AddressBalance(ctx context.Context, req *AddressBalanceRequest) (*AddressBalanceResponse, ErrorReason, error)

// BlockInfoByHash gets block information using the hash of the block
BlockInfoByHash(ctx context.Context, req *BlockInfoByHashRequest) (*BlockInfoByHashResponse, ErrorReason, error)
Expand Down
37 changes: 37 additions & 0 deletions pkg/fftm/address_management.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright © 2022 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// 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 fftm

import (
"context"

"github.com/hyperledger/firefly-common/pkg/log"
"github.com/hyperledger/firefly-transaction-manager/pkg/apitypes"
"github.com/hyperledger/firefly-transaction-manager/pkg/ffcapi"
)

func (m *manager) getLiveBalance(ctx context.Context, address string, blockTag string) (resp *apitypes.LiveAddressBalance, err error) {
resp = &apitypes.LiveAddressBalance{}
balance, reason, err := m.connector.AddressBalance(ctx, &ffcapi.AddressBalanceRequest{Address: address, BlockTag: blockTag})
if err == nil {
resp.AddressBalanceResponse = *balance
} else {
log.L(ctx).Warnf("Failed to fetch live address balance: %s (reason: %s)", err, reason)
return nil, err
}
return resp, nil
}
70 changes: 70 additions & 0 deletions pkg/fftm/address_management_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright © 2022 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// 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 fftm

import (
"context"
"fmt"
"testing"

"github.com/hyperledger/firefly-common/pkg/fftypes"
"github.com/hyperledger/firefly-transaction-manager/mocks/ffcapimocks"
"github.com/hyperledger/firefly-transaction-manager/pkg/ffcapi"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

func TestBalanceOK(t *testing.T) {

_, m, cancel := newTestManager(t)
defer cancel()
m.Start()

mca := m.connector.(*ffcapimocks.API)
mca.On("AddressBalance", mock.Anything, mock.Anything).Return(&ffcapi.AddressBalanceResponse{Balance: fftypes.NewFFBigInt(999)}, ffcapi.ErrorReason(""), nil)

res, err := m.getLiveBalance(context.Background(), "0x4a8c8f1717570f9774652075e249ded38124d708", "latest")

assert.Nil(t, err)
assert.NotNil(t, res)
assert.Equal(t, int64(999), res.AddressBalanceResponse.Balance.Int64())

res, err = m.getLiveBalance(context.Background(), "0x4a8c8f1717570f9774652075e249ded38124d708", "")

assert.Nil(t, err)
assert.NotNil(t, res)
assert.Equal(t, int64(999), res.AddressBalanceResponse.Balance.Int64())

mca.AssertExpectations(t)
}

func TestBalanceFail(t *testing.T) {

_, m, cancel := newTestManager(t)
defer cancel()
m.Start()

mca := m.connector.(*ffcapimocks.API)
mca.On("AddressBalance", mock.Anything, mock.Anything).Return(nil, ffcapi.ErrorReason(""), fmt.Errorf("pop"))

res, err := m.getLiveBalance(context.Background(), "0x4a8c8f1717570f9774652075e249ded38124d708", "")

assert.Nil(t, res)
assert.Regexp(t, "pop", err)

mca.AssertExpectations(t)
}
46 changes: 46 additions & 0 deletions pkg/fftm/route_get_address_balance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright © 2022 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// 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 fftm

import (
"net/http"

"github.com/hyperledger/firefly-common/pkg/ffapi"
"github.com/hyperledger/firefly-transaction-manager/internal/tmmsgs"
"github.com/hyperledger/firefly-transaction-manager/pkg/apitypes"
)

var getAddressBalance = func(m *manager) *ffapi.Route {
return &ffapi.Route{
Name: "getBalance",
Path: "/gastoken/balances/{address}",
Method: http.MethodGet,
PathParams: []*ffapi.PathParam{
{Name: "address", Description: tmmsgs.APIParamSignerAddress},
},
QueryParams: []*ffapi.QueryParam{
{Name: "blocktag", Description: tmmsgs.APIParamBlocktag},
},
Description: tmmsgs.APIEndpointGetAddressBalance,
JSONInputValue: nil,
JSONOutputValue: func() interface{} { return &apitypes.LiveAddressBalance{} },
JSONOutputCodes: []int{http.StatusOK},
JSONHandler: func(r *ffapi.APIRequest) (output interface{}, err error) {
return m.getLiveBalance(r.Req.Context(), r.PP["address"], r.QP["blocktag"])
},
}
}
72 changes: 72 additions & 0 deletions pkg/fftm/route_get_address_balance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright © 2022 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// 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 fftm

import (
"encoding/json"
"fmt"
"testing"

"github.com/go-resty/resty/v2"
"github.com/hyperledger/firefly-common/pkg/fftypes"
"github.com/hyperledger/firefly-transaction-manager/mocks/ffcapimocks"
"github.com/hyperledger/firefly-transaction-manager/pkg/apitypes"
"github.com/hyperledger/firefly-transaction-manager/pkg/ffcapi"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

func TestGetAddressBalanceOK(t *testing.T) {
url, m, done := newTestManager(t)
defer done()

mfc := m.connector.(*ffcapimocks.API)

mfc.On("AddressBalance", mock.Anything, mock.Anything).Return(&ffcapi.AddressBalanceResponse{Balance: fftypes.NewFFBigInt(999)}, ffcapi.ErrorReason(""), nil)

err := m.Start()
assert.NoError(t, err)

var liv apitypes.LiveAddressBalance
res, err := resty.New().R().
SetResult(&liv).
Get(url + "/gastoken/balances/0x4a8c8f1717570f9774652075e249ded38124d708")
assert.NoError(t, err)
assert.Equal(t, 200, res.StatusCode())
var responseObj fftypes.JSONObject
err = json.Unmarshal(res.Body(), &responseObj)
assert.NoError(t, err)
assert.Equal(t, responseObj.GetString("balance"), "999")
}

func TestGetAddressBalanceBadAddress(t *testing.T) {
url, m, done := newTestManager(t)
defer done()

mfc := m.connector.(*ffcapimocks.API)
mfc.On("AddressBalance", mock.Anything, mock.Anything).Return(nil, ffcapi.ErrorReason(""), fmt.Errorf("pop"))

err := m.Start()
assert.NoError(t, err)

var liv apitypes.LiveAddressBalance
res, err := resty.New().R().
SetResult(&liv).
Get(url + "/gastoken/balances/0x4a8c8f1717570f9774652075e249ded38124d708")
assert.NoError(t, err)
assert.Equal(t, 500, res.StatusCode())
}
1 change: 1 addition & 0 deletions pkg/fftm/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,6 @@ func (m *manager) routes() []*ffapi.Route {
postRootCommand(m),
postSubscriptionReset(m),
postSubscriptions(m),
getAddressBalance(m),
}
}

0 comments on commit 1bd9c85

Please sign in to comment.