Compare commits
No commits in common. "master" and "feat/multinet_err2" have entirely different histories.
master
...
feat/multi
37 changed files with 252 additions and 356 deletions
10
Makefile
10
Makefile
|
@ -270,12 +270,10 @@ env-up: all
|
|||
echo "Frostfs contracts not found"; exit 1; \
|
||||
fi
|
||||
${BIN}/frostfs-adm --config ./dev/adm/frostfs-adm.yml morph init --contracts ${FROSTFS_CONTRACTS_PATH}
|
||||
${BIN}/frostfs-adm --config ./dev/adm/frostfs-adm.yml morph refill-gas --gas 10.0 \
|
||||
--storage-wallet ./dev/storage/wallet01.json \
|
||||
--storage-wallet ./dev/storage/wallet02.json \
|
||||
--storage-wallet ./dev/storage/wallet03.json \
|
||||
--storage-wallet ./dev/storage/wallet04.json
|
||||
|
||||
${BIN}/frostfs-adm --config ./dev/adm/frostfs-adm.yml morph refill-gas --storage-wallet ./dev/storage/wallet01.json --gas 10.0
|
||||
${BIN}/frostfs-adm --config ./dev/adm/frostfs-adm.yml morph refill-gas --storage-wallet ./dev/storage/wallet02.json --gas 10.0
|
||||
${BIN}/frostfs-adm --config ./dev/adm/frostfs-adm.yml morph refill-gas --storage-wallet ./dev/storage/wallet03.json --gas 10.0
|
||||
${BIN}/frostfs-adm --config ./dev/adm/frostfs-adm.yml morph refill-gas --storage-wallet ./dev/storage/wallet04.json --gas 10.0
|
||||
@if [ ! -f "$(LOCODE_DB_PATH)" ]; then \
|
||||
make locode-download; \
|
||||
fi
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
|
@ -140,12 +141,42 @@ func addMultisigAccount(w *wallet.Wallet, m int, name, password string, pubs key
|
|||
}
|
||||
|
||||
func generateStorageCreds(cmd *cobra.Command, _ []string) error {
|
||||
walletPath, _ := cmd.Flags().GetString(commonflags.StorageWalletFlag)
|
||||
w, err := wallet.NewWallet(walletPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create wallet: %w", err)
|
||||
return refillGas(cmd, storageGasConfigFlag, true)
|
||||
}
|
||||
|
||||
func refillGas(cmd *cobra.Command, gasFlag string, createWallet bool) (err error) {
|
||||
// storage wallet path is not part of the config
|
||||
storageWalletPath, _ := cmd.Flags().GetString(commonflags.StorageWalletFlag)
|
||||
// wallet address is not part of the config
|
||||
walletAddress, _ := cmd.Flags().GetString(walletAddressFlag)
|
||||
|
||||
var gasReceiver util.Uint160
|
||||
|
||||
if len(walletAddress) != 0 {
|
||||
gasReceiver, err = address.StringToUint160(walletAddress)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid wallet address %s: %w", walletAddress, err)
|
||||
}
|
||||
} else {
|
||||
if storageWalletPath == "" {
|
||||
return fmt.Errorf("missing wallet path (use '--%s <out.json>')", commonflags.StorageWalletFlag)
|
||||
}
|
||||
|
||||
var w *wallet.Wallet
|
||||
|
||||
if createWallet {
|
||||
w, err = wallet.NewWallet(storageWalletPath)
|
||||
} else {
|
||||
w, err = wallet.NewWalletFromFile(storageWalletPath)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't create wallet: %w", err)
|
||||
}
|
||||
|
||||
if createWallet {
|
||||
var password string
|
||||
|
||||
label, _ := cmd.Flags().GetString(storageWalletLabelFlag)
|
||||
password, err := config.GetStoragePassword(viper.GetViper(), label)
|
||||
if err != nil {
|
||||
|
@ -159,10 +190,11 @@ func generateStorageCreds(cmd *cobra.Command, _ []string) error {
|
|||
if err := w.CreateAccount(label, password); err != nil {
|
||||
return fmt.Errorf("can't create account: %w", err)
|
||||
}
|
||||
return refillGas(cmd, storageGasConfigFlag, w.Accounts[0].ScriptHash())
|
||||
}
|
||||
|
||||
func refillGas(cmd *cobra.Command, gasFlag string, gasReceivers ...util.Uint160) (err error) {
|
||||
gasReceiver = w.Accounts[0].Contract.ScriptHash()
|
||||
}
|
||||
|
||||
gasStr := viper.GetString(gasFlag)
|
||||
|
||||
gasAmount, err := helper.ParseGASAmount(gasStr)
|
||||
|
@ -176,11 +208,9 @@ func refillGas(cmd *cobra.Command, gasFlag string, gasReceivers ...util.Uint160)
|
|||
}
|
||||
|
||||
bw := io.NewBufBinWriter()
|
||||
for _, gasReceiver := range gasReceivers {
|
||||
emit.AppCall(bw.BinWriter, gas.Hash, "transfer", callflag.All,
|
||||
wCtx.CommitteeAcc.Contract.ScriptHash(), gasReceiver, int64(gasAmount), nil)
|
||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||
}
|
||||
if bw.Err != nil {
|
||||
return fmt.Errorf("BUG: invalid transfer arguments: %w", bw.Err)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
package generate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
@ -38,27 +33,7 @@ var (
|
|||
_ = viper.BindPFlag(commonflags.RefillGasAmountFlag, cmd.Flags().Lookup(commonflags.RefillGasAmountFlag))
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
storageWalletPaths, _ := cmd.Flags().GetStringArray(commonflags.StorageWalletFlag)
|
||||
walletAddresses, _ := cmd.Flags().GetStringArray(walletAddressFlag)
|
||||
|
||||
var gasReceivers []util.Uint160
|
||||
for _, walletAddress := range walletAddresses {
|
||||
addr, err := address.StringToUint160(walletAddress)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid wallet address %s: %w", walletAddress, err)
|
||||
}
|
||||
|
||||
gasReceivers = append(gasReceivers, addr)
|
||||
}
|
||||
for _, storageWalletPath := range storageWalletPaths {
|
||||
w, err := wallet.NewWalletFromFile(storageWalletPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't create wallet: %w", err)
|
||||
}
|
||||
|
||||
gasReceivers = append(gasReceivers, w.Accounts[0].Contract.ScriptHash())
|
||||
}
|
||||
return refillGas(cmd, commonflags.RefillGasAmountFlag, gasReceivers...)
|
||||
return refillGas(cmd, commonflags.RefillGasAmountFlag, false)
|
||||
},
|
||||
}
|
||||
GenerateAlphabetCmd = &cobra.Command{
|
||||
|
@ -75,10 +50,10 @@ var (
|
|||
func initRefillGasCmd() {
|
||||
RefillGasCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||
RefillGasCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
RefillGasCmd.Flags().StringArray(commonflags.StorageWalletFlag, nil, "Path to storage node wallet")
|
||||
RefillGasCmd.Flags().StringArray(walletAddressFlag, nil, "Address of wallet")
|
||||
RefillGasCmd.Flags().String(commonflags.StorageWalletFlag, "", "Path to storage node wallet")
|
||||
RefillGasCmd.Flags().String(walletAddressFlag, "", "Address of wallet")
|
||||
RefillGasCmd.Flags().String(commonflags.RefillGasAmountFlag, "", "Additional amount of GAS to transfer")
|
||||
RefillGasCmd.MarkFlagsOneRequired(walletAddressFlag, commonflags.StorageWalletFlag)
|
||||
RefillGasCmd.MarkFlagsMutuallyExclusive(walletAddressFlag, commonflags.StorageWalletFlag)
|
||||
}
|
||||
|
||||
func initGenerateStorageCmd() {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||
|
@ -40,8 +41,7 @@ func depositNotary(cmd *cobra.Command, _ []string) error {
|
|||
}
|
||||
|
||||
accHash := w.GetChangeAddress()
|
||||
addr, _ := cmd.Flags().GetString(walletAccountFlag)
|
||||
if addr != "" {
|
||||
if addr, err := cmd.Flags().GetString(walletAccountFlag); err == nil {
|
||||
accHash, err = address.StringToUint160(addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid address: %s", addr)
|
||||
|
@ -73,10 +73,17 @@ func depositNotary(cmd *cobra.Command, _ []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
till, _ := cmd.Flags().GetInt64(notaryDepositTillFlag)
|
||||
if till <= 0 {
|
||||
till := int64(defaultNotaryDepositLifetime)
|
||||
tillStr, err := cmd.Flags().GetString(notaryDepositTillFlag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tillStr != "" {
|
||||
till, err = strconv.ParseInt(tillStr, 10, 64)
|
||||
if err != nil || till <= 0 {
|
||||
return errInvalidNotaryDepositLifetime
|
||||
}
|
||||
}
|
||||
|
||||
return transferGas(cmd, acc, accHash, gasAmount, till)
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ func initDepositoryNotaryCmd() {
|
|||
DepositCmd.Flags().String(commonflags.StorageWalletFlag, "", "Path to storage node wallet")
|
||||
DepositCmd.Flags().String(walletAccountFlag, "", "Wallet account address")
|
||||
DepositCmd.Flags().String(commonflags.RefillGasAmountFlag, "", "Amount of GAS to deposit")
|
||||
DepositCmd.Flags().Int64(notaryDepositTillFlag, defaultNotaryDepositLifetime, "Notary deposit duration in blocks")
|
||||
DepositCmd.Flags().String(notaryDepositTillFlag, "", "Notary deposit duration in blocks")
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -20,32 +20,23 @@ const (
|
|||
accountAddressFlag = "account"
|
||||
)
|
||||
|
||||
func parseAddresses(cmd *cobra.Command) []util.Uint160 {
|
||||
var addrs []util.Uint160
|
||||
|
||||
accs, _ := cmd.Flags().GetStringArray(accountAddressFlag)
|
||||
for _, acc := range accs {
|
||||
func addProxyAccount(cmd *cobra.Command, _ []string) {
|
||||
acc, _ := cmd.Flags().GetString(accountAddressFlag)
|
||||
addr, err := address.StringToUint160(acc)
|
||||
commonCmd.ExitOnErr(cmd, "invalid account: %w", err)
|
||||
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
func addProxyAccount(cmd *cobra.Command, _ []string) {
|
||||
addrs := parseAddresses(cmd)
|
||||
err := processAccount(cmd, addrs, "addAccount")
|
||||
err = processAccount(cmd, addr, "addAccount")
|
||||
commonCmd.ExitOnErr(cmd, "processing error: %w", err)
|
||||
}
|
||||
|
||||
func removeProxyAccount(cmd *cobra.Command, _ []string) {
|
||||
addrs := parseAddresses(cmd)
|
||||
err := processAccount(cmd, addrs, "removeAccount")
|
||||
acc, _ := cmd.Flags().GetString(accountAddressFlag)
|
||||
addr, err := address.StringToUint160(acc)
|
||||
commonCmd.ExitOnErr(cmd, "invalid account: %w", err)
|
||||
err = processAccount(cmd, addr, "removeAccount")
|
||||
commonCmd.ExitOnErr(cmd, "processing error: %w", err)
|
||||
}
|
||||
|
||||
func processAccount(cmd *cobra.Command, addrs []util.Uint160, method string) error {
|
||||
func processAccount(cmd *cobra.Command, addr util.Uint160, method string) error {
|
||||
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't initialize context: %w", err)
|
||||
|
@ -63,9 +54,7 @@ func processAccount(cmd *cobra.Command, addrs []util.Uint160, method string) err
|
|||
}
|
||||
|
||||
bw := io.NewBufBinWriter()
|
||||
for _, addr := range addrs {
|
||||
emit.AppCall(bw.BinWriter, proxyHash, method, callflag.All, addr)
|
||||
}
|
||||
|
||||
if err := wCtx.SendConsensusTx(bw.Bytes()); err != nil {
|
||||
return err
|
||||
|
|
|
@ -29,15 +29,13 @@ var (
|
|||
|
||||
func initProxyAddAccount() {
|
||||
AddAccountCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
AddAccountCmd.Flags().StringArray(accountAddressFlag, nil, "Wallet address string")
|
||||
_ = AddAccountCmd.MarkFlagRequired(accountAddressFlag)
|
||||
AddAccountCmd.Flags().String(accountAddressFlag, "", "Wallet address string")
|
||||
AddAccountCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||
}
|
||||
|
||||
func initProxyRemoveAccount() {
|
||||
RemoveAccountCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
RemoveAccountCmd.Flags().StringArray(accountAddressFlag, nil, "Wallet address string")
|
||||
_ = AddAccountCmd.MarkFlagRequired(accountAddressFlag)
|
||||
RemoveAccountCmd.Flags().String(accountAddressFlag, "", "Wallet address string")
|
||||
RemoveAccountCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ func storageConfig(cmd *cobra.Command, args []string) {
|
|||
fatalOnErr(errors.New("can't find account in wallet"))
|
||||
}
|
||||
|
||||
c.Wallet.Password, err = input.ReadPassword(fmt.Sprintf("Enter password for %s > ", c.Wallet.Account))
|
||||
c.Wallet.Password, err = input.ReadPassword(fmt.Sprintf("Account password for %s: ", c.Wallet.Account))
|
||||
fatalOnErr(err)
|
||||
|
||||
err = acc.Decrypt(c.Wallet.Password, keys.NEP2ScryptParams())
|
||||
|
|
|
@ -19,7 +19,6 @@ func initAPEManagerService(c *cfg) {
|
|||
c.cfgObject.cfgAccessPolicyEngine.policyContractHash)
|
||||
|
||||
execsvc := apemanager.New(c.cfgObject.cnrSource, contractStorage,
|
||||
c.cfgMorph.client,
|
||||
apemanager.WithLogger(c.log))
|
||||
sigsvc := apemanager.NewSignService(&c.key.PrivateKey, execsvc)
|
||||
auditSvc := apemanager.NewAuditService(sigsvc, c.log, c.audit)
|
||||
|
|
|
@ -1220,9 +1220,9 @@ func (c *cfg) updateContractNodeInfo(ctx context.Context, epoch uint64) {
|
|||
// bootstrapWithState calls "addPeer" method of the Sidechain Netmap contract
|
||||
// with the binary-encoded information from the current node's configuration.
|
||||
// The state is set using the provided setter which MUST NOT be nil.
|
||||
func (c *cfg) bootstrapWithState(ctx context.Context, state netmap.NodeState) error {
|
||||
func (c *cfg) bootstrapWithState(ctx context.Context, stateSetter func(*netmap.NodeInfo)) error {
|
||||
ni := c.cfgNodeInfo.localInfo
|
||||
ni.SetStatus(state)
|
||||
stateSetter(&ni)
|
||||
|
||||
prm := nmClient.AddPeerPrm{}
|
||||
prm.SetNodeInfo(ni)
|
||||
|
@ -1232,7 +1232,9 @@ func (c *cfg) bootstrapWithState(ctx context.Context, state netmap.NodeState) er
|
|||
|
||||
// bootstrapOnline calls cfg.bootstrapWithState with "online" state.
|
||||
func bootstrapOnline(ctx context.Context, c *cfg) error {
|
||||
return c.bootstrapWithState(ctx, netmap.Online)
|
||||
return c.bootstrapWithState(ctx, func(ni *netmap.NodeInfo) {
|
||||
ni.SetStatus(netmap.Online)
|
||||
})
|
||||
}
|
||||
|
||||
// bootstrap calls bootstrapWithState with:
|
||||
|
@ -1243,7 +1245,9 @@ func (c *cfg) bootstrap(ctx context.Context) error {
|
|||
st := c.cfgNetmap.state.controlNetmapStatus()
|
||||
if st == control.NetmapStatus_MAINTENANCE {
|
||||
c.log.Info(ctx, logs.FrostFSNodeBootstrappingWithTheMaintenanceState)
|
||||
return c.bootstrapWithState(ctx, netmap.Maintenance)
|
||||
return c.bootstrapWithState(ctx, func(ni *netmap.NodeInfo) {
|
||||
ni.SetStatus(netmap.Maintenance)
|
||||
})
|
||||
}
|
||||
|
||||
c.log.Info(ctx, logs.FrostFSNodeBootstrappingWithOnlineState,
|
||||
|
|
|
@ -6,7 +6,7 @@ const (
|
|||
subsection = "container"
|
||||
listStreamSubsection = "list_stream"
|
||||
|
||||
// ContainerBatchSizeDefault represents the maximum amount of containers to send via stream at once.
|
||||
// ContainerBatchSizeDefault represents he maximum amount of containers to send via stream at once.
|
||||
ContainerBatchSizeDefault = 1000
|
||||
)
|
||||
|
||||
|
|
|
@ -222,7 +222,6 @@ type morphContainerReader struct {
|
|||
|
||||
lister interface {
|
||||
ContainersOf(*user.ID) ([]cid.ID, error)
|
||||
IterateContainersOf(*user.ID, func(cid.ID) error) error
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,10 +237,6 @@ func (x *morphContainerReader) ContainersOf(id *user.ID) ([]cid.ID, error) {
|
|||
return x.lister.ContainersOf(id)
|
||||
}
|
||||
|
||||
func (x *morphContainerReader) IterateContainersOf(id *user.ID, processCID func(cid.ID) error) error {
|
||||
return x.lister.IterateContainersOf(id, processCID)
|
||||
}
|
||||
|
||||
type morphContainerWriter struct {
|
||||
neoClient *cntClient.Client
|
||||
}
|
||||
|
|
|
@ -151,7 +151,7 @@ func makeNotaryDeposit(ctx context.Context, c *cfg) (util.Uint256, uint32, error
|
|||
}
|
||||
|
||||
func waitNotaryDeposit(ctx context.Context, c *cfg, tx util.Uint256, vub uint32) error {
|
||||
if err := c.cfgMorph.client.WaitTxHalt(ctx, vub, tx); err != nil {
|
||||
if err := c.cfgMorph.client.WaitTxHalt(ctx, client.InvokeRes{Hash: tx, VUB: vub}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -423,7 +423,7 @@ func (c *cfg) updateNetMapState(ctx context.Context, stateSetter func(*nmClient.
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.cfgNetmap.wrapper.Morph().WaitTxHalt(ctx, res.VUB, res.Hash)
|
||||
return c.cfgNetmap.wrapper.Morph().WaitTxHalt(ctx, res)
|
||||
}
|
||||
|
||||
type netInfo struct {
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
"FROSTFS_MORPH_DIAL_TIMEOUT":"30s",
|
||||
"FROSTFS_MORPH_RPC_ENDPOINT_0_ADDRESS":"ws://127.0.0.1:30333/ws",
|
||||
"FROSTFS_MORPH_RPC_ENDPOINT_0_PRIORITY":"0",
|
||||
"FROSTFS_MORPH_INACTIVITY_TIMEOUT":"60s",
|
||||
"FROSTFS_NODE_WALLET_PATH":"${workspaceFolder}/dev/storage/wallet01.json",
|
||||
"FROSTFS_NODE_WALLET_PASSWORD":"",
|
||||
"FROSTFS_NODE_ADDRESSES":"127.0.0.1:8080",
|
||||
|
@ -97,6 +98,7 @@
|
|||
"FROSTFS_MORPH_DIAL_TIMEOUT":"30s",
|
||||
"FROSTFS_MORPH_RPC_ENDPOINT_0_ADDRESS":"ws://127.0.0.1:30333/ws",
|
||||
"FROSTFS_MORPH_RPC_ENDPOINT_0_PRIORITY":"0",
|
||||
"FROSTFS_MORPH_INACTIVITY_TIMEOUT":"60s",
|
||||
"FROSTFS_NODE_WALLET_PATH":"${workspaceFolder}/dev/storage/wallet02.json",
|
||||
"FROSTFS_NODE_WALLET_PASSWORD":"",
|
||||
"FROSTFS_NODE_ADDRESSES":"127.0.0.1:8082",
|
||||
|
@ -152,6 +154,7 @@
|
|||
"FROSTFS_MORPH_DIAL_TIMEOUT":"30s",
|
||||
"FROSTFS_MORPH_RPC_ENDPOINT_0_ADDRESS":"ws://127.0.0.1:30333/ws",
|
||||
"FROSTFS_MORPH_RPC_ENDPOINT_0_PRIORITY":"0",
|
||||
"FROSTFS_MORPH_INACTIVITY_TIMEOUT":"60s",
|
||||
"FROSTFS_NODE_WALLET_PATH":"${workspaceFolder}/dev/storage/wallet03.json",
|
||||
"FROSTFS_NODE_WALLET_PASSWORD":"",
|
||||
"FROSTFS_NODE_ADDRESSES":"127.0.0.1:8084",
|
||||
|
@ -207,6 +210,7 @@
|
|||
"FROSTFS_MORPH_DIAL_TIMEOUT":"30s",
|
||||
"FROSTFS_MORPH_RPC_ENDPOINT_0_ADDRESS":"ws://127.0.0.1:30333/ws",
|
||||
"FROSTFS_MORPH_RPC_ENDPOINT_0_PRIORITY":"0",
|
||||
"FROSTFS_MORPH_INACTIVITY_TIMEOUT":"60s",
|
||||
"FROSTFS_NODE_WALLET_PATH":"${workspaceFolder}/dev/storage/wallet04.json",
|
||||
"FROSTFS_NODE_WALLET_PASSWORD":"",
|
||||
"FROSTFS_NODE_ADDRESSES":"127.0.0.1:8086",
|
||||
|
|
32
go.mod
32
go.mod
|
@ -8,7 +8,7 @@ require (
|
|||
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0
|
||||
git.frostfs.info/TrueCloudLab/frostfs-locode-db v0.4.1-0.20240710074952-65761deb5c0d
|
||||
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20241112082307-f17779933e88
|
||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20250109084609-328d214d2d76
|
||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20241210104938-c4463df8d467
|
||||
git.frostfs.info/TrueCloudLab/hrw v1.2.1
|
||||
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972
|
||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240814080254-96225afacb88
|
||||
|
@ -27,7 +27,7 @@ require (
|
|||
github.com/klauspost/compress v1.17.4
|
||||
github.com/mailru/easyjson v0.7.7
|
||||
github.com/mr-tron/base58 v1.2.0
|
||||
github.com/multiformats/go-multiaddr v0.14.0
|
||||
github.com/multiformats/go-multiaddr v0.12.1
|
||||
github.com/nspcc-dev/neo-go v0.106.3
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/panjf2000/ants/v2 v2.9.0
|
||||
|
@ -40,15 +40,15 @@ require (
|
|||
github.com/ssgreg/journald v1.0.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
go.etcd.io/bbolt v1.3.10
|
||||
go.opentelemetry.io/otel v1.31.0
|
||||
go.opentelemetry.io/otel/trace v1.31.0
|
||||
go.opentelemetry.io/otel v1.28.0
|
||||
go.opentelemetry.io/otel/trace v1.28.0
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/sys v0.28.0
|
||||
golang.org/x/term v0.27.0
|
||||
google.golang.org/grpc v1.69.2
|
||||
google.golang.org/protobuf v1.36.1
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/sys v0.22.0
|
||||
golang.org/x/term v0.21.0
|
||||
google.golang.org/grpc v1.66.2
|
||||
google.golang.org/protobuf v1.34.2
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
|
@ -119,15 +119,15 @@ require (
|
|||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.28.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
|
||||
golang.org/x/crypto v0.24.0 // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
lukechampine.com/blake3 v1.2.1 // indirect
|
||||
rsc.io/tmplfunc v0.0.3 // indirect
|
||||
|
|
BIN
go.sum
BIN
go.sum
Binary file not shown.
|
@ -12,7 +12,6 @@ type ApplicationInfo struct {
|
|||
func NewApplicationInfo(version string) *ApplicationInfo {
|
||||
appInfo := &ApplicationInfo{
|
||||
versionValue: metrics.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Name: "app_info",
|
||||
Help: "General information about the application.",
|
||||
}, []string{"version"}),
|
||||
|
|
|
@ -31,7 +31,9 @@ type RPCActorProvider interface {
|
|||
type ProxyVerificationContractStorage struct {
|
||||
rpcActorProvider RPCActorProvider
|
||||
|
||||
cosigners []actor.SignerAccount
|
||||
acc *wallet.Account
|
||||
|
||||
proxyScriptHash util.Uint160
|
||||
|
||||
policyScriptHash util.Uint160
|
||||
}
|
||||
|
@ -39,27 +41,12 @@ type ProxyVerificationContractStorage struct {
|
|||
var _ ProxyAdaptedContractStorage = (*ProxyVerificationContractStorage)(nil)
|
||||
|
||||
func NewProxyVerificationContractStorage(rpcActorProvider RPCActorProvider, key *keys.PrivateKey, proxyScriptHash, policyScriptHash util.Uint160) *ProxyVerificationContractStorage {
|
||||
acc := wallet.NewAccountFromPrivateKey(key)
|
||||
return &ProxyVerificationContractStorage{
|
||||
rpcActorProvider: rpcActorProvider,
|
||||
|
||||
cosigners: []actor.SignerAccount{
|
||||
{
|
||||
Signer: transaction.Signer{
|
||||
Account: proxyScriptHash,
|
||||
Scopes: transaction.CustomContracts,
|
||||
AllowedContracts: []util.Uint160{policyScriptHash},
|
||||
},
|
||||
Account: notary.FakeContractAccount(proxyScriptHash),
|
||||
},
|
||||
{
|
||||
Signer: transaction.Signer{
|
||||
Account: acc.Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: acc,
|
||||
},
|
||||
},
|
||||
acc: wallet.NewAccountFromPrivateKey(key),
|
||||
|
||||
proxyScriptHash: proxyScriptHash,
|
||||
|
||||
policyScriptHash: policyScriptHash,
|
||||
}
|
||||
|
@ -77,7 +64,7 @@ func (n *contractStorageActorAdapter) GetRPCInvoker() invoker.RPCInvoke {
|
|||
|
||||
func (contractStorage *ProxyVerificationContractStorage) newContractStorageActor() (policy_morph.ContractStorageActor, error) {
|
||||
rpcActor := contractStorage.rpcActorProvider.GetRPCActor()
|
||||
act, err := actor.New(rpcActor, contractStorage.cosigners)
|
||||
act, err := actor.New(rpcActor, cosigners(contractStorage.acc, contractStorage.proxyScriptHash, contractStorage.policyScriptHash))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -111,16 +98,31 @@ func (contractStorage *ProxyVerificationContractStorage) RemoveMorphRuleChain(na
|
|||
|
||||
// ListMorphRuleChains lists morph rule chains from Policy contract using both Proxy contract and storage account as consigners.
|
||||
func (contractStorage *ProxyVerificationContractStorage) ListMorphRuleChains(name chain.Name, target engine.Target) ([]*chain.Chain, error) {
|
||||
rpcActor := contractStorage.rpcActorProvider.GetRPCActor()
|
||||
inv := &invokerAdapter{Invoker: invoker.New(rpcActor, nil), rpcInvoker: rpcActor}
|
||||
return policy_morph.NewContractStorageReader(inv, contractStorage.policyScriptHash).ListMorphRuleChains(name, target)
|
||||
// contractStorageActor is reconstructed per each method invocation because RPCActor's (that is, basically, WSClient) connection may get invalidated, but
|
||||
// ProxyVerificationContractStorage does not manage reconnections.
|
||||
contractStorageActor, err := contractStorage.newContractStorageActor()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return policy_morph.NewContractStorage(contractStorageActor, contractStorage.policyScriptHash).ListMorphRuleChains(name, target)
|
||||
}
|
||||
|
||||
type invokerAdapter struct {
|
||||
*invoker.Invoker
|
||||
rpcInvoker invoker.RPCInvoke
|
||||
func cosigners(acc *wallet.Account, proxyScriptHash, policyScriptHash util.Uint160) []actor.SignerAccount {
|
||||
return []actor.SignerAccount{
|
||||
{
|
||||
Signer: transaction.Signer{
|
||||
Account: proxyScriptHash,
|
||||
Scopes: transaction.CustomContracts,
|
||||
AllowedContracts: []util.Uint160{policyScriptHash},
|
||||
},
|
||||
Account: notary.FakeContractAccount(proxyScriptHash),
|
||||
},
|
||||
{
|
||||
Signer: transaction.Signer{
|
||||
Account: acc.Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: acc,
|
||||
},
|
||||
}
|
||||
|
||||
func (n *invokerAdapter) GetRPCInvoker() invoker.RPCInvoke {
|
||||
return n.rpcInvoker
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package meta
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
@ -226,7 +227,7 @@ func getSplitInfo(tx *bbolt.Tx, cnr cid.ID, key []byte) (*objectSDK.SplitInfo, e
|
|||
|
||||
splitInfo := objectSDK.NewSplitInfo()
|
||||
|
||||
err := splitInfo.Unmarshal(rawSplitInfo)
|
||||
err := splitInfo.Unmarshal(bytes.Clone(rawSplitInfo))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unmarshal split info from root index: %w", err)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package meta
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
@ -111,7 +112,7 @@ func (db *DB) get(tx *bbolt.Tx, addr oid.Address, key []byte, checkStatus, raw b
|
|||
// check in primary index
|
||||
data := getFromBucket(tx, primaryBucketName(cnr, bucketName), key)
|
||||
if len(data) != 0 {
|
||||
return obj, obj.Unmarshal(data)
|
||||
return obj, obj.Unmarshal(bytes.Clone(data))
|
||||
}
|
||||
|
||||
data = getFromBucket(tx, ecInfoBucketName(cnr, bucketName), key)
|
||||
|
@ -122,13 +123,13 @@ func (db *DB) get(tx *bbolt.Tx, addr oid.Address, key []byte, checkStatus, raw b
|
|||
// if not found then check in tombstone index
|
||||
data = getFromBucket(tx, tombstoneBucketName(cnr, bucketName), key)
|
||||
if len(data) != 0 {
|
||||
return obj, obj.Unmarshal(data)
|
||||
return obj, obj.Unmarshal(bytes.Clone(data))
|
||||
}
|
||||
|
||||
// if not found then check in locker index
|
||||
data = getFromBucket(tx, bucketNameLockers(cnr, bucketName), key)
|
||||
if len(data) != 0 {
|
||||
return obj, obj.Unmarshal(data)
|
||||
return obj, obj.Unmarshal(bytes.Clone(data))
|
||||
}
|
||||
|
||||
// if not found then check if object is a virtual
|
||||
|
@ -184,7 +185,7 @@ func getVirtualObject(tx *bbolt.Tx, cnr cid.ID, key []byte, raw bool) (*objectSD
|
|||
|
||||
child := objectSDK.New()
|
||||
|
||||
err = child.Unmarshal(data)
|
||||
err = child.Unmarshal(bytes.Clone(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unmarshal child with parent: %w", err)
|
||||
}
|
||||
|
@ -218,7 +219,7 @@ func getECInfoError(tx *bbolt.Tx, cnr cid.ID, data []byte) error {
|
|||
objData := getFromBucket(tx, primaryBucketName(cnr, make([]byte, bucketKeySize)), key)
|
||||
if len(objData) != 0 {
|
||||
obj := objectSDK.New()
|
||||
if err := obj.Unmarshal(objData); err != nil {
|
||||
if err := obj.Unmarshal(bytes.Clone(objData)); err != nil {
|
||||
return err
|
||||
}
|
||||
chunk := objectSDK.ECChunk{}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package meta
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
@ -129,7 +130,7 @@ func iteratePhyObjects(tx *bbolt.Tx, f func(cid.ID, oid.ID, *objectSDK.Object) e
|
|||
}
|
||||
|
||||
return b.ForEach(func(k, v []byte) error {
|
||||
if oid.Decode(k) == nil && obj.Unmarshal(v) == nil {
|
||||
if oid.Decode(k) == nil && obj.Unmarshal(bytes.Clone(v)) == nil {
|
||||
return f(cid, oid, obj)
|
||||
}
|
||||
|
||||
|
|
|
@ -87,8 +87,7 @@ type CountAliveObjectsInContainerPrm struct {
|
|||
}
|
||||
|
||||
// ListWithCursor lists physical objects available in metabase starting from
|
||||
// cursor. Includes objects of all types. Does not include inhumed and expired
|
||||
// objects.
|
||||
// cursor. Includes objects of all types. Does not include inhumed objects.
|
||||
// Use cursor value from response for consecutive requests.
|
||||
//
|
||||
// Returns ErrEndOfListing if there are no more objects to return or count
|
||||
|
@ -144,8 +143,6 @@ func (db *DB) listWithCursor(tx *bbolt.Tx, result []objectcore.Info, count int,
|
|||
|
||||
rawAddr := make([]byte, cidSize, addressKeySize)
|
||||
|
||||
currEpoch := db.epochState.CurrentEpoch()
|
||||
|
||||
loop:
|
||||
for ; name != nil; name, _ = c.Next() {
|
||||
cidRaw, prefix := parseContainerIDWithPrefix(&containerID, name)
|
||||
|
@ -170,7 +167,7 @@ loop:
|
|||
if bkt != nil {
|
||||
copy(rawAddr, cidRaw)
|
||||
result, offset, cursor, err = selectNFromBucket(bkt, objType, graveyardBkt, garbageBkt, rawAddr, containerID,
|
||||
result, count, cursor, threshold, currEpoch)
|
||||
result, count, cursor, threshold)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -215,7 +212,6 @@ func selectNFromBucket(bkt *bbolt.Bucket, // main bucket
|
|||
limit int, // stop listing at `limit` items in result
|
||||
cursor *Cursor, // start from cursor object
|
||||
threshold bool, // ignore cursor and start immediately
|
||||
currEpoch uint64,
|
||||
) ([]objectcore.Info, []byte, *Cursor, error) {
|
||||
if cursor == nil {
|
||||
cursor = new(Cursor)
|
||||
|
@ -247,19 +243,13 @@ func selectNFromBucket(bkt *bbolt.Bucket, // main bucket
|
|||
continue
|
||||
}
|
||||
|
||||
var o objectSDK.Object
|
||||
if err := o.Unmarshal(v); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
expEpoch, hasExpEpoch := hasExpirationEpoch(&o)
|
||||
if !objectLocked(bkt.Tx(), cnt, obj) && hasExpEpoch && expEpoch < currEpoch {
|
||||
continue
|
||||
}
|
||||
|
||||
var isLinkingObj bool
|
||||
var ecInfo *objectcore.ECInfo
|
||||
if objType == objectSDK.TypeRegular {
|
||||
var o objectSDK.Object
|
||||
if err := o.Unmarshal(bytes.Clone(v)); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
isLinkingObj = isLinkObject(&o)
|
||||
ecHeader := o.ECHeader()
|
||||
if ecHeader != nil {
|
||||
|
@ -423,7 +413,7 @@ func (db *DB) iterateOverObjectsInContainer(ctx context.Context, tx *bbolt.Tx, p
|
|||
var ecInfo *objectcore.ECInfo
|
||||
if prm.ObjectType == objectSDK.TypeRegular {
|
||||
var o objectSDK.Object
|
||||
if err := o.Unmarshal(v); err != nil {
|
||||
if err := o.Unmarshal(bytes.Clone(v)); err != nil {
|
||||
return err
|
||||
}
|
||||
isLinkingObj = isLinkObject(&o)
|
||||
|
|
|
@ -3,17 +3,14 @@ package meta_test
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/testutil"
|
||||
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
||||
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object"
|
||||
cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.etcd.io/bbolt"
|
||||
|
@ -74,16 +71,14 @@ func benchmarkListWithCursor(b *testing.B, db *meta.DB, batchSize int) {
|
|||
func TestLisObjectsWithCursor(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
currEpoch = 100
|
||||
expEpoch = currEpoch - 1
|
||||
containers = 5
|
||||
total = containers * 6 // regular + ts + child + lock + non-expired regular + locked expired
|
||||
)
|
||||
|
||||
db := newDB(t, meta.WithEpochState(epochState{currEpoch}))
|
||||
db := newDB(t)
|
||||
defer func() { require.NoError(t, db.Close(context.Background())) }()
|
||||
|
||||
const (
|
||||
containers = 5
|
||||
total = containers * 4 // regular + ts + child + lock
|
||||
)
|
||||
|
||||
expected := make([]object.Info, 0, total)
|
||||
|
||||
// fill metabase with objects
|
||||
|
@ -132,26 +127,6 @@ func TestLisObjectsWithCursor(t *testing.T) {
|
|||
err = putBig(db, child)
|
||||
require.NoError(t, err)
|
||||
expected = append(expected, object.Info{Address: object.AddressOf(child), Type: objectSDK.TypeRegular})
|
||||
|
||||
// add expired object (do not include into expected)
|
||||
obj = testutil.GenerateObjectWithCID(containerID)
|
||||
testutil.AddAttribute(obj, objectV2.SysAttributeExpEpoch, strconv.Itoa(expEpoch))
|
||||
require.NoError(t, metaPut(db, obj, nil))
|
||||
|
||||
// add non-expired object (include into expected)
|
||||
obj = testutil.GenerateObjectWithCID(containerID)
|
||||
testutil.AddAttribute(obj, objectV2.SysAttributeExpEpoch, strconv.Itoa(currEpoch))
|
||||
require.NoError(t, metaPut(db, obj, nil))
|
||||
expected = append(expected, object.Info{Address: object.AddressOf(obj), Type: objectSDK.TypeRegular})
|
||||
|
||||
// add locked expired object (include into expected)
|
||||
obj = testutil.GenerateObjectWithCID(containerID)
|
||||
objID := oidtest.ID()
|
||||
obj.SetID(objID)
|
||||
testutil.AddAttribute(obj, objectV2.SysAttributeExpEpoch, strconv.Itoa(expEpoch))
|
||||
require.NoError(t, metaPut(db, obj, nil))
|
||||
require.NoError(t, db.Lock(context.Background(), containerID, oidtest.ID(), []oid.ID{objID}))
|
||||
expected = append(expected, object.Info{Address: object.AddressOf(obj), Type: objectSDK.TypeRegular})
|
||||
}
|
||||
|
||||
t.Run("success with various count", func(t *testing.T) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package meta
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
@ -319,7 +320,7 @@ func updateSplitInfoIndex(tx *bbolt.Tx, objKey []byte, cnr cid.ID, bucketName []
|
|||
return si.Marshal()
|
||||
default:
|
||||
oldSI := objectSDK.NewSplitInfo()
|
||||
if err := oldSI.Unmarshal(old); err != nil {
|
||||
if err := oldSI.Unmarshal(bytes.Clone(old)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
si = util.MergeSplitInfo(si, oldSI)
|
||||
|
|
|
@ -210,7 +210,7 @@ func (c *Client) Invoke(ctx context.Context, contract util.Uint160, fee fixedn.F
|
|||
|
||||
// TestInvokeIterator invokes contract method returning an iterator and executes cb on each element.
|
||||
// If cb returns an error, the session is closed and this error is returned as-is.
|
||||
// If the remote neo-go node does not support sessions, `unwrap.ErrNoSessionID` is returned.
|
||||
// If the remove neo-go node does not support sessions, `unwrap.ErrNoSessionID` is returned.
|
||||
// batchSize is the number of items to prefetch: if the number of items in the iterator is less than batchSize, no session will be created.
|
||||
// The default batchSize is 100, the default limit from neo-go.
|
||||
func (c *Client) TestInvokeIterator(cb func(stackitem.Item) error, batchSize int, contract util.Uint160, method string, args ...interface{}) error {
|
||||
|
|
|
@ -2,7 +2,9 @@ package container
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||
|
@ -14,36 +16,27 @@ import (
|
|||
//
|
||||
// If remote RPC does not support neo-go session API, fallback to List() method.
|
||||
func (c *Client) ContainersOf(idUser *user.ID) ([]cid.ID, error) {
|
||||
var cidList []cid.ID
|
||||
var err error
|
||||
|
||||
cb := func(id cid.ID) error {
|
||||
cidList = append(cidList, id)
|
||||
return nil
|
||||
}
|
||||
if err = c.IterateContainersOf(idUser, cb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cidList, nil
|
||||
}
|
||||
|
||||
// iterateContainers iterates over a list of container identifiers
|
||||
// belonging to the specified user of FrostFS system and executes
|
||||
// `cb` on each element. If idUser is nil, calls it on the list of all containers.
|
||||
func (c *Client) IterateContainersOf(idUser *user.ID, cb func(item cid.ID) error) error {
|
||||
var rawID []byte
|
||||
|
||||
if idUser != nil {
|
||||
rawID = idUser.WalletBytes()
|
||||
}
|
||||
|
||||
itemCb := func(item stackitem.Item) error {
|
||||
id, err := getCIDfromStackItem(item)
|
||||
var cidList []cid.ID
|
||||
cb := func(item stackitem.Item) error {
|
||||
rawID, err := client.BytesFromStackItem(item)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("get byte array from stack item (%s): %w", containersOfMethod, err)
|
||||
}
|
||||
if err = cb(id); err != nil {
|
||||
return err
|
||||
|
||||
var id cid.ID
|
||||
|
||||
err = id.Decode(rawID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decode container ID: %w", err)
|
||||
}
|
||||
|
||||
cidList = append(cidList, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -57,10 +50,13 @@ func (c *Client) IterateContainersOf(idUser *user.ID, cb func(item cid.ID) error
|
|||
const batchSize = 512
|
||||
|
||||
cnrHash := c.client.ContractAddress()
|
||||
err := c.client.Morph().TestInvokeIterator(itemCb, batchSize, cnrHash, containersOfMethod, rawID)
|
||||
if err != nil && errors.Is(err, unwrap.ErrNoSessionID) {
|
||||
return c.iterate(idUser, cb)
|
||||
err := c.client.Morph().TestInvokeIterator(cb, batchSize, cnrHash, containersOfMethod, rawID)
|
||||
if err != nil {
|
||||
if errors.Is(err, unwrap.ErrNoSessionID) {
|
||||
return c.list(idUser)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return err
|
||||
return cidList, nil
|
||||
}
|
||||
|
|
|
@ -6,16 +6,15 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
)
|
||||
|
||||
// iterate iterates through a list of container identifiers belonging
|
||||
// list returns a list of container identifiers belonging
|
||||
// to the specified user of FrostFS system. The list is composed
|
||||
// through Container contract call.
|
||||
//
|
||||
// Iterates through the identifiers of all FrostFS containers if pointer
|
||||
// Returns the identifiers of all FrostFS containers if pointer
|
||||
// to user identifier is nil.
|
||||
func (c *Client) iterate(idUser *user.ID, cb func(cid.ID) error) error {
|
||||
func (c *Client) list(idUser *user.ID) ([]cid.ID, error) {
|
||||
var rawID []byte
|
||||
|
||||
if idUser != nil {
|
||||
|
@ -28,41 +27,32 @@ func (c *Client) iterate(idUser *user.ID, cb func(cid.ID) error) error {
|
|||
|
||||
res, err := c.client.TestInvoke(prm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("test invoke (%s): %w", listMethod, err)
|
||||
return nil, fmt.Errorf("test invoke (%s): %w", listMethod, err)
|
||||
} else if ln := len(res); ln != 1 {
|
||||
return fmt.Errorf("unexpected stack item count (%s): %d", listMethod, ln)
|
||||
return nil, fmt.Errorf("unexpected stack item count (%s): %d", listMethod, ln)
|
||||
}
|
||||
|
||||
res, err = client.ArrayFromStackItem(res[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("get stack item array from stack item (%s): %w", listMethod, err)
|
||||
return nil, fmt.Errorf("get stack item array from stack item (%s): %w", listMethod, err)
|
||||
}
|
||||
|
||||
cidList := make([]cid.ID, 0, len(res))
|
||||
for i := range res {
|
||||
id, err := getCIDfromStackItem(res[i])
|
||||
rawID, err := client.BytesFromStackItem(res[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = cb(id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCIDfromStackItem(item stackitem.Item) (cid.ID, error) {
|
||||
rawID, err := client.BytesFromStackItem(item)
|
||||
if err != nil {
|
||||
return cid.ID{}, fmt.Errorf("get byte array from stack item (%s): %w", listMethod, err)
|
||||
return nil, fmt.Errorf("get byte array from stack item (%s): %w", listMethod, err)
|
||||
}
|
||||
|
||||
var id cid.ID
|
||||
|
||||
err = id.Decode(rawID)
|
||||
if err != nil {
|
||||
return cid.ID{}, fmt.Errorf("decode container ID: %w", err)
|
||||
return nil, fmt.Errorf("decode container ID: %w", err)
|
||||
}
|
||||
return id, nil
|
||||
|
||||
cidList = append(cidList, id)
|
||||
}
|
||||
|
||||
return cidList, nil
|
||||
}
|
||||
|
|
|
@ -33,13 +33,13 @@ func (w *waiterClient) GetVersion() (*result.Version, error) {
|
|||
|
||||
// WaitTxHalt waits until transaction with the specified hash persists on the blockchain.
|
||||
// It also checks execution result to finish in HALT state.
|
||||
func (c *Client) WaitTxHalt(ctx context.Context, vub uint32, h util.Uint256) error {
|
||||
func (c *Client) WaitTxHalt(ctx context.Context, p InvokeRes) error {
|
||||
w, err := waiter.NewPollingBased(&waiterClient{c: c})
|
||||
if err != nil {
|
||||
return fmt.Errorf("create tx waiter: %w", err)
|
||||
}
|
||||
|
||||
res, err := w.WaitAny(ctx, vub, h)
|
||||
res, err := w.WaitAny(ctx, p.VUB, p.Hash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("wait until tx persists: %w", err)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import (
|
|||
policy_engine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
|
||||
"github.com/mr-tron/base58/base58"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -35,8 +34,6 @@ type cfg struct {
|
|||
type Service struct {
|
||||
cfg
|
||||
|
||||
waiter Waiter
|
||||
|
||||
cnrSrc containercore.Source
|
||||
|
||||
contractStorage ape_contract.ProxyAdaptedContractStorage
|
||||
|
@ -44,17 +41,11 @@ type Service struct {
|
|||
|
||||
type Option func(*cfg)
|
||||
|
||||
type Waiter interface {
|
||||
WaitTxHalt(context.Context, uint32, util.Uint256) error
|
||||
}
|
||||
|
||||
func New(cnrSrc containercore.Source, contractStorage ape_contract.ProxyAdaptedContractStorage, waiter Waiter, opts ...Option) *Service {
|
||||
func New(cnrSrc containercore.Source, contractStorage ape_contract.ProxyAdaptedContractStorage, opts ...Option) *Service {
|
||||
s := &Service{
|
||||
cnrSrc: cnrSrc,
|
||||
|
||||
contractStorage: contractStorage,
|
||||
|
||||
waiter: waiter,
|
||||
}
|
||||
|
||||
for i := range opts {
|
||||
|
@ -93,7 +84,7 @@ func (s *Service) validateContainerTargetRequest(cid string, pubKey *keys.Public
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) AddChain(ctx context.Context, req *apemanagerV2.AddChainRequest) (*apemanagerV2.AddChainResponse, error) {
|
||||
func (s *Service) AddChain(_ context.Context, req *apemanagerV2.AddChainRequest) (*apemanagerV2.AddChainResponse, error) {
|
||||
pub, err := getSignaturePublicKey(req.GetVerificationHeader())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -125,11 +116,7 @@ func (s *Service) AddChain(ctx context.Context, req *apemanagerV2.AddChainReques
|
|||
return nil, fmt.Errorf("unsupported target type: %s", targetType)
|
||||
}
|
||||
|
||||
txHash, vub, err := s.contractStorage.AddMorphRuleChain(apechain.Ingress, target, &chain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.waiter.WaitTxHalt(ctx, vub, txHash); err != nil {
|
||||
if _, _, err = s.contractStorage.AddMorphRuleChain(apechain.Ingress, target, &chain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -142,7 +129,7 @@ func (s *Service) AddChain(ctx context.Context, req *apemanagerV2.AddChainReques
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *Service) RemoveChain(ctx context.Context, req *apemanagerV2.RemoveChainRequest) (*apemanagerV2.RemoveChainResponse, error) {
|
||||
func (s *Service) RemoveChain(_ context.Context, req *apemanagerV2.RemoveChainRequest) (*apemanagerV2.RemoveChainResponse, error) {
|
||||
pub, err := getSignaturePublicKey(req.GetVerificationHeader())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -161,11 +148,7 @@ func (s *Service) RemoveChain(ctx context.Context, req *apemanagerV2.RemoveChain
|
|||
return nil, fmt.Errorf("unsupported target type: %s", targetType)
|
||||
}
|
||||
|
||||
txHash, vub, err := s.contractStorage.RemoveMorphRuleChain(apechain.Ingress, target, req.GetBody().GetChainID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.waiter.WaitTxHalt(ctx, vub, txHash); err != nil {
|
||||
if _, _, err = s.contractStorage.RemoveMorphRuleChain(apechain.Ingress, target, req.GetBody().GetChainID()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@ type Reader interface {
|
|||
// to the specified user of FrostFS system. Returns the identifiers
|
||||
// of all FrostFS containers if pointer to owner identifier is nil.
|
||||
ContainersOf(*user.ID) ([]cid.ID, error)
|
||||
IterateContainersOf(*user.ID, func(cid.ID) error) error
|
||||
}
|
||||
|
||||
// Writer is an interface of container storage updater.
|
||||
|
@ -202,7 +201,7 @@ func (s *morphExecutor) List(_ context.Context, body *container.ListRequestBody)
|
|||
return res, nil
|
||||
}
|
||||
|
||||
func (s *morphExecutor) ListStream(ctx context.Context, req *container.ListStreamRequest, stream containerSvc.ListStream) error {
|
||||
func (s *morphExecutor) ListStream(_ context.Context, req *container.ListStreamRequest, stream containerSvc.ListStream) error {
|
||||
body := req.GetBody()
|
||||
idV2 := body.GetOwnerID()
|
||||
if idV2 == nil {
|
||||
|
@ -216,41 +215,20 @@ func (s *morphExecutor) ListStream(ctx context.Context, req *container.ListStrea
|
|||
return fmt.Errorf("invalid user ID: %w", err)
|
||||
}
|
||||
|
||||
resBody := new(container.ListStreamResponseBody)
|
||||
r := new(container.ListStreamResponse)
|
||||
r.SetBody(resBody)
|
||||
|
||||
var cidList []refs.ContainerID
|
||||
|
||||
// Amount of containers to send at once.
|
||||
const batchSize = 1000
|
||||
|
||||
processCID := func(id cid.ID) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
var refID refs.ContainerID
|
||||
id.WriteToV2(&refID)
|
||||
cidList = append(cidList, refID)
|
||||
if len(cidList) == batchSize {
|
||||
r.GetBody().SetContainerIDs(cidList)
|
||||
cidList = cidList[:0]
|
||||
return stream.Send(r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = s.rdr.IterateContainersOf(&id, processCID); err != nil {
|
||||
cnrs, err := s.rdr.ContainersOf(&id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(cidList) > 0 {
|
||||
r.GetBody().SetContainerIDs(cidList)
|
||||
return stream.Send(r)
|
||||
cidList := make([]refs.ContainerID, len(cnrs))
|
||||
for i := range cnrs {
|
||||
cnrs[i].WriteToV2(&cidList[i])
|
||||
}
|
||||
|
||||
return nil
|
||||
resBody := new(container.ListStreamResponseBody)
|
||||
resBody.SetContainerIDs(cidList)
|
||||
r := new(container.ListStreamResponse)
|
||||
r.SetBody(resBody)
|
||||
|
||||
return stream.Send(r)
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/policy"
|
||||
svcutil "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/util"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object_manager/placement"
|
||||
clientSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/erasurecode"
|
||||
|
@ -275,8 +274,6 @@ func (e *ECWriter) writePart(ctx context.Context, obj *objectSDK.Object, partIdx
|
|||
err := e.putECPartToNode(ctx, obj, node)
|
||||
if err == nil {
|
||||
return nil
|
||||
} else if clientSDK.IsErrObjectAlreadyRemoved(err) {
|
||||
return err
|
||||
}
|
||||
e.Config.Logger.Warn(ctx, logs.ECFailedToSaveECPart, zap.Stringer("part_address", object.AddressOf(obj)),
|
||||
zap.Stringer("parent_address", obj.ECHeader().Parent()), zap.Int("part_index", partIdx),
|
||||
|
|
|
@ -7,11 +7,9 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
coreclient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/client"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
||||
sessionAPI "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
|
@ -33,8 +31,6 @@ type commonPrm struct {
|
|||
local bool
|
||||
|
||||
xHeaders []string
|
||||
|
||||
netmapEpoch uint64
|
||||
}
|
||||
|
||||
// SetClient sets base client for ForstFS API communication.
|
||||
|
@ -77,14 +73,6 @@ func (x *commonPrm) SetXHeaders(hs []string) {
|
|||
x.xHeaders = hs
|
||||
}
|
||||
|
||||
func (x *commonPrm) calculateXHeaders() []string {
|
||||
hs := x.xHeaders
|
||||
if x.netmapEpoch != 0 {
|
||||
hs = append(hs, sessionAPI.XHeaderNetmapEpoch, strconv.FormatUint(x.netmapEpoch, 10))
|
||||
}
|
||||
return hs
|
||||
}
|
||||
|
||||
type readPrmCommon struct {
|
||||
commonPrm
|
||||
}
|
||||
|
@ -92,8 +80,8 @@ type readPrmCommon struct {
|
|||
// SetNetmapEpoch sets the epoch number to be used to locate the objectSDK.
|
||||
//
|
||||
// By default current epoch on the server will be used.
|
||||
func (x *readPrmCommon) SetNetmapEpoch(epoch uint64) {
|
||||
x.netmapEpoch = epoch
|
||||
func (x *readPrmCommon) SetNetmapEpoch(_ uint64) {
|
||||
// FIXME(@fyrchik): https://git.frostfs.info/TrueCloudLab/frostfs-node/issues/465
|
||||
}
|
||||
|
||||
// GetObjectPrm groups parameters of GetObject operation.
|
||||
|
@ -151,7 +139,7 @@ func GetObject(ctx context.Context, prm GetObjectPrm) (*GetObjectRes, error) {
|
|||
prm.ClientParams.Session = prm.tokenSession
|
||||
}
|
||||
|
||||
prm.ClientParams.XHeaders = prm.calculateXHeaders()
|
||||
prm.ClientParams.XHeaders = prm.xHeaders
|
||||
prm.ClientParams.BearerToken = prm.tokenBearer
|
||||
prm.ClientParams.Local = prm.local
|
||||
prm.ClientParams.Key = prm.key
|
||||
|
@ -245,7 +233,7 @@ func HeadObject(ctx context.Context, prm HeadObjectPrm) (*HeadObjectRes, error)
|
|||
|
||||
prm.ClientParams.BearerToken = prm.tokenBearer
|
||||
prm.ClientParams.Local = prm.local
|
||||
prm.ClientParams.XHeaders = prm.calculateXHeaders()
|
||||
prm.ClientParams.XHeaders = prm.xHeaders
|
||||
|
||||
cliRes, err := prm.cli.ObjectHead(ctx, prm.ClientParams)
|
||||
if err == nil {
|
||||
|
@ -338,7 +326,7 @@ func PayloadRange(ctx context.Context, prm PayloadRangePrm) (*PayloadRangeRes, e
|
|||
prm.ClientParams.Session = prm.tokenSession
|
||||
}
|
||||
|
||||
prm.ClientParams.XHeaders = prm.calculateXHeaders()
|
||||
prm.ClientParams.XHeaders = prm.xHeaders
|
||||
prm.ClientParams.BearerToken = prm.tokenBearer
|
||||
prm.ClientParams.Local = prm.local
|
||||
prm.ClientParams.Length = prm.ln
|
||||
|
@ -402,7 +390,7 @@ func PutObject(ctx context.Context, prm PutObjectPrm) (*PutObjectRes, error) {
|
|||
defer span.End()
|
||||
|
||||
prmCli := client.PrmObjectPutInit{
|
||||
XHeaders: prm.calculateXHeaders(),
|
||||
XHeaders: prm.xHeaders,
|
||||
BearerToken: prm.tokenBearer,
|
||||
Session: prm.tokenSession,
|
||||
Local: true,
|
||||
|
@ -449,7 +437,7 @@ func PutObjectSingle(ctx context.Context, prm PutObjectPrm) (*PutObjectRes, erro
|
|||
}
|
||||
|
||||
prmCli := client.PrmObjectPutSingle{
|
||||
XHeaders: prm.calculateXHeaders(),
|
||||
XHeaders: prm.xHeaders,
|
||||
BearerToken: prm.tokenBearer,
|
||||
Session: prm.tokenSession,
|
||||
Local: true,
|
||||
|
@ -508,7 +496,7 @@ func SearchObjects(ctx context.Context, prm SearchObjectsPrm) (*SearchObjectsRes
|
|||
prm.cliPrm.Local = prm.local
|
||||
prm.cliPrm.Session = prm.tokenSession
|
||||
prm.cliPrm.BearerToken = prm.tokenBearer
|
||||
prm.cliPrm.XHeaders = prm.calculateXHeaders()
|
||||
prm.cliPrm.XHeaders = prm.xHeaders
|
||||
prm.cliPrm.Key = prm.key
|
||||
|
||||
rdr, err := prm.cli.ObjectSearchInit(ctx, prm.cliPrm)
|
||||
|
|
|
@ -29,7 +29,6 @@ import (
|
|||
sessionV2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/signature"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
"git.frostfs.info/TrueCloudLab/tzhash/tz"
|
||||
|
@ -352,12 +351,8 @@ func (s *Service) redirectPutSingleRequest(ctx context.Context,
|
|||
err = signature.VerifyServiceMessage(resp)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("response verification failed: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
st := apistatus.FromStatusV2(resp.GetMetaHeader().GetStatus())
|
||||
err = apistatus.ErrFromStatus(st)
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
|
|
|
@ -162,13 +162,13 @@ func (s *searchStreamMsgSizeCtrl) Send(resp *object.SearchResponse) error {
|
|||
|
||||
var newResp *object.SearchResponse
|
||||
|
||||
for {
|
||||
for ln := uint64(len(ids)); ; {
|
||||
if newResp == nil {
|
||||
newResp = new(object.SearchResponse)
|
||||
newResp.SetBody(body)
|
||||
}
|
||||
|
||||
cut := min(s.addrAmount, uint64(len(ids)))
|
||||
cut := min(s.addrAmount, ln)
|
||||
|
||||
body.SetIDList(ids[:cut])
|
||||
newResp.SetMetaHeader(resp.GetMetaHeader())
|
||||
|
|
|
@ -38,7 +38,7 @@ func (s *TokenStore) Create(_ context.Context, body *session.CreateRequestBody)
|
|||
s.mtx.Lock()
|
||||
s.tokens[key{
|
||||
tokenID: base58.Encode(uidBytes),
|
||||
ownerID: id.EncodeToString(),
|
||||
ownerID: base58.Encode(id.WalletBytes()),
|
||||
}] = storage.NewPrivateToken(&sk.PrivateKey, body.GetExpiration())
|
||||
s.mtx.Unlock()
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ func (s *TokenStore) Get(ownerID user.ID, tokenID []byte) *storage.PrivateToken
|
|||
s.mtx.RLock()
|
||||
t := s.tokens[key{
|
||||
tokenID: base58.Encode(tokenID),
|
||||
ownerID: ownerID.EncodeToString(),
|
||||
ownerID: base58.Encode(ownerID.WalletBytes()),
|
||||
}]
|
||||
s.mtx.RUnlock()
|
||||
|
||||
|
|
Loading…
Reference in a new issue