package frostfs import ( "context" "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/balance" frostfsEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/frostfs" "github.com/nspcc-dev/neo-go/pkg/util" "go.uber.org/zap" ) const ( // lockAccountLifeTime defines the amount of epochs when lock account is valid. lockAccountLifetime uint64 = 20 ) // Process deposit event by invoking a balance contract and sending native // gas in the sidechain. func (np *Processor) processDeposit(ctx context.Context, deposit frostfsEvent.Deposit) bool { if !np.alphabetState.IsAlphabet(ctx) { np.log.Info(ctx, logs.FrostFSNonAlphabetModeIgnoreDeposit) return true } prm := balance.MintPrm{} prm.SetTo(deposit.To()) prm.SetAmount(np.converter.ToBalancePrecision(deposit.Amount())) prm.SetID(deposit.ID()) // send transferX to a balance contract err := np.balanceClient.Mint(ctx, prm) if err != nil { np.log.Error(ctx, logs.FrostFSCantTransferAssetsToBalanceContract, zap.Error(err)) } curEpoch := np.epochState.EpochCounter() receiver := deposit.To() // check if the receiver has already received some mint GAS emissions // we should lock there even though LRU cache is already thread save // we lock there because GAS transfer AND cache update must be atomic np.mintEmitLock.Lock() defer np.mintEmitLock.Unlock() val, ok := np.mintEmitCache.Get(receiver.String()) if ok && val+np.mintEmitThreshold >= curEpoch { np.log.Warn(ctx, logs.FrostFSDoubleMintEmissionDeclined, zap.Stringer("receiver", receiver), zap.Uint64("last_emission", val), zap.Uint64("current_epoch", curEpoch)) return false } // get gas balance of the node // before gas transfer check if the balance is greater than the threshold balance, err := np.morphClient.GasBalance() if err != nil { np.log.Error(ctx, logs.FrostFSCantGetGasBalanceOfTheNode, zap.Error(err)) return false } if balance < np.gasBalanceThreshold { np.log.Warn(ctx, logs.FrostFSGasBalanceThresholdHasBeenReached, zap.Int64("balance", balance), zap.Int64("threshold", np.gasBalanceThreshold)) return false } err = np.morphClient.TransferGas(receiver, np.mintEmitValue) if err != nil { np.log.Error(ctx, logs.FrostFSCantTransferNativeGasToReceiver, zap.String("error", err.Error())) return false } np.mintEmitCache.Add(receiver.String(), curEpoch) return true } // Process withdraw event by locking assets in the balance account. func (np *Processor) processWithdraw(ctx context.Context, withdraw frostfsEvent.Withdraw) bool { if !np.alphabetState.IsAlphabet(ctx) { np.log.Info(ctx, logs.FrostFSNonAlphabetModeIgnoreWithdraw) return true } // create lock account lock, err := util.Uint160DecodeBytesBE(withdraw.ID()[:util.Uint160Size]) if err != nil { np.log.Error(ctx, logs.FrostFSCantCreateLockAccount, zap.Error(err)) return false } curEpoch := np.epochState.EpochCounter() prm := balance.LockPrm{} prm.SetID(withdraw.ID()) prm.SetUser(withdraw.User()) prm.SetLock(lock) prm.SetAmount(np.converter.ToBalancePrecision(withdraw.Amount())) prm.SetDueEpoch(int64(curEpoch + lockAccountLifetime)) err = np.balanceClient.Lock(ctx, prm) if err != nil { np.log.Error(ctx, logs.FrostFSCantLockAssetsForWithdraw, zap.Error(err)) return false } return true } // Process cheque event by transferring assets from the lock account back to // the reserve account. func (np *Processor) processCheque(ctx context.Context, cheque frostfsEvent.Cheque) bool { if !np.alphabetState.IsAlphabet(ctx) { np.log.Info(ctx, logs.FrostFSNonAlphabetModeIgnoreCheque) return true } prm := balance.BurnPrm{} prm.SetTo(cheque.LockAccount()) prm.SetAmount(np.converter.ToBalancePrecision(cheque.Amount())) prm.SetID(cheque.ID()) err := np.balanceClient.Burn(ctx, prm) if err != nil { np.log.Error(ctx, logs.FrostFSCantTransferAssetsToFedContract, zap.Error(err)) return false } return true }