Skip to content

Commit 1bd9c85

Browse files
authored
Merge pull request #46 from kaleido-io/get-address-balance
Add AddressBalance() to FFCAPI and a new REST API route to call it
2 parents 6c78411 + 258fccd commit 1bd9c85

File tree

10 files changed

+309
-1
lines changed

10 files changed

+309
-1
lines changed

internal/tmmsgs/en_api_descriptions.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ var (
5151
APIEndpointPostEventStreamListenerReset = ffm("api.endpoints.post.eventstream.listener.reset", "Reset an event stream listener, to redeliver all events since the specified block")
5252
APIEndpointPatchEventStreamListener = ffm("api.endpoints.patch.eventstream.listener", "Update event stream listener")
5353
APIEndpointDeleteEventStreamListener = ffm("api.endpoints.delete.eventstream.listener", "Delete event stream listener")
54+
APIEndpointGetAddressBalance = ffm("api.endpoints.get.address.balance", "Get gas token balance for a signer address")
5455

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

mocks/ffcapimocks/api.go

Lines changed: 46 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apitypes/api_types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ type ReadyStatus struct {
129129
ffcapi.ReadyResponse
130130
}
131131

132+
type LiveAddressBalance struct {
133+
ffcapi.AddressBalanceResponse
134+
}
135+
132136
// CheckUpdateString helper merges supplied configuration, with a base, and applies a default if unset
133137
func CheckUpdateString(changed bool, merged **string, old *string, new *string, defValue string) bool {
134138
if new != nil {

pkg/ffcapi/address_balance.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright © 2022 Kaleido, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package ffcapi
18+
19+
import "github.com/hyperledger/firefly-common/pkg/fftypes"
20+
21+
type AddressBalanceRequest struct {
22+
Address string `json:"address"`
23+
BlockTag string `json:"blockTag"`
24+
}
25+
26+
type AddressBalanceResponse struct {
27+
Balance *fftypes.FFBigInt `json:"balance"`
28+
}

pkg/ffcapi/api.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import (
2828
// The functions follow a consistent pattern of request/response objects, to allow extensibility of the
2929
// inputs/outputs with minimal code change to existing connector implementations.
3030
type API interface {
31+
// AddressBalance gets the balance of the specified address
32+
AddressBalance(ctx context.Context, req *AddressBalanceRequest) (*AddressBalanceResponse, ErrorReason, error)
3133

3234
// BlockInfoByHash gets block information using the hash of the block
3335
BlockInfoByHash(ctx context.Context, req *BlockInfoByHashRequest) (*BlockInfoByHashResponse, ErrorReason, error)

pkg/fftm/address_management.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright © 2022 Kaleido, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package fftm
18+
19+
import (
20+
"context"
21+
22+
"github.com/hyperledger/firefly-common/pkg/log"
23+
"github.com/hyperledger/firefly-transaction-manager/pkg/apitypes"
24+
"github.com/hyperledger/firefly-transaction-manager/pkg/ffcapi"
25+
)
26+
27+
func (m *manager) getLiveBalance(ctx context.Context, address string, blockTag string) (resp *apitypes.LiveAddressBalance, err error) {
28+
resp = &apitypes.LiveAddressBalance{}
29+
balance, reason, err := m.connector.AddressBalance(ctx, &ffcapi.AddressBalanceRequest{Address: address, BlockTag: blockTag})
30+
if err == nil {
31+
resp.AddressBalanceResponse = *balance
32+
} else {
33+
log.L(ctx).Warnf("Failed to fetch live address balance: %s (reason: %s)", err, reason)
34+
return nil, err
35+
}
36+
return resp, nil
37+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright © 2022 Kaleido, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package fftm
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"testing"
23+
24+
"github.com/hyperledger/firefly-common/pkg/fftypes"
25+
"github.com/hyperledger/firefly-transaction-manager/mocks/ffcapimocks"
26+
"github.com/hyperledger/firefly-transaction-manager/pkg/ffcapi"
27+
"github.com/stretchr/testify/assert"
28+
"github.com/stretchr/testify/mock"
29+
)
30+
31+
func TestBalanceOK(t *testing.T) {
32+
33+
_, m, cancel := newTestManager(t)
34+
defer cancel()
35+
m.Start()
36+
37+
mca := m.connector.(*ffcapimocks.API)
38+
mca.On("AddressBalance", mock.Anything, mock.Anything).Return(&ffcapi.AddressBalanceResponse{Balance: fftypes.NewFFBigInt(999)}, ffcapi.ErrorReason(""), nil)
39+
40+
res, err := m.getLiveBalance(context.Background(), "0x4a8c8f1717570f9774652075e249ded38124d708", "latest")
41+
42+
assert.Nil(t, err)
43+
assert.NotNil(t, res)
44+
assert.Equal(t, int64(999), res.AddressBalanceResponse.Balance.Int64())
45+
46+
res, err = m.getLiveBalance(context.Background(), "0x4a8c8f1717570f9774652075e249ded38124d708", "")
47+
48+
assert.Nil(t, err)
49+
assert.NotNil(t, res)
50+
assert.Equal(t, int64(999), res.AddressBalanceResponse.Balance.Int64())
51+
52+
mca.AssertExpectations(t)
53+
}
54+
55+
func TestBalanceFail(t *testing.T) {
56+
57+
_, m, cancel := newTestManager(t)
58+
defer cancel()
59+
m.Start()
60+
61+
mca := m.connector.(*ffcapimocks.API)
62+
mca.On("AddressBalance", mock.Anything, mock.Anything).Return(nil, ffcapi.ErrorReason(""), fmt.Errorf("pop"))
63+
64+
res, err := m.getLiveBalance(context.Background(), "0x4a8c8f1717570f9774652075e249ded38124d708", "")
65+
66+
assert.Nil(t, res)
67+
assert.Regexp(t, "pop", err)
68+
69+
mca.AssertExpectations(t)
70+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright © 2022 Kaleido, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package fftm
18+
19+
import (
20+
"net/http"
21+
22+
"github.com/hyperledger/firefly-common/pkg/ffapi"
23+
"github.com/hyperledger/firefly-transaction-manager/internal/tmmsgs"
24+
"github.com/hyperledger/firefly-transaction-manager/pkg/apitypes"
25+
)
26+
27+
var getAddressBalance = func(m *manager) *ffapi.Route {
28+
return &ffapi.Route{
29+
Name: "getBalance",
30+
Path: "/gastoken/balances/{address}",
31+
Method: http.MethodGet,
32+
PathParams: []*ffapi.PathParam{
33+
{Name: "address", Description: tmmsgs.APIParamSignerAddress},
34+
},
35+
QueryParams: []*ffapi.QueryParam{
36+
{Name: "blocktag", Description: tmmsgs.APIParamBlocktag},
37+
},
38+
Description: tmmsgs.APIEndpointGetAddressBalance,
39+
JSONInputValue: nil,
40+
JSONOutputValue: func() interface{} { return &apitypes.LiveAddressBalance{} },
41+
JSONOutputCodes: []int{http.StatusOK},
42+
JSONHandler: func(r *ffapi.APIRequest) (output interface{}, err error) {
43+
return m.getLiveBalance(r.Req.Context(), r.PP["address"], r.QP["blocktag"])
44+
},
45+
}
46+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright © 2022 Kaleido, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package fftm
18+
19+
import (
20+
"encoding/json"
21+
"fmt"
22+
"testing"
23+
24+
"github.com/go-resty/resty/v2"
25+
"github.com/hyperledger/firefly-common/pkg/fftypes"
26+
"github.com/hyperledger/firefly-transaction-manager/mocks/ffcapimocks"
27+
"github.com/hyperledger/firefly-transaction-manager/pkg/apitypes"
28+
"github.com/hyperledger/firefly-transaction-manager/pkg/ffcapi"
29+
"github.com/stretchr/testify/assert"
30+
"github.com/stretchr/testify/mock"
31+
)
32+
33+
func TestGetAddressBalanceOK(t *testing.T) {
34+
url, m, done := newTestManager(t)
35+
defer done()
36+
37+
mfc := m.connector.(*ffcapimocks.API)
38+
39+
mfc.On("AddressBalance", mock.Anything, mock.Anything).Return(&ffcapi.AddressBalanceResponse{Balance: fftypes.NewFFBigInt(999)}, ffcapi.ErrorReason(""), nil)
40+
41+
err := m.Start()
42+
assert.NoError(t, err)
43+
44+
var liv apitypes.LiveAddressBalance
45+
res, err := resty.New().R().
46+
SetResult(&liv).
47+
Get(url + "/gastoken/balances/0x4a8c8f1717570f9774652075e249ded38124d708")
48+
assert.NoError(t, err)
49+
assert.Equal(t, 200, res.StatusCode())
50+
var responseObj fftypes.JSONObject
51+
err = json.Unmarshal(res.Body(), &responseObj)
52+
assert.NoError(t, err)
53+
assert.Equal(t, responseObj.GetString("balance"), "999")
54+
}
55+
56+
func TestGetAddressBalanceBadAddress(t *testing.T) {
57+
url, m, done := newTestManager(t)
58+
defer done()
59+
60+
mfc := m.connector.(*ffcapimocks.API)
61+
mfc.On("AddressBalance", mock.Anything, mock.Anything).Return(nil, ffcapi.ErrorReason(""), fmt.Errorf("pop"))
62+
63+
err := m.Start()
64+
assert.NoError(t, err)
65+
66+
var liv apitypes.LiveAddressBalance
67+
res, err := resty.New().R().
68+
SetResult(&liv).
69+
Get(url + "/gastoken/balances/0x4a8c8f1717570f9774652075e249ded38124d708")
70+
assert.NoError(t, err)
71+
assert.Equal(t, 500, res.StatusCode())
72+
}

pkg/fftm/routes.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,6 @@ func (m *manager) routes() []*ffapi.Route {
4646
postRootCommand(m),
4747
postSubscriptionReset(m),
4848
postSubscriptions(m),
49+
getAddressBalance(m),
4950
}
5051
}

0 commit comments

Comments
 (0)