2020-11-19 10:00:46 +00:00
|
|
|
package native
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
"math/big"
|
|
|
|
|
2023-11-21 09:35:18 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
2020-11-19 10:00:46 +00:00
|
|
|
"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"
|
2020-12-13 18:25:04 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
2021-03-23 10:49:27 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativeprices"
|
2021-03-23 10:37:30 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
2020-11-19 10:00:46 +00:00
|
|
|
"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"
|
2021-03-25 16:18:01 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
2020-11-19 10:00:46 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
2020-12-29 10:45:49 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
2020-11-19 10:00:46 +00:00
|
|
|
"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
|
2023-09-21 15:22:32 +00:00
|
|
|
GAS *GAS
|
|
|
|
NEO *NEO
|
|
|
|
Desig *Designate
|
|
|
|
Policy *Policy
|
2022-04-12 14:29:11 +00:00
|
|
|
}
|
2020-11-27 10:55:48 +00:00
|
|
|
|
2022-04-12 14:29:11 +00:00
|
|
|
type NotaryCache struct {
|
2020-11-27 10:55:48 +00:00
|
|
|
maxNotValidBeforeDelta uint32
|
2020-11-19 10:00:46 +00:00
|
|
|
}
|
|
|
|
|
2022-07-22 19:18:55 +00:00
|
|
|
// NotaryService is a Notary module interface.
|
|
|
|
type NotaryService interface {
|
|
|
|
UpdateNotaryNodes(pubs keys.PublicKeys)
|
|
|
|
}
|
|
|
|
|
2020-11-19 10:00:46 +00:00
|
|
|
const (
|
2022-03-01 13:03:21 +00:00
|
|
|
notaryContractID = -10
|
2020-11-19 10:00:46 +00:00
|
|
|
// prefixDeposit is a prefix for storing Notary deposits.
|
2020-11-27 10:55:48 +00:00
|
|
|
prefixDeposit = 1
|
|
|
|
defaultDepositDeltaTill = 5760
|
2023-09-21 15:22:32 +00:00
|
|
|
defaultMaxNotValidBeforeDelta = 140 // 20 rounds for 7 validators, a little more than half an hour
|
2020-11-19 10:00:46 +00:00
|
|
|
)
|
|
|
|
|
2023-09-21 15:22:32 +00:00
|
|
|
var maxNotValidBeforeDeltaKey = []byte{10}
|
2020-11-27 10:55:48 +00:00
|
|
|
|
2022-04-15 14:48:58 +00:00
|
|
|
var (
|
2022-04-20 14:47:48 +00:00
|
|
|
_ interop.Contract = (*Notary)(nil)
|
|
|
|
_ dao.NativeContractCache = (*NotaryCache)(nil)
|
2022-04-15 14:48:58 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Copy implements NativeContractCache interface.
|
2022-04-20 14:47:48 +00:00
|
|
|
func (c *NotaryCache) Copy() dao.NativeContractCache {
|
2022-04-15 14:48:58 +00:00
|
|
|
cp := &NotaryCache{}
|
|
|
|
copyNotaryCache(c, cp)
|
|
|
|
return cp
|
|
|
|
}
|
|
|
|
|
|
|
|
func copyNotaryCache(src, dst *NotaryCache) {
|
2022-04-19 09:26:46 +00:00
|
|
|
*dst = *src
|
2022-04-15 14:48:58 +00:00
|
|
|
}
|
|
|
|
|
2020-11-19 10:00:46 +00:00
|
|
|
// newNotary returns Notary native contract.
|
|
|
|
func newNotary() *Notary {
|
2021-01-15 21:17:31 +00:00
|
|
|
n := &Notary{ContractMD: *interop.NewContractMD(nativenames.Notary, notaryContractID)}
|
2021-02-15 13:40:44 +00:00
|
|
|
defer n.UpdateHash()
|
2020-11-19 10:00:46 +00:00
|
|
|
|
2021-02-05 13:09:51 +00:00
|
|
|
desc := newDescriptor("onNEP17Payment", smartcontract.VoidType,
|
2020-11-19 10:00:46 +00:00
|
|
|
manifest.NewParameter("from", smartcontract.Hash160Type),
|
|
|
|
manifest.NewParameter("amount", smartcontract.IntegerType),
|
|
|
|
manifest.NewParameter("data", smartcontract.AnyType))
|
2021-03-05 10:30:16 +00:00
|
|
|
md := newMethodAndPrice(n.onPayment, 1<<15, callflag.States)
|
2020-12-08 10:27:41 +00:00
|
|
|
n.AddMethod(md, desc)
|
2020-11-19 10:00:46 +00:00
|
|
|
|
|
|
|
desc = newDescriptor("lockDepositUntil", smartcontract.BoolType,
|
|
|
|
manifest.NewParameter("address", smartcontract.Hash160Type),
|
|
|
|
manifest.NewParameter("till", smartcontract.IntegerType))
|
2021-03-05 10:30:16 +00:00
|
|
|
md = newMethodAndPrice(n.lockDepositUntil, 1<<15, callflag.States)
|
2020-12-08 10:27:41 +00:00
|
|
|
n.AddMethod(md, desc)
|
2020-11-19 10:00:46 +00:00
|
|
|
|
|
|
|
desc = newDescriptor("withdraw", smartcontract.BoolType,
|
|
|
|
manifest.NewParameter("from", smartcontract.Hash160Type),
|
|
|
|
manifest.NewParameter("to", smartcontract.Hash160Type))
|
2022-01-19 08:33:33 +00:00
|
|
|
md = newMethodAndPrice(n.withdraw, 1<<15, callflag.All)
|
2020-12-08 10:27:41 +00:00
|
|
|
n.AddMethod(md, desc)
|
2020-11-19 10:00:46 +00:00
|
|
|
|
|
|
|
desc = newDescriptor("balanceOf", smartcontract.IntegerType,
|
|
|
|
manifest.NewParameter("addr", smartcontract.Hash160Type))
|
2021-03-05 10:30:16 +00:00
|
|
|
md = newMethodAndPrice(n.balanceOf, 1<<15, callflag.ReadStates)
|
2020-12-08 10:27:41 +00:00
|
|
|
n.AddMethod(md, desc)
|
2020-11-19 10:00:46 +00:00
|
|
|
|
|
|
|
desc = newDescriptor("expirationOf", smartcontract.IntegerType,
|
|
|
|
manifest.NewParameter("addr", smartcontract.Hash160Type))
|
2021-03-05 10:30:16 +00:00
|
|
|
md = newMethodAndPrice(n.expirationOf, 1<<15, callflag.ReadStates)
|
2020-12-08 10:27:41 +00:00
|
|
|
n.AddMethod(md, desc)
|
2020-11-19 10:00:46 +00:00
|
|
|
|
|
|
|
desc = newDescriptor("verify", smartcontract.BoolType,
|
|
|
|
manifest.NewParameter("signature", smartcontract.SignatureType))
|
2021-03-23 10:49:27 +00:00
|
|
|
md = newMethodAndPrice(n.verify, nativeprices.NotaryVerificationPrice, callflag.ReadStates)
|
2020-12-08 10:27:41 +00:00
|
|
|
n.AddMethod(md, desc)
|
2020-11-19 10:00:46 +00:00
|
|
|
|
2020-11-27 10:55:48 +00:00
|
|
|
desc = newDescriptor("getMaxNotValidBeforeDelta", smartcontract.IntegerType)
|
2021-03-05 10:30:16 +00:00
|
|
|
md = newMethodAndPrice(n.getMaxNotValidBeforeDelta, 1<<15, callflag.ReadStates)
|
2020-11-27 10:55:48 +00:00
|
|
|
n.AddMethod(md, desc)
|
|
|
|
|
2021-01-28 15:01:30 +00:00
|
|
|
desc = newDescriptor("setMaxNotValidBeforeDelta", smartcontract.VoidType,
|
2020-11-27 10:55:48 +00:00
|
|
|
manifest.NewParameter("value", smartcontract.IntegerType))
|
2021-03-05 10:30:16 +00:00
|
|
|
md = newMethodAndPrice(n.setMaxNotValidBeforeDelta, 1<<15, callflag.States)
|
2020-11-27 10:55:48 +00:00
|
|
|
n.AddMethod(md, desc)
|
|
|
|
|
2020-11-19 10:00:46 +00:00
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// Metadata implements the Contract interface.
|
2020-11-19 10:00:46 +00:00
|
|
|
func (n *Notary) Metadata() *interop.ContractMD {
|
|
|
|
return &n.ContractMD
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// Initialize initializes Notary native contract and implements the Contract interface.
|
2020-11-19 10:00:46 +00:00
|
|
|
func (n *Notary) Initialize(ic *interop.Context) error {
|
2022-02-16 14:48:15 +00:00
|
|
|
setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, defaultMaxNotValidBeforeDelta)
|
2022-04-12 14:29:11 +00:00
|
|
|
|
|
|
|
cache := &NotaryCache{
|
|
|
|
maxNotValidBeforeDelta: defaultMaxNotValidBeforeDelta,
|
|
|
|
}
|
2022-04-20 14:47:48 +00:00
|
|
|
ic.DAO.SetCache(n.ID, cache)
|
2022-04-12 14:29:11 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-04-26 09:52:59 +00:00
|
|
|
func (n *Notary) InitializeCache(blockHeight uint32, d *dao.Simple) error {
|
2022-04-19 15:32:34 +00:00
|
|
|
cache := &NotaryCache{
|
|
|
|
maxNotValidBeforeDelta: uint32(getIntWithKey(n.ID, d, maxNotValidBeforeDeltaKey)),
|
|
|
|
}
|
2022-04-12 14:29:11 +00:00
|
|
|
|
2022-04-20 14:47:48 +00:00
|
|
|
d.SetCache(n.ID, cache)
|
2020-11-19 10:00:46 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// OnPersist implements the Contract interface.
|
2020-11-19 10:00:46 +00:00
|
|
|
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]
|
2020-11-27 10:55:48 +00:00
|
|
|
balance := n.GetDepositFor(ic.DAO, payer.Account)
|
2020-11-19 10:00:46 +00:00
|
|
|
balance.Amount.Sub(balance.Amount, big.NewInt(tx.SystemFee+tx.NetworkFee))
|
|
|
|
if balance.Amount.Sign() == 0 {
|
2022-02-16 14:48:15 +00:00
|
|
|
n.removeDepositFor(ic.DAO, payer.Account)
|
2020-11-19 10:00:46 +00:00
|
|
|
} 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
|
|
|
|
}
|
2023-09-21 15:22:32 +00:00
|
|
|
feePerKey := n.Policy.GetAttributeFeeInternal(ic.DAO, transaction.NotaryAssistedT)
|
2022-03-01 10:10:54 +00:00
|
|
|
singleReward := calculateNotaryReward(nFees, feePerKey, len(notaries))
|
2020-11-19 10:00:46 +00:00
|
|
|
for _, notary := range notaries {
|
2020-11-30 10:05:42 +00:00
|
|
|
n.GAS.mint(ic, notary.GetScriptHash(), singleReward, false)
|
2020-11-19 10:00:46 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// PostPersist implements the Contract interface.
|
2020-12-13 20:30:21 +00:00
|
|
|
func (n *Notary) PostPersist(ic *interop.Context) error {
|
2020-11-27 10:55:48 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-21 09:35:18 +00:00
|
|
|
// ActiveIn implements the Contract interface.
|
|
|
|
func (n *Notary) ActiveIn() *config.Hardfork {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// onPayment records the deposited amount as belonging to "from" address with a lock
|
2020-11-19 10:00:46 +00:00
|
|
|
// 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])
|
|
|
|
}
|
2020-12-07 08:43:32 +00:00
|
|
|
|
|
|
|
allowedChangeTill := ic.Tx.Sender() == to
|
2022-04-29 15:00:46 +00:00
|
|
|
currentHeight := ic.BlockHeight()
|
2020-11-27 10:55:48 +00:00
|
|
|
deposit := n.GetDepositFor(ic.DAO, to)
|
2020-12-07 08:43:32 +00:00
|
|
|
till := toUint32(additionalParams[1])
|
2023-11-21 07:49:05 +00:00
|
|
|
if till < currentHeight+2 {
|
2023-11-21 10:47:59 +00:00
|
|
|
panic(fmt.Errorf("`till` shouldn't be less than the chain's height + 1 (%d at min)", currentHeight+2))
|
2020-11-19 10:00:46 +00:00
|
|
|
}
|
2020-12-07 08:43:32 +00:00
|
|
|
if deposit != nil && till < deposit.Till {
|
2023-11-21 10:47:59 +00:00
|
|
|
panic(fmt.Errorf("`till` shouldn't be less than the previous value %d", deposit.Till))
|
2020-12-07 08:43:32 +00:00
|
|
|
}
|
2023-09-21 15:22:32 +00:00
|
|
|
feePerKey := n.Policy.GetAttributeFeeInternal(ic.DAO, transaction.NotaryAssistedT)
|
2020-11-19 10:00:46 +00:00
|
|
|
if deposit == nil {
|
2022-03-01 10:10:54 +00:00
|
|
|
if amount.Cmp(big.NewInt(2*feePerKey)) < 0 {
|
2023-11-21 10:47:59 +00:00
|
|
|
panic(fmt.Errorf("first deposit can not be less than %d, got %d", 2*feePerKey, amount.Int64()))
|
2020-11-19 10:00:46 +00:00
|
|
|
}
|
|
|
|
deposit = &state.Deposit{
|
|
|
|
Amount: new(big.Int),
|
|
|
|
}
|
2020-12-07 08:43:32 +00:00
|
|
|
if !allowedChangeTill {
|
|
|
|
till = currentHeight + defaultDepositDeltaTill
|
2020-11-19 10:00:46 +00:00
|
|
|
}
|
2020-12-07 08:43:32 +00:00
|
|
|
} else if !allowedChangeTill { // only deposit's owner is allowed to set or update `till`
|
|
|
|
till = deposit.Till
|
2020-11-19 10:00:46 +00:00
|
|
|
}
|
|
|
|
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{}
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// lockDepositUntil updates the chain's height until which the deposit is locked.
|
2020-11-19 10:00:46 +00:00
|
|
|
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])
|
2023-11-21 07:49:05 +00:00
|
|
|
if till < (ic.BlockHeight() + 1 + 1) { // deposit can't expire at the current persisting block.
|
2020-11-19 10:00:46 +00:00
|
|
|
return stackitem.NewBool(false)
|
|
|
|
}
|
2020-11-27 10:55:48 +00:00
|
|
|
deposit := n.GetDepositFor(ic.DAO, addr)
|
2020-11-19 10:00:46 +00:00
|
|
|
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])
|
|
|
|
}
|
2020-11-27 10:55:48 +00:00
|
|
|
deposit := n.GetDepositFor(ic.DAO, from)
|
2020-11-19 10:00:46 +00:00
|
|
|
if deposit == nil {
|
|
|
|
return stackitem.NewBool(false)
|
|
|
|
}
|
2023-11-21 07:49:05 +00:00
|
|
|
// Allow withdrawal only after `till` block was persisted, thus, use ic.BlockHeight().
|
2022-04-29 15:00:46 +00:00
|
|
|
if ic.BlockHeight() < deposit.Till {
|
2020-11-19 10:00:46 +00:00
|
|
|
return stackitem.NewBool(false)
|
|
|
|
}
|
2020-12-13 15:26:35 +00:00
|
|
|
cs, err := ic.GetContract(n.GAS.Hash)
|
2020-11-19 10:00:46 +00:00
|
|
|
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{}}
|
2020-12-29 10:44:07 +00:00
|
|
|
err = contract.CallFromNative(ic, n.Hash, cs, "transfer", transferArgs, true)
|
2020-11-19 10:00:46 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Errorf("failed to transfer GAS from Notary account: %w", err))
|
|
|
|
}
|
2020-12-09 12:16:49 +00:00
|
|
|
if !ic.VM.Estack().Pop().Bool() {
|
|
|
|
panic("failed to transfer GAS from Notary account: `transfer` returned false")
|
|
|
|
}
|
2022-02-16 14:48:15 +00:00
|
|
|
n.removeDepositFor(ic.DAO, from)
|
2020-11-19 10:00:46 +00:00
|
|
|
return stackitem.NewBool(true)
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// balanceOf returns the deposited GAS amount for the specified address.
|
2020-11-19 10:00:46 +00:00
|
|
|
func (n *Notary) balanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
|
|
acc := toUint160(args[0])
|
2020-11-27 10:55:48 +00:00
|
|
|
return stackitem.NewBigInteger(n.BalanceOf(ic.DAO, acc))
|
|
|
|
}
|
|
|
|
|
|
|
|
// BalanceOf is an internal representation of `balanceOf` Notary method.
|
2022-02-16 15:04:47 +00:00
|
|
|
func (n *Notary) BalanceOf(dao *dao.Simple, acc util.Uint160) *big.Int {
|
2020-11-27 10:55:48 +00:00
|
|
|
deposit := n.GetDepositFor(dao, acc)
|
2020-11-19 10:00:46 +00:00
|
|
|
if deposit == nil {
|
2020-11-27 10:55:48 +00:00
|
|
|
return big.NewInt(0)
|
2020-11-19 10:00:46 +00:00
|
|
|
}
|
2020-11-27 10:55:48 +00:00
|
|
|
return deposit.Amount
|
2020-11-19 10:00:46 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// expirationOf returns the deposit lock height for the specified address.
|
2020-11-19 10:00:46 +00:00
|
|
|
func (n *Notary) expirationOf(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
|
|
acc := toUint160(args[0])
|
2020-11-27 10:55:48 +00:00
|
|
|
return stackitem.Make(n.ExpirationOf(ic.DAO, acc))
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExpirationOf is an internal representation of `expirationOf` Notary method.
|
2022-02-16 15:04:47 +00:00
|
|
|
func (n *Notary) ExpirationOf(dao *dao.Simple, acc util.Uint160) uint32 {
|
2020-11-27 10:55:48 +00:00
|
|
|
deposit := n.GetDepositFor(dao, acc)
|
2020-11-19 10:00:46 +00:00
|
|
|
if deposit == nil {
|
2020-11-27 10:55:48 +00:00
|
|
|
return 0
|
2020-11-19 10:00:46 +00:00
|
|
|
}
|
2020-11-27 10:55:48 +00:00
|
|
|
return deposit.Till
|
2020-11-19 10:00:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
2021-02-11 10:53:37 +00:00
|
|
|
break
|
2020-11-19 10:00:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if tx.Sender() == n.Hash {
|
|
|
|
if len(tx.Signers) != 2 {
|
|
|
|
return stackitem.NewBool(false)
|
|
|
|
}
|
|
|
|
payer := tx.Signers[1].Account
|
2020-11-27 10:55:48 +00:00
|
|
|
balance := n.GetDepositFor(ic.DAO, payer)
|
2020-11-19 10:00:46 +00:00
|
|
|
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))
|
|
|
|
}
|
2021-03-25 16:18:01 +00:00
|
|
|
shash := hash.NetSha256(uint32(ic.Network), tx)
|
2020-11-19 10:00:46 +00:00
|
|
|
var verified bool
|
|
|
|
for _, n := range notaries {
|
2021-03-25 16:18:01 +00:00
|
|
|
if n.Verify(sig, shash[:]) {
|
2020-11-19 10:00:46 +00:00
|
|
|
verified = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return stackitem.NewBool(verified)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetNotaryNodes returns public keys of notary nodes.
|
2022-02-16 15:04:47 +00:00
|
|
|
func (n *Notary) GetNotaryNodes(d *dao.Simple) (keys.PublicKeys, error) {
|
2021-03-23 10:37:30 +00:00
|
|
|
nodes, _, err := n.Desig.GetDesignatedByRole(d, noderoles.P2PNotary, math.MaxUint32)
|
2020-11-19 10:00:46 +00:00
|
|
|
return nodes, err
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// getMaxNotValidBeforeDelta is a Notary contract method and returns the maximum NotValidBefore delta.
|
2020-11-27 10:55:48 +00:00
|
|
|
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.
|
2022-02-16 15:04:47 +00:00
|
|
|
func (n *Notary) GetMaxNotValidBeforeDelta(dao *dao.Simple) uint32 {
|
2022-04-20 14:47:48 +00:00
|
|
|
cache := dao.GetROCache(n.ID).(*NotaryCache)
|
2022-04-19 15:32:34 +00:00
|
|
|
return cache.maxNotValidBeforeDelta
|
2020-11-27 10:55:48 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// setMaxNotValidBeforeDelta is a Notary contract method and sets the maximum NotValidBefore delta.
|
2020-11-27 10:55:48 +00:00
|
|
|
func (n *Notary) setMaxNotValidBeforeDelta(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
|
|
value := toUint32(args[0])
|
2022-01-21 02:33:06 +00:00
|
|
|
cfg := ic.Chain.GetConfig()
|
|
|
|
maxInc := cfg.MaxValidUntilBlockIncrement
|
2022-04-29 15:00:46 +00:00
|
|
|
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())))
|
2020-11-27 10:55:48 +00:00
|
|
|
}
|
2021-01-21 12:05:15 +00:00
|
|
|
if !n.NEO.checkCommittee(ic) {
|
2021-01-28 15:01:30 +00:00
|
|
|
panic("invalid committee signature")
|
2020-11-27 10:55:48 +00:00
|
|
|
}
|
2022-02-16 14:48:15 +00:00
|
|
|
setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, int64(value))
|
2022-04-20 14:47:48 +00:00
|
|
|
cache := ic.DAO.GetRWCache(n.ID).(*NotaryCache)
|
2022-04-19 15:32:34 +00:00
|
|
|
cache.maxNotValidBeforeDelta = value
|
2021-01-28 15:01:30 +00:00
|
|
|
return stackitem.Null{}
|
2020-11-27 10:55:48 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// 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.
|
2022-02-16 15:04:47 +00:00
|
|
|
func (n *Notary) GetDepositFor(dao *dao.Simple, acc util.Uint160) *state.Deposit {
|
2020-11-19 10:00:46 +00:00
|
|
|
key := append([]byte{prefixDeposit}, acc.BytesBE()...)
|
|
|
|
deposit := new(state.Deposit)
|
2021-07-17 15:37:33 +00:00
|
|
|
err := getConvertibleFromDAO(n.ID, dao, key, deposit)
|
2020-11-19 10:00:46 +00:00
|
|
|
if err == nil {
|
|
|
|
return deposit
|
|
|
|
}
|
2022-09-02 11:29:47 +00:00
|
|
|
if errors.Is(err, storage.ErrKeyNotFound) {
|
2020-11-19 10:00:46 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
panic(fmt.Errorf("failed to get deposit for %s from storage: %w", acc.StringBE(), err))
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// putDepositFor puts the deposit on the balance of the specified account in the storage.
|
2022-02-16 15:04:47 +00:00
|
|
|
func (n *Notary) putDepositFor(dao *dao.Simple, deposit *state.Deposit, acc util.Uint160) error {
|
2020-11-19 10:00:46 +00:00
|
|
|
key := append([]byte{prefixDeposit}, acc.BytesBE()...)
|
2021-07-17 15:37:33 +00:00
|
|
|
return putConvertibleToDAO(n.ID, dao, key, deposit)
|
2020-11-19 10:00:46 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// removeDepositFor removes the deposit from the storage.
|
2022-02-16 15:04:47 +00:00
|
|
|
func (n *Notary) removeDepositFor(dao *dao.Simple, acc util.Uint160) {
|
2020-11-19 10:00:46 +00:00
|
|
|
key := append([]byte{prefixDeposit}, acc.BytesBE()...)
|
2022-02-16 14:48:15 +00:00
|
|
|
dao.DeleteStorageItem(n.ID, key)
|
2020-11-19 10:00:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// calculateNotaryReward calculates the reward for a single notary node based on FEE's count and Notary nodes count.
|
2022-03-01 10:10:54 +00:00
|
|
|
func calculateNotaryReward(nFees int64, feePerKey int64, notariesCount int) *big.Int {
|
|
|
|
return big.NewInt(nFees * feePerKey / int64(notariesCount))
|
2020-11-19 10:00:46 +00:00
|
|
|
}
|