Skip to content

Commit 32e330e

Browse files
committed
Combine duplicate identity entries from asset owners list
1 parent 6e790d8 commit 32e330e

4 files changed

Lines changed: 151 additions & 18 deletions

File tree

api/main.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ package main
33
import (
44
"context"
55
"fmt"
6+
"log"
7+
"os"
8+
"time"
9+
610
"github.com/ardanlabs/conf"
711
"github.com/jellydator/ttlcache/v3"
812
"github.com/pkg/errors"
@@ -12,9 +16,6 @@ import (
1216
"github.com/qubic/qubic-stats-api/rpc"
1317
"go.mongodb.org/mongo-driver/mongo"
1418
"go.mongodb.org/mongo-driver/mongo/options"
15-
"log"
16-
"os"
17-
"time"
1819
)
1920

2021
const prefix = "QUBIC_STATS_API"

api/rpc/asset_service.go

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ import (
44
"cmp"
55
"context"
66
"fmt"
7+
"log"
8+
"slices"
9+
710
"github.com/jellydator/ttlcache/v3"
8-
"github.com/pkg/errors"
911
qubic "github.com/qubic/go-node-connector"
1012
"github.com/qubic/go-node-connector/types"
1113
"github.com/qubic/qubic-stats-api/protobuff"
1214
"google.golang.org/grpc/codes"
1315
"google.golang.org/grpc/status"
14-
"log"
15-
"slices"
1616
)
1717

1818
type AssetService interface {
@@ -65,7 +65,7 @@ func (s *AssetServiceImpl) GetOwnedAssets(ctx context.Context, issuerIdentity, a
6565
var owner types.Identity
6666
owner, err := owner.FromPubKey(asset.Asset.PublicKey, false)
6767
if err != nil {
68-
return nil, 0, -1, errors.Wrap(err, "failed to get identity for public key")
68+
return nil, 0, -1, fmt.Errorf("failed to get identity for public key: %w", err)
6969
}
7070

7171
assetOwnership := protobuff.AssetOwnership{
@@ -92,6 +92,10 @@ func (s *AssetServiceImpl) getAssetOwners(ctx context.Context, issuerIdentity, a
9292
if err != nil {
9393
return nil, err
9494
}
95+
queriedAssets, err = combineEntriesForSameIdentity(*queriedAssets)
96+
if err != nil {
97+
return nil, err
98+
}
9599
if len(*queriedAssets) > 0 {
96100
// we only cache queries that return data
97101
s.assetOwnerCache.Set(key, queriedAssets, ttlcache.DefaultTTL)
@@ -101,15 +105,46 @@ func (s *AssetServiceImpl) getAssetOwners(ctx context.Context, issuerIdentity, a
101105
return assets, nil
102106
}
103107

108+
// combineEntriesForSameIdentity there might be several entries for one identity and different managing contracts
109+
func combineEntriesForSameIdentity(ownerships []types.AssetOwnership) (*types.AssetOwnerships, error) {
110+
var identityMap = make(map[[32]byte]*types.AssetOwnership)
111+
112+
// combine multiple ownerships for the same identity into one
113+
for _, ownership := range ownerships {
114+
val, found := identityMap[ownership.Asset.PublicKey]
115+
if !found {
116+
identityMap[ownership.Asset.PublicKey] = &ownership
117+
} else {
118+
val.Asset.NumberOfUnits += ownership.Asset.NumberOfUnits
119+
}
120+
}
121+
122+
// create combined ownership list
123+
var combined = make(types.AssetOwnerships, 0, len(identityMap))
124+
for _, v := range identityMap {
125+
combined = append(combined, *v)
126+
}
127+
128+
slices.SortFunc(combined, func(a, b types.AssetOwnership) int {
129+
if a.Asset.NumberOfUnits > b.Asset.NumberOfUnits {
130+
return -1 // reverse sort
131+
} else {
132+
return 1
133+
}
134+
})
135+
136+
return &combined, nil
137+
}
138+
104139
func (s *AssetServiceImpl) getAssetOwnersFromNode(ctx context.Context, identity string, name string) (*types.AssetOwnerships, error) {
105140
client, err := s.qPool.Get()
106141
if err != nil {
107-
return nil, errors.Wrap(err, "getting pool connection")
142+
return nil, fmt.Errorf("getting pool connection: %w", err)
108143
}
109144
assets, err := client.GetAssetOwnershipsByFilter(ctx, identity, name, "", 0)
110145
if err != nil {
111146
_ = s.qPool.Close(client)
112-
return nil, errors.Wrap(err, "getting asset ownerships")
147+
return nil, fmt.Errorf("getting asset ownerships: %w", err)
113148
}
114149
err = s.qPool.Put(client)
115150
if err != nil {

api/rpc/asset_service_test.go

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ package rpc
33
import (
44
"context"
55
"encoding/hex"
6-
"github.com/jellydator/ttlcache/v3"
7-
qubic "github.com/qubic/go-node-connector"
8-
"github.com/qubic/go-node-connector/types"
9-
"github.com/stretchr/testify/assert"
106
"log"
117
"net"
128
"testing"
139
"time"
10+
11+
"github.com/jellydator/ttlcache/v3"
12+
qubic "github.com/qubic/go-node-connector"
13+
"github.com/qubic/go-node-connector/types"
14+
"github.com/stretchr/testify/assert"
1415
)
1516

1617
var tcpServer net.Listener
@@ -131,6 +132,101 @@ func Test_AssetService_GetOwnedAssets_ReturnPaginated(t *testing.T) {
131132

132133
}
133134

135+
func Test_AssetService_CombineOwnedAssets(t *testing.T) {
136+
137+
ownerships := types.AssetOwnerships{
138+
{
139+
Asset: types.AssetOwnershipData{
140+
PublicKey: [32]byte{7, 8, 9},
141+
Type: 3,
142+
Padding: [1]int8{},
143+
ManagingContractIndex: 0,
144+
IssuanceIndex: 0,
145+
NumberOfUnits: 1000,
146+
},
147+
Tick: 1,
148+
UniverseIndex: 1,
149+
},
150+
{
151+
Asset: types.AssetOwnershipData{
152+
PublicKey: [32]byte{1, 2, 3},
153+
Type: 3,
154+
Padding: [1]int8{},
155+
ManagingContractIndex: 1,
156+
IssuanceIndex: 1,
157+
NumberOfUnits: 1000,
158+
},
159+
Tick: 1,
160+
UniverseIndex: 1,
161+
}, {
162+
Asset: types.AssetOwnershipData{
163+
PublicKey: [32]byte{6, 6, 6},
164+
Type: 3,
165+
Padding: [1]int8{},
166+
ManagingContractIndex: 2,
167+
IssuanceIndex: 2,
168+
NumberOfUnits: 100,
169+
},
170+
Tick: 1,
171+
UniverseIndex: 2,
172+
}, {
173+
Asset: types.AssetOwnershipData{
174+
PublicKey: [32]byte{1, 2, 3},
175+
Type: 3,
176+
Padding: [1]int8{},
177+
ManagingContractIndex: 3,
178+
IssuanceIndex: 3,
179+
NumberOfUnits: 10,
180+
},
181+
Tick: 1,
182+
UniverseIndex: 3,
183+
},
184+
}
185+
186+
combined, err := combineEntriesForSameIdentity(ownerships)
187+
assert.NoError(t, err)
188+
189+
expected := types.AssetOwnerships{
190+
{
191+
Asset: types.AssetOwnershipData{
192+
PublicKey: [32]byte{1, 2, 3},
193+
Type: 3,
194+
Padding: [1]int8{},
195+
ManagingContractIndex: 1,
196+
IssuanceIndex: 1,
197+
NumberOfUnits: 1010,
198+
},
199+
Tick: 1,
200+
UniverseIndex: 1,
201+
}, {
202+
Asset: types.AssetOwnershipData{
203+
PublicKey: [32]byte{7, 8, 9},
204+
Type: 3,
205+
Padding: [1]int8{},
206+
ManagingContractIndex: 0,
207+
IssuanceIndex: 0,
208+
NumberOfUnits: 1000,
209+
},
210+
Tick: 1,
211+
UniverseIndex: 1,
212+
}, {
213+
Asset: types.AssetOwnershipData{
214+
PublicKey: [32]byte{6, 6, 6},
215+
Type: 3,
216+
Padding: [1]int8{},
217+
ManagingContractIndex: 2,
218+
IssuanceIndex: 2,
219+
NumberOfUnits: 100,
220+
},
221+
Tick: 1,
222+
UniverseIndex: 2,
223+
},
224+
}
225+
226+
assert.Equal(t, expected, *combined)
227+
228+
}
229+
134230
func setup(t *testing.T) {
135231
assetOwnersCache = ttlcache.New[string, *types.AssetOwnerships](
136232
ttlcache.WithTTL[string, *types.AssetOwnerships](10*time.Second),

api/rpc/rpc_server.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ package rpc
22

33
import (
44
"context"
5+
"log"
6+
"net"
7+
"net/http"
8+
"regexp"
9+
"strconv"
10+
511
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
612
"github.com/pkg/errors"
713
"github.com/qubic/go-node-connector/types"
@@ -17,11 +23,6 @@ import (
1723
"google.golang.org/grpc/status"
1824
"google.golang.org/protobuf/encoding/protojson"
1925
"google.golang.org/protobuf/types/known/emptypb"
20-
"log"
21-
"net"
22-
"net/http"
23-
"regexp"
24-
"strconv"
2526
)
2627

2728
type Server struct {

0 commit comments

Comments
 (0)