mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-26 19:42:23 +00:00
33c971b0e4
Make the contracts cache initialization unified. The order of cache iniitialization is not important and Nottary contract is added to the bc.contracts.Contracts wrt P2PSigExtensions setting, thus no functional changes, just refactoring for future applications. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
493 lines
17 KiB
Go
493 lines
17 KiB
Go
package native
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"math/big"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativeprices"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
)
|
|
|
|
// Notary represents Notary native contract.
|
|
type Notary struct {
|
|
interop.ContractMD
|
|
GAS *GAS
|
|
NEO *NEO
|
|
Desig *Designate
|
|
}
|
|
|
|
type NotaryCache struct {
|
|
maxNotValidBeforeDelta uint32
|
|
notaryServiceFeePerKey int64
|
|
}
|
|
|
|
// NotaryService is a Notary module interface.
|
|
type NotaryService interface {
|
|
UpdateNotaryNodes(pubs keys.PublicKeys)
|
|
}
|
|
|
|
const (
|
|
notaryContractID = -10
|
|
// prefixDeposit is a prefix for storing Notary deposits.
|
|
prefixDeposit = 1
|
|
defaultDepositDeltaTill = 5760
|
|
defaultMaxNotValidBeforeDelta = 140 // 20 rounds for 7 validators, a little more than half an hour
|
|
defaultNotaryServiceFeePerKey = 1000_0000 // 0.1 GAS
|
|
maxNotaryServiceFeePerKey = 1_0000_0000 // 1 GAS
|
|
)
|
|
|
|
var (
|
|
maxNotValidBeforeDeltaKey = []byte{10}
|
|
notaryServiceFeeKey = []byte{5}
|
|
)
|
|
|
|
var (
|
|
_ interop.Contract = (*Notary)(nil)
|
|
_ dao.NativeContractCache = (*NotaryCache)(nil)
|
|
)
|
|
|
|
// Copy implements NativeContractCache interface.
|
|
func (c *NotaryCache) Copy() dao.NativeContractCache {
|
|
cp := &NotaryCache{}
|
|
copyNotaryCache(c, cp)
|
|
return cp
|
|
}
|
|
|
|
func copyNotaryCache(src, dst *NotaryCache) {
|
|
*dst = *src
|
|
}
|
|
|
|
// newNotary returns Notary native contract.
|
|
func newNotary() *Notary {
|
|
n := &Notary{ContractMD: *interop.NewContractMD(nativenames.Notary, notaryContractID)}
|
|
defer n.UpdateHash()
|
|
|
|
desc := newDescriptor("onNEP17Payment", smartcontract.VoidType,
|
|
manifest.NewParameter("from", smartcontract.Hash160Type),
|
|
manifest.NewParameter("amount", smartcontract.IntegerType),
|
|
manifest.NewParameter("data", smartcontract.AnyType))
|
|
md := newMethodAndPrice(n.onPayment, 1<<15, callflag.States)
|
|
n.AddMethod(md, desc)
|
|
|
|
desc = newDescriptor("lockDepositUntil", smartcontract.BoolType,
|
|
manifest.NewParameter("address", smartcontract.Hash160Type),
|
|
manifest.NewParameter("till", smartcontract.IntegerType))
|
|
md = newMethodAndPrice(n.lockDepositUntil, 1<<15, callflag.States)
|
|
n.AddMethod(md, desc)
|
|
|
|
desc = newDescriptor("withdraw", smartcontract.BoolType,
|
|
manifest.NewParameter("from", smartcontract.Hash160Type),
|
|
manifest.NewParameter("to", smartcontract.Hash160Type))
|
|
md = newMethodAndPrice(n.withdraw, 1<<15, callflag.All)
|
|
n.AddMethod(md, desc)
|
|
|
|
desc = newDescriptor("balanceOf", smartcontract.IntegerType,
|
|
manifest.NewParameter("addr", smartcontract.Hash160Type))
|
|
md = newMethodAndPrice(n.balanceOf, 1<<15, callflag.ReadStates)
|
|
n.AddMethod(md, desc)
|
|
|
|
desc = newDescriptor("expirationOf", smartcontract.IntegerType,
|
|
manifest.NewParameter("addr", smartcontract.Hash160Type))
|
|
md = newMethodAndPrice(n.expirationOf, 1<<15, callflag.ReadStates)
|
|
n.AddMethod(md, desc)
|
|
|
|
desc = newDescriptor("verify", smartcontract.BoolType,
|
|
manifest.NewParameter("signature", smartcontract.SignatureType))
|
|
md = newMethodAndPrice(n.verify, nativeprices.NotaryVerificationPrice, callflag.ReadStates)
|
|
n.AddMethod(md, desc)
|
|
|
|
desc = newDescriptor("getMaxNotValidBeforeDelta", smartcontract.IntegerType)
|
|
md = newMethodAndPrice(n.getMaxNotValidBeforeDelta, 1<<15, callflag.ReadStates)
|
|
n.AddMethod(md, desc)
|
|
|
|
desc = newDescriptor("setMaxNotValidBeforeDelta", smartcontract.VoidType,
|
|
manifest.NewParameter("value", smartcontract.IntegerType))
|
|
md = newMethodAndPrice(n.setMaxNotValidBeforeDelta, 1<<15, callflag.States)
|
|
n.AddMethod(md, desc)
|
|
|
|
desc = newDescriptor("getNotaryServiceFeePerKey", smartcontract.IntegerType)
|
|
md = newMethodAndPrice(n.getNotaryServiceFeePerKey, 1<<15, callflag.ReadStates)
|
|
n.AddMethod(md, desc)
|
|
|
|
desc = newDescriptor("setNotaryServiceFeePerKey", smartcontract.VoidType,
|
|
manifest.NewParameter("value", smartcontract.IntegerType))
|
|
md = newMethodAndPrice(n.setNotaryServiceFeePerKey, 1<<15, callflag.States)
|
|
n.AddMethod(md, desc)
|
|
|
|
return n
|
|
}
|
|
|
|
// Metadata implements the Contract interface.
|
|
func (n *Notary) Metadata() *interop.ContractMD {
|
|
return &n.ContractMD
|
|
}
|
|
|
|
// Initialize initializes Notary native contract and implements the Contract interface.
|
|
func (n *Notary) Initialize(ic *interop.Context) error {
|
|
setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, defaultMaxNotValidBeforeDelta)
|
|
setIntWithKey(n.ID, ic.DAO, notaryServiceFeeKey, defaultNotaryServiceFeePerKey)
|
|
|
|
cache := &NotaryCache{
|
|
maxNotValidBeforeDelta: defaultMaxNotValidBeforeDelta,
|
|
notaryServiceFeePerKey: defaultNotaryServiceFeePerKey,
|
|
}
|
|
ic.DAO.SetCache(n.ID, cache)
|
|
return nil
|
|
}
|
|
|
|
func (n *Notary) InitializeCache(blockHeight uint32, d *dao.Simple) error {
|
|
cache := &NotaryCache{
|
|
maxNotValidBeforeDelta: uint32(getIntWithKey(n.ID, d, maxNotValidBeforeDeltaKey)),
|
|
notaryServiceFeePerKey: getIntWithKey(n.ID, d, notaryServiceFeeKey),
|
|
}
|
|
|
|
d.SetCache(n.ID, cache)
|
|
return nil
|
|
}
|
|
|
|
// OnPersist implements the Contract interface.
|
|
func (n *Notary) OnPersist(ic *interop.Context) error {
|
|
var (
|
|
nFees int64
|
|
notaries keys.PublicKeys
|
|
err error
|
|
)
|
|
for _, tx := range ic.Block.Transactions {
|
|
if tx.HasAttribute(transaction.NotaryAssistedT) {
|
|
if notaries == nil {
|
|
notaries, err = n.GetNotaryNodes(ic.DAO)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get notary nodes: %w", err)
|
|
}
|
|
}
|
|
nKeys := tx.GetAttributes(transaction.NotaryAssistedT)[0].Value.(*transaction.NotaryAssisted).NKeys
|
|
nFees += int64(nKeys) + 1
|
|
if tx.Sender() == n.Hash {
|
|
payer := tx.Signers[1]
|
|
balance := n.GetDepositFor(ic.DAO, payer.Account)
|
|
balance.Amount.Sub(balance.Amount, big.NewInt(tx.SystemFee+tx.NetworkFee))
|
|
if balance.Amount.Sign() == 0 {
|
|
n.removeDepositFor(ic.DAO, payer.Account)
|
|
} else {
|
|
err := n.putDepositFor(ic.DAO, balance, payer.Account)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update deposit for %s: %w", payer.Account.StringBE(), err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if nFees == 0 {
|
|
return nil
|
|
}
|
|
feePerKey := n.GetNotaryServiceFeePerKey(ic.DAO)
|
|
singleReward := calculateNotaryReward(nFees, feePerKey, len(notaries))
|
|
for _, notary := range notaries {
|
|
n.GAS.mint(ic, notary.GetScriptHash(), singleReward, false)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// PostPersist implements the Contract interface.
|
|
func (n *Notary) PostPersist(ic *interop.Context) error {
|
|
return nil
|
|
}
|
|
|
|
// onPayment records the deposited amount as belonging to "from" address with a lock
|
|
// till the specified chain's height.
|
|
func (n *Notary) onPayment(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
if h := ic.VM.GetCallingScriptHash(); h != n.GAS.Hash {
|
|
panic(fmt.Errorf("only GAS can be accepted for deposit, got %s", h.StringBE()))
|
|
}
|
|
from := toUint160(args[0])
|
|
to := from
|
|
amount := toBigInt(args[1])
|
|
data, ok := args[2].(*stackitem.Array)
|
|
if !ok || len(data.Value().([]stackitem.Item)) != 2 {
|
|
panic(errors.New("`data` parameter should be an array of 2 elements"))
|
|
}
|
|
additionalParams := data.Value().([]stackitem.Item)
|
|
if !additionalParams[0].Equals(stackitem.Null{}) {
|
|
to = toUint160(additionalParams[0])
|
|
}
|
|
|
|
allowedChangeTill := ic.Tx.Sender() == to
|
|
currentHeight := ic.BlockHeight()
|
|
deposit := n.GetDepositFor(ic.DAO, to)
|
|
till := toUint32(additionalParams[1])
|
|
if till < currentHeight {
|
|
panic(fmt.Errorf("`till` shouldn't be less then the chain's height %d", currentHeight))
|
|
}
|
|
if deposit != nil && till < deposit.Till {
|
|
panic(fmt.Errorf("`till` shouldn't be less then the previous value %d", deposit.Till))
|
|
}
|
|
feePerKey := n.GetNotaryServiceFeePerKey(ic.DAO)
|
|
if deposit == nil {
|
|
if amount.Cmp(big.NewInt(2*feePerKey)) < 0 {
|
|
panic(fmt.Errorf("first deposit can not be less then %d, got %d", 2*feePerKey, amount.Int64()))
|
|
}
|
|
deposit = &state.Deposit{
|
|
Amount: new(big.Int),
|
|
}
|
|
if !allowedChangeTill {
|
|
till = currentHeight + defaultDepositDeltaTill
|
|
}
|
|
} else if !allowedChangeTill { // only deposit's owner is allowed to set or update `till`
|
|
till = deposit.Till
|
|
}
|
|
deposit.Amount.Add(deposit.Amount, amount)
|
|
deposit.Till = till
|
|
|
|
if err := n.putDepositFor(ic.DAO, deposit, to); err != nil {
|
|
panic(fmt.Errorf("failed to put deposit for %s into the storage: %w", from.StringBE(), err))
|
|
}
|
|
return stackitem.Null{}
|
|
}
|
|
|
|
// lockDepositUntil updates the chain's height until which the deposit is locked.
|
|
func (n *Notary) lockDepositUntil(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
addr := toUint160(args[0])
|
|
ok, err := runtime.CheckHashedWitness(ic, addr)
|
|
if err != nil {
|
|
panic(fmt.Errorf("failed to check witness for %s: %w", addr.StringBE(), err))
|
|
}
|
|
if !ok {
|
|
return stackitem.NewBool(false)
|
|
}
|
|
till := toUint32(args[1])
|
|
if till < ic.BlockHeight() {
|
|
return stackitem.NewBool(false)
|
|
}
|
|
deposit := n.GetDepositFor(ic.DAO, addr)
|
|
if deposit == nil {
|
|
return stackitem.NewBool(false)
|
|
}
|
|
if till < deposit.Till {
|
|
return stackitem.NewBool(false)
|
|
}
|
|
deposit.Till = till
|
|
err = n.putDepositFor(ic.DAO, deposit, addr)
|
|
if err != nil {
|
|
panic(fmt.Errorf("failed to put deposit for %s into the storage: %w", addr.StringBE(), err))
|
|
}
|
|
return stackitem.NewBool(true)
|
|
}
|
|
|
|
// withdraw sends all deposited GAS for "from" address to "to" address.
|
|
func (n *Notary) withdraw(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
from := toUint160(args[0])
|
|
ok, err := runtime.CheckHashedWitness(ic, from)
|
|
if err != nil {
|
|
panic(fmt.Errorf("failed to check witness for %s: %w", from.StringBE(), err))
|
|
}
|
|
if !ok {
|
|
return stackitem.NewBool(false)
|
|
}
|
|
to := from
|
|
if !args[1].Equals(stackitem.Null{}) {
|
|
to = toUint160(args[1])
|
|
}
|
|
deposit := n.GetDepositFor(ic.DAO, from)
|
|
if deposit == nil {
|
|
return stackitem.NewBool(false)
|
|
}
|
|
if ic.BlockHeight() < deposit.Till {
|
|
return stackitem.NewBool(false)
|
|
}
|
|
cs, err := ic.GetContract(n.GAS.Hash)
|
|
if err != nil {
|
|
panic(fmt.Errorf("failed to get GAS contract state: %w", err))
|
|
}
|
|
transferArgs := []stackitem.Item{stackitem.NewByteArray(n.Hash.BytesBE()), stackitem.NewByteArray(to.BytesBE()), stackitem.NewBigInteger(deposit.Amount), stackitem.Null{}}
|
|
err = contract.CallFromNative(ic, n.Hash, cs, "transfer", transferArgs, true)
|
|
if err != nil {
|
|
panic(fmt.Errorf("failed to transfer GAS from Notary account: %w", err))
|
|
}
|
|
if !ic.VM.Estack().Pop().Bool() {
|
|
panic("failed to transfer GAS from Notary account: `transfer` returned false")
|
|
}
|
|
n.removeDepositFor(ic.DAO, from)
|
|
return stackitem.NewBool(true)
|
|
}
|
|
|
|
// balanceOf returns the deposited GAS amount for the specified address.
|
|
func (n *Notary) balanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
acc := toUint160(args[0])
|
|
return stackitem.NewBigInteger(n.BalanceOf(ic.DAO, acc))
|
|
}
|
|
|
|
// BalanceOf is an internal representation of `balanceOf` Notary method.
|
|
func (n *Notary) BalanceOf(dao *dao.Simple, acc util.Uint160) *big.Int {
|
|
deposit := n.GetDepositFor(dao, acc)
|
|
if deposit == nil {
|
|
return big.NewInt(0)
|
|
}
|
|
return deposit.Amount
|
|
}
|
|
|
|
// expirationOf returns the deposit lock height for the specified address.
|
|
func (n *Notary) expirationOf(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
acc := toUint160(args[0])
|
|
return stackitem.Make(n.ExpirationOf(ic.DAO, acc))
|
|
}
|
|
|
|
// ExpirationOf is an internal representation of `expirationOf` Notary method.
|
|
func (n *Notary) ExpirationOf(dao *dao.Simple, acc util.Uint160) uint32 {
|
|
deposit := n.GetDepositFor(dao, acc)
|
|
if deposit == nil {
|
|
return 0
|
|
}
|
|
return deposit.Till
|
|
}
|
|
|
|
// verify checks whether the transaction was signed by one of the notaries.
|
|
func (n *Notary) verify(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
sig, err := args[0].TryBytes()
|
|
if err != nil {
|
|
panic(fmt.Errorf("failed to get signature bytes: %w", err))
|
|
}
|
|
tx := ic.Tx
|
|
if len(tx.GetAttributes(transaction.NotaryAssistedT)) == 0 {
|
|
return stackitem.NewBool(false)
|
|
}
|
|
for _, signer := range tx.Signers {
|
|
if signer.Account == n.Hash {
|
|
if signer.Scopes != transaction.None {
|
|
return stackitem.NewBool(false)
|
|
}
|
|
break
|
|
}
|
|
}
|
|
if tx.Sender() == n.Hash {
|
|
if len(tx.Signers) != 2 {
|
|
return stackitem.NewBool(false)
|
|
}
|
|
payer := tx.Signers[1].Account
|
|
balance := n.GetDepositFor(ic.DAO, payer)
|
|
if balance == nil || balance.Amount.Cmp(big.NewInt(tx.NetworkFee+tx.SystemFee)) < 0 {
|
|
return stackitem.NewBool(false)
|
|
}
|
|
}
|
|
notaries, err := n.GetNotaryNodes(ic.DAO)
|
|
if err != nil {
|
|
panic(fmt.Errorf("failed to get notary nodes: %w", err))
|
|
}
|
|
shash := hash.NetSha256(uint32(ic.Network), tx)
|
|
var verified bool
|
|
for _, n := range notaries {
|
|
if n.Verify(sig, shash[:]) {
|
|
verified = true
|
|
break
|
|
}
|
|
}
|
|
return stackitem.NewBool(verified)
|
|
}
|
|
|
|
// GetNotaryNodes returns public keys of notary nodes.
|
|
func (n *Notary) GetNotaryNodes(d *dao.Simple) (keys.PublicKeys, error) {
|
|
nodes, _, err := n.Desig.GetDesignatedByRole(d, noderoles.P2PNotary, math.MaxUint32)
|
|
return nodes, err
|
|
}
|
|
|
|
// getMaxNotValidBeforeDelta is a Notary contract method and returns the maximum NotValidBefore delta.
|
|
func (n *Notary) getMaxNotValidBeforeDelta(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
|
return stackitem.NewBigInteger(big.NewInt(int64(n.GetMaxNotValidBeforeDelta(ic.DAO))))
|
|
}
|
|
|
|
// GetMaxNotValidBeforeDelta is an internal representation of Notary getMaxNotValidBeforeDelta method.
|
|
func (n *Notary) GetMaxNotValidBeforeDelta(dao *dao.Simple) uint32 {
|
|
cache := dao.GetROCache(n.ID).(*NotaryCache)
|
|
return cache.maxNotValidBeforeDelta
|
|
}
|
|
|
|
// setMaxNotValidBeforeDelta is a Notary contract method and sets the maximum NotValidBefore delta.
|
|
func (n *Notary) setMaxNotValidBeforeDelta(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
value := toUint32(args[0])
|
|
cfg := ic.Chain.GetConfig()
|
|
maxInc := cfg.MaxValidUntilBlockIncrement
|
|
if value > maxInc/2 || value < uint32(cfg.GetNumOfCNs(ic.BlockHeight())) {
|
|
panic(fmt.Errorf("MaxNotValidBeforeDelta cannot be more than %d or less than %d", maxInc/2, cfg.GetNumOfCNs(ic.BlockHeight())))
|
|
}
|
|
if !n.NEO.checkCommittee(ic) {
|
|
panic("invalid committee signature")
|
|
}
|
|
setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, int64(value))
|
|
cache := ic.DAO.GetRWCache(n.ID).(*NotaryCache)
|
|
cache.maxNotValidBeforeDelta = value
|
|
return stackitem.Null{}
|
|
}
|
|
|
|
// getNotaryServiceFeePerKey is a Notary contract method and returns a reward per notary request key for notary nodes.
|
|
func (n *Notary) getNotaryServiceFeePerKey(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
|
return stackitem.NewBigInteger(big.NewInt(int64(n.GetNotaryServiceFeePerKey(ic.DAO))))
|
|
}
|
|
|
|
// GetNotaryServiceFeePerKey is an internal representation of Notary getNotaryServiceFeePerKey method.
|
|
func (n *Notary) GetNotaryServiceFeePerKey(dao *dao.Simple) int64 {
|
|
cache := dao.GetROCache(n.ID).(*NotaryCache)
|
|
return cache.notaryServiceFeePerKey
|
|
}
|
|
|
|
// setNotaryServiceFeePerKey is a Notary contract method and sets a reward per notary request key for notary nodes.
|
|
func (n *Notary) setNotaryServiceFeePerKey(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
value := toInt64(args[0])
|
|
if value < 0 || value > maxNotaryServiceFeePerKey {
|
|
panic("NotaryServiceFeePerKey value is out of range")
|
|
}
|
|
if !n.NEO.checkCommittee(ic) {
|
|
panic("invalid committee signature")
|
|
}
|
|
setIntWithKey(n.ID, ic.DAO, notaryServiceFeeKey, int64(value))
|
|
cache := ic.DAO.GetRWCache(n.ID).(*NotaryCache)
|
|
cache.notaryServiceFeePerKey = value
|
|
return stackitem.Null{}
|
|
}
|
|
|
|
// GetDepositFor returns state.Deposit for the account specified. It returns nil in case
|
|
// the deposit is not found in the storage and panics in case of any other error.
|
|
func (n *Notary) GetDepositFor(dao *dao.Simple, acc util.Uint160) *state.Deposit {
|
|
key := append([]byte{prefixDeposit}, acc.BytesBE()...)
|
|
deposit := new(state.Deposit)
|
|
err := getConvertibleFromDAO(n.ID, dao, key, deposit)
|
|
if err == nil {
|
|
return deposit
|
|
}
|
|
if errors.Is(err, storage.ErrKeyNotFound) {
|
|
return nil
|
|
}
|
|
panic(fmt.Errorf("failed to get deposit for %s from storage: %w", acc.StringBE(), err))
|
|
}
|
|
|
|
// putDepositFor puts the deposit on the balance of the specified account in the storage.
|
|
func (n *Notary) putDepositFor(dao *dao.Simple, deposit *state.Deposit, acc util.Uint160) error {
|
|
key := append([]byte{prefixDeposit}, acc.BytesBE()...)
|
|
return putConvertibleToDAO(n.ID, dao, key, deposit)
|
|
}
|
|
|
|
// removeDepositFor removes the deposit from the storage.
|
|
func (n *Notary) removeDepositFor(dao *dao.Simple, acc util.Uint160) {
|
|
key := append([]byte{prefixDeposit}, acc.BytesBE()...)
|
|
dao.DeleteStorageItem(n.ID, key)
|
|
}
|
|
|
|
// calculateNotaryReward calculates the reward for a single notary node based on FEE's count and Notary nodes count.
|
|
func calculateNotaryReward(nFees int64, feePerKey int64, notariesCount int) *big.Int {
|
|
return big.NewInt(nFees * feePerKey / int64(notariesCount))
|
|
}
|