/
keeper_chain_links.go
165 lines (137 loc) · 5.99 KB
/
keeper_chain_links.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package keeper
import (
"cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/desmos-labs/desmos/v5/x/profiles/types"
)
// SaveChainLink stores the given chain link
// The first chain link of each chain for the owner will be set as default external address
// Chain links are stored using two keys:
// 1. ChainLinkStoreKey (user + chain name + target) -> types.ChainLink
// 2. ChainLinkOwnerKey (chain name + target + user) -> 0x01
func (k Keeper) SaveChainLink(ctx sdk.Context, link types.ChainLink) error {
// Validate the chain link
err := link.Validate()
if err != nil {
return errors.Wrap(types.ErrInvalidChainLink, err.Error())
}
// Make sure the user has a profile
if !k.HasProfile(ctx, link.User) {
return errors.Wrap(types.ErrProfileNotFound, "a profile is required to link a chain")
}
// Validate the source address
srcAddrData, err := types.UnpackAddressData(k.cdc, link.Address)
if err != nil {
return err
}
err = srcAddrData.Validate()
if err != nil {
return errors.Wrap(types.ErrInvalidAddressData, err.Error())
}
// Verify the proof
err = link.Proof.Verify(k.cdc, k.legacyAmino, link.User, srcAddrData)
if err != nil {
return errors.Wrap(types.ErrInvalidProof, err.Error())
}
target := srcAddrData.GetValue()
if _, found := k.GetChainLink(ctx, link.User, link.ChainConfig.Name, target); found {
return types.ErrDuplicatedChainLink
}
// Store the data
store := ctx.KVStore(k.storeKey)
store.Set(types.ChainLinksStoreKey(link.User, link.ChainConfig.Name, target), types.MustMarshalChainLink(k.cdc, link))
store.Set(types.ChainLinkOwnerKey(link.ChainConfig.Name, target, link.User), []byte{0x01})
// Set the link as default external address if there is no default external address
if !k.HasDefaultExternalAddress(ctx, link.User, link.ChainConfig.Name) {
k.SaveDefaultExternalAddress(ctx, link.User, link.ChainConfig.Name, srcAddrData.GetValue())
}
k.AfterChainLinkSaved(ctx, link)
return nil
}
// HasChainLink tells whether the given chain link exists or not
func (k Keeper) HasChainLink(ctx sdk.Context, owner, chainName, target string) bool {
store := ctx.KVStore(k.storeKey)
return store.Has(types.ChainLinksStoreKey(owner, chainName, target))
}
// GetChainLink returns the chain link for the given owner, chain name and target.
// If such link does not exist, returns false instead.
func (k Keeper) GetChainLink(ctx sdk.Context, owner, chainName, target string) (types.ChainLink, bool) {
store := ctx.KVStore(k.storeKey)
key := types.ChainLinksStoreKey(owner, chainName, target)
if !store.Has(key) {
return types.ChainLink{}, false
}
return types.MustUnmarshalChainLink(k.cdc, store.Get(key)), true
}
// DeleteChainLink deletes the link associated with the given address and chain name
func (k Keeper) DeleteChainLink(ctx sdk.Context, link types.ChainLink) {
store := ctx.KVStore(k.storeKey)
store.Delete(types.ChainLinksStoreKey(link.User, link.ChainConfig.Name, link.GetAddressData().GetValue()))
store.Delete(types.ChainLinkOwnerKey(link.ChainConfig.Name, link.GetAddressData().GetValue(), link.User))
// Update the default external address to be the oldest link if the deleted link is default exnternal address
if k.isDefaultExternalAddress(ctx, link) {
k.updateOwnerDefaultExternalAddress(ctx, link.User, link.ChainConfig.Name)
}
k.AfterChainLinkDeleted(ctx, link)
}
// DeleteAllUserChainLinks deletes all the chain links associated with the given user
func (k Keeper) DeleteAllUserChainLinks(ctx sdk.Context, user string) {
var links []types.ChainLink
k.IterateUserChainLinks(ctx, user, func(link types.ChainLink) (stop bool) {
links = append(links, link)
return false
})
for _, link := range links {
k.DeleteChainLink(ctx, link)
}
}
// getOldestUserChainLink returns the oldest chain link of the given owner associated to the given chain name.
// If such chain link does not exist, returns false instead.
func (k Keeper) getOldestUserChainLink(ctx sdk.Context, owner, chainName string) (types.ChainLink, bool) {
var oldestLink types.ChainLink
found := false
k.IterateUserChainLinksByChain(ctx, owner, chainName, func(link types.ChainLink) (stop bool) {
if !found {
oldestLink = link
found = true
return false
}
if link.CreationTime.Before(oldestLink.CreationTime) {
oldestLink = link
}
return false
})
return oldestLink, found
}
// updateOwnerDefaultExternalAddress updates the default external address of the given chain name for the given owner
// It must be performed after deleting the default external address chain link
func (k Keeper) updateOwnerDefaultExternalAddress(ctx sdk.Context, owner, chainName string) {
store := ctx.KVStore(k.storeKey)
link, found := k.getOldestUserChainLink(ctx, owner, chainName)
if !found {
// If the owner has no chain link on the given chain name, then delete the key
store.Delete(types.DefaultExternalAddressKey(owner, chainName))
return
}
srcAddrData, err := types.UnpackAddressData(k.cdc, link.Address)
if err != nil {
panic(err)
}
k.SaveDefaultExternalAddress(ctx, owner, chainName, srcAddrData.GetValue())
}
// SaveDefaultExternalAddress stores the given address as a default external address
func (k Keeper) SaveDefaultExternalAddress(ctx sdk.Context, owner, chainName, target string) {
store := ctx.KVStore(k.storeKey)
store.Set(types.DefaultExternalAddressKey(owner, chainName), []byte(target))
}
// HasDefaultExternalAddress tells whether the given owner has a default external address on the given chain name or not
func (k Keeper) HasDefaultExternalAddress(ctx sdk.Context, owner, chainName string) bool {
store := ctx.KVStore(k.storeKey)
return store.Has(types.DefaultExternalAddressKey(owner, chainName))
}
// isDefaultExternalAddress tells whether the given chain link is a default external address or not
func (k Keeper) isDefaultExternalAddress(ctx sdk.Context, link types.ChainLink) bool {
store := ctx.KVStore(k.storeKey)
addressBz := store.Get(types.DefaultExternalAddressKey(link.User, link.ChainConfig.Name))
return string(addressBz) == link.GetAddressData().GetValue()
}