Skip to content

Commit

Permalink
network: add notary request payload
Browse files Browse the repository at this point in the history
  • Loading branch information
AnnaShaleva committed Dec 2, 2020
1 parent 7044e9b commit 0aa3339
Show file tree
Hide file tree
Showing 15 changed files with 806 additions and 71 deletions.
5 changes: 4 additions & 1 deletion pkg/config/protocol_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ type (
ProtocolConfiguration struct {
Magic netmode.Magic `yaml:"Magic"`
MemPoolSize int `yaml:"MemPoolSize"`
// P2PNotaryRequestPayloadPoolSize specifies the memory pool size for P2PNotaryRequestPayloads.
// It is valid only if P2PSigExtensions are enabled.
P2PNotaryRequestPayloadPoolSize int `yaml:"P2PNotaryRequestPayloadPoolSize"`
// KeepOnlyLatestState specifies if MPT should only store latest state.
// If true, DB size will be smaller, but older roots won't be accessible.
// This value should remain the same for the same database.
Expand All @@ -17,7 +20,7 @@ type (
RemoveUntraceableBlocks bool `yaml:"RemoveUntraceableBlocks"`
// MaxTraceableBlocks is the length of the chain accessible to smart contracts.
MaxTraceableBlocks uint32 `yaml:"MaxTraceableBlocks"`
// P2PSigExtensions enables additional signature-related transaction attributes
// P2PSigExtensions enables additional signature-related logic.
P2PSigExtensions bool `yaml:"P2PSigExtensions"`
// ReservedAttributes allows to have reserved attributes range for experimental or private purposes.
ReservedAttributes bool `yaml:"ReservedAttributes"`
Expand Down
71 changes: 68 additions & 3 deletions pkg/core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
"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/mempool"
Expand All @@ -26,6 +27,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/network/payload"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
Expand All @@ -42,9 +44,10 @@ const (
headerBatchCount = 2000
version = "0.1.0"

defaultMemPoolSize = 50000
defaultMaxTraceableBlocks = 2102400 // 1 year of 15s blocks
verificationGasLimit = 100000000 // 1 GAS
defaultMemPoolSize = 50000
defaultP2PNotaryRequestPayloadPoolSize = 1000
defaultMaxTraceableBlocks = 2102400 // 1 year of 15s blocks
verificationGasLimit = 100000000 // 1 GAS
)

var (
Expand Down Expand Up @@ -116,6 +119,10 @@ type Blockchain struct {

memPool *mempool.Pool

// Callback methods for NotaryRequestPool which should be run under the blockchain lock.
poolP2PNotaryRequestCallback func(blockchainer.Blockchainer, *payload.P2PNotaryRequest) error
postBlock func(blockchainer.Blockchainer, *mempool.Pool, *block.Block)

sbCommittee keys.PublicKeys

log *zap.Logger
Expand Down Expand Up @@ -151,6 +158,10 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L
cfg.MemPoolSize = defaultMemPoolSize
log.Info("mempool size is not set or wrong, setting default value", zap.Int("MemPoolSize", cfg.MemPoolSize))
}
if cfg.P2PSigExtensions && cfg.P2PNotaryRequestPayloadPoolSize <= 0 {
cfg.P2PNotaryRequestPayloadPoolSize = defaultP2PNotaryRequestPayloadPoolSize
log.Info("P2PNotaryRequestPayloadPool size is not set or wrong, setting default value", zap.Int("P2PNotaryRequestPayloadPoolSize", cfg.P2PNotaryRequestPayloadPoolSize))
}
if cfg.MaxTraceableBlocks == 0 {
cfg.MaxTraceableBlocks = defaultMaxTraceableBlocks
log.Info("MaxTraceableBlocks is not set or wrong, using default value", zap.Uint32("MaxTraceableBlocks", cfg.MaxTraceableBlocks))
Expand Down Expand Up @@ -720,6 +731,13 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
bc.lock.Unlock()
return fmt.Errorf("failed to call OnPersistEnd for Policy native contract: %w", err)
}
if bc.P2PSigExtensionsEnabled() {
err := bc.contracts.Notary.OnPersistEnd(bc.dao)
if err != nil {
bc.lock.Unlock()
return fmt.Errorf("failed to call OnPersistEnd for Notary native contract: %w", err)
}
}
if err := bc.contracts.Designate.OnPersistEnd(bc.dao); err != nil {
bc.lock.Unlock()
return err
Expand All @@ -734,6 +752,9 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
bc.topBlock.Store(block)
atomic.StoreUint32(&bc.blockHeight, block.Index)
bc.memPool.RemoveStale(func(tx *transaction.Transaction) bool { return bc.isTxStillRelevant(tx, txpool) }, bc)
if bc.postBlock != nil {
bc.postBlock(bc, txpool, block)
}
bc.lock.Unlock()

updateBlockHeightMetric(block.Index)
Expand Down Expand Up @@ -934,6 +955,11 @@ func (bc *Blockchain) GetGoverningTokenBalance(acc util.Uint160) (*big.Int, uint
return &neo.Balance, neo.LastUpdatedBlock
}

// GetDepositFor returns GAS amount deposited to Notary contract for the specified account.
func (bc *Blockchain) GetDepositFor(acc util.Uint160) *big.Int {
return bc.contracts.Notary.BalanceOf(bc.dao, acc)
}

// LastBatch returns last persisted storage batch.
func (bc *Blockchain) LastBatch() *storage.MemBatch {
return bc.lastBatch
Expand Down Expand Up @@ -1548,6 +1574,14 @@ func (bc *Blockchain) PoolTx(t *transaction.Transaction, pools ...*mempool.Pool)
return bc.verifyAndPoolTx(t, pool)
}

// PoolP2PNotaryRequestPayload verifies NotaryRequest payload and adds it to the memory pool.
func (bc *Blockchain) PoolP2PNotaryRequestPayload(r *payload.P2PNotaryRequest) error {
bc.lock.RLock()
defer bc.lock.RUnlock()

return bc.poolP2PNotaryRequestCallback(bc, r)
}

//GetStandByValidators returns validators from the configuration.
func (bc *Blockchain) GetStandByValidators() keys.PublicKeys {
return bc.sbCommittee[:bc.config.ValidatorsCount].Copy()
Expand Down Expand Up @@ -1757,3 +1791,34 @@ func (bc *Blockchain) newInteropContext(trigger trigger.Type, d dao.DAO, block *
func (bc *Blockchain) P2PSigExtensionsEnabled() bool {
return bc.config.P2PSigExtensions
}

func (bc *Blockchain) GetMaxVerificationGas() int64 {
return bc.contracts.Policy.GetMaxVerificationGas(bc.dao)
}

func (bc *Blockchain) NotaryContractHash() util.Uint160 {
if bc.P2PSigExtensionsEnabled() {
return bc.contracts.Notary.Hash
}
return util.Uint160{}
}

func (bc *Blockchain) RegisterPostBlock(f func(blockchainer.Blockchainer, *mempool.Pool, *block.Block)) {
if bc.P2PSigExtensionsEnabled() {
bc.postBlock = f
}
}

func (bc *Blockchain) RegisterPoolNotaryRequestCallback(f func(blockchainer.Blockchainer, *payload.P2PNotaryRequest) error) {
if bc.P2PSigExtensionsEnabled() {
bc.poolP2PNotaryRequestCallback = f
}
}

func (bc *Blockchain) GetMaxNotValidBeforeDelta() int64 {
return bc.contracts.Notary.GetMaxNotValidBeforeDelta(bc.dao)
}

func (bc *Blockchain) ExpirationOf(acc util.Uint160) uint32 {
return bc.contracts.Notary.ExpirationOf(bc.dao, acc)
}
11 changes: 11 additions & 0 deletions pkg/core/blockchainer/blockchainer.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/network/payload"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
Expand Down Expand Up @@ -67,4 +68,14 @@ type Blockchainer interface {
UnsubscribeFromExecutions(ch chan<- *state.AppExecResult)
UnsubscribeFromNotifications(ch chan<- *state.NotificationEvent)
UnsubscribeFromTransactions(ch chan<- *transaction.Transaction)

// signature extensions
RegisterPostBlock(f func(Blockchainer, *mempool.Pool, *block.Block))
RegisterPoolNotaryRequestCallback(f func(Blockchainer, *payload.P2PNotaryRequest) error)
PoolP2PNotaryRequestPayload(r *payload.P2PNotaryRequest) error
NotaryContractHash() util.Uint160
GetMaxVerificationGas() int64
GetMaxNotValidBeforeDelta() int64
ExpirationOf(acc util.Uint160) uint32
GetDepositFor(acc util.Uint160) *big.Int
}
96 changes: 89 additions & 7 deletions pkg/core/native/notary.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"math"
"math/big"
"sync"

"github.com/nspcc-dev/neo-go/pkg/core/dao"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
Expand All @@ -26,16 +27,26 @@ type Notary struct {
interop.ContractMD
GAS *GAS
Desig *Designate

lock sync.RWMutex
// isValid defies whether cached values were changed during the current
// consensus iteration. If false, these values will be updated after
// blockchain DAO persisting. If true, we can safely use cached values.
isValid bool
maxNotValidBeforeDelta int64
}

const (
notaryName = "Notary"
notaryContractID = reservedContractID - 1

// prefixDeposit is a prefix for storing Notary deposits.
prefixDeposit = 1
prefixDeposit = 1
defaultMaxNotValidBeforeDelta = 140 // 20 rounds for 7 validators, a little more than half an hour
)

var maxNotValidBeforeDeltaKey = []byte{10}

// newNotary returns Notary native contract.
func newNotary() *Notary {
n := &Notary{ContractMD: *interop.NewContractMD(notaryName)}
Expand Down Expand Up @@ -75,6 +86,14 @@ func newNotary() *Notary {
md = newMethodAndPrice(n.verify, 100_0000, smartcontract.AllowStates)
n.AddMethod(md, desc, false)

desc = newDescriptor("getMaxNotValidBeforeDelta", smartcontract.IntegerType)
md = newMethodAndPrice(n.getMaxNotValidBeforeDelta, 100_0000, smartcontract.AllowStates)
n.AddMethod(md, desc, false)

desc = newDescriptor("setMaxNotValidBeforeDelta", smartcontract.BoolType)
md = newMethodAndPrice(n.setMaxNotValidBeforeDelta, 300_0000, smartcontract.AllowModifyStates)
n.AddMethod(md, desc, false)

desc = newDescriptor("onPersist", smartcontract.VoidType)
md = newMethodAndPrice(getOnPersistWrapper(n.OnPersist), 0, smartcontract.AllowModifyStates)
n.AddMethod(md, desc, false)
Expand All @@ -93,6 +112,8 @@ func (n *Notary) Metadata() *interop.ContractMD {

// Initialize initializes Notary native contract and implements Contract interface.
func (n *Notary) Initialize(ic *interop.Context) error {
n.isValid = true
n.maxNotValidBeforeDelta = defaultMaxNotValidBeforeDelta
return nil
}

Expand Down Expand Up @@ -141,6 +162,19 @@ func (n *Notary) OnPersist(ic *interop.Context) error {
return nil
}

// OnPersistEnd updates cached Policy values if they've been changed
func (n *Notary) OnPersistEnd(dao dao.DAO) error {
if n.isValid {
return nil
}
n.lock.Lock()
defer n.lock.Unlock()

n.maxNotValidBeforeDelta = getInt64WithKey(n.ContractID, dao, maxNotValidBeforeDeltaKey, defaultMaxNotValidBeforeDelta)
n.isValid = true
return nil
}

// onPayment records 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 {
Expand Down Expand Up @@ -259,21 +293,31 @@ func (n *Notary) withdraw(ic *interop.Context, args []stackitem.Item) stackitem.
// balanceOf returns deposited GAS amount for specified address.
func (n *Notary) balanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item {
acc := toUint160(args[0])
deposit := n.getDepositFor(ic.DAO, acc)
return stackitem.NewBigInteger(n.BalanceOf(ic.DAO, acc))
}

// BalanceOf is an internal representation of `balanceOf` Notary method.
func (n *Notary) BalanceOf(dao dao.DAO, acc util.Uint160) *big.Int {
deposit := n.getDepositFor(dao, acc)
if deposit == nil {
return stackitem.NewBigInteger(big.NewInt(0))
return big.NewInt(0)
}
return stackitem.NewBigInteger(deposit.Amount)
return deposit.Amount
}

// expirationOf Returns deposit lock height for specified address.
func (n *Notary) expirationOf(ic *interop.Context, args []stackitem.Item) stackitem.Item {
acc := toUint160(args[0])
deposit := n.getDepositFor(ic.DAO, acc)
return stackitem.Make(n.ExpirationOf(ic.DAO, acc))
}

// ExpirationOf is an internal representation of `expirationOf` Notary method.
func (n *Notary) ExpirationOf(dao dao.DAO, acc util.Uint160) uint32 {
deposit := n.getDepositFor(dao, acc)
if deposit == nil {
return stackitem.Make(0)
return 0
}
return stackitem.Make(deposit.Till)
return deposit.Till
}

// verify checks whether the transaction was signed by one of the notaries.
Expand Down Expand Up @@ -324,6 +368,44 @@ func (n *Notary) GetNotaryNodes(d dao.DAO) (keys.PublicKeys, error) {
return nodes, err
}

// getMaxNotValidBeforeDelta is 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(n.GetMaxNotValidBeforeDelta(ic.DAO)))
}

// GetMaxNotValidBeforeDelta is an internal representation of Notary getMaxNotValidBeforeDelta method.
func (n *Notary) GetMaxNotValidBeforeDelta(dao dao.DAO) int64 {
n.lock.RLock()
defer n.lock.RUnlock()
if n.isValid {
return n.maxNotValidBeforeDelta
}
return getInt64WithKey(n.ContractID, dao, maxNotValidBeforeDeltaKey, defaultMaxNotValidBeforeDelta)
}

// setMaxNotValidBeforeDelta is Notary contract method and sets the maximum NotValidBefore delta.
func (n *Notary) setMaxNotValidBeforeDelta(ic *interop.Context, args []stackitem.Item) stackitem.Item {
value := toBigInt(args[0]).Int64()
if value < defaultMaxNotValidBeforeDelta { // TODO: another rule?
panic(fmt.Errorf("MaxNotValidBeforeDelta cannot be less than the default value = %d", defaultMaxNotValidBeforeDelta))
}
ok, err := checkValidators(ic)
if err != nil {
panic(err)
}
if !ok {
return stackitem.NewBool(false)
}
n.lock.Lock()
defer n.lock.Unlock()
err = setInt64WithKey(n.ContractID, ic.DAO, maxNotValidBeforeDeltaKey, value)
if err != nil {
panic(err)
}
n.isValid = false
return stackitem.NewBool(true)
}

// getDepositFor returns state.Deposit for the account specified. It returns nil in case if
// deposit is not found in storage and panics in case of any other error.
func (n *Notary) getDepositFor(dao dao.DAO, acc util.Uint160) *state.Deposit {
Expand Down
Loading

0 comments on commit 0aa3339

Please sign in to comment.