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 1, 2020
1 parent 7044e9b commit 8b459e0
Show file tree
Hide file tree
Showing 15 changed files with 632 additions and 29 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
79 changes: 75 additions & 4 deletions pkg/core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,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 +43,10 @@ const (
headerBatchCount = 2000
version = "0.1.0"

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

var (
Expand Down Expand Up @@ -114,7 +116,8 @@ type Blockchain struct {
stopCh chan struct{}
runToExitCh chan struct{}

memPool *mempool.Pool
memPool *mempool.Pool
notaryRequestPool *mempool.NotaryRequestPool

sbCommittee keys.PublicKeys

Expand Down Expand Up @@ -151,6 +154,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 All @@ -173,6 +180,9 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L

contracts: *native.NewContracts(cfg.P2PSigExtensions),
}
if cfg.P2PSigExtensions {
bc.notaryRequestPool = mempool.NewNotaryRequestPool(cfg.P2PNotaryRequestPayloadPoolSize)
}

if err := bc.init(); err != nil {
return nil, err
Expand Down Expand Up @@ -734,6 +744,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.config.P2PSigExtensions {
bc.notaryRequestPool.RemoveStale(func(r *payload.P2PNotaryRequest) bool { return bc.isNotaryRequestStillRelevant(r, txpool) }, bc)
}
bc.lock.Unlock()

updateBlockHeightMetric(block.Index)
Expand Down Expand Up @@ -934,6 +947,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 @@ -1448,6 +1466,19 @@ func (bc *Blockchain) isTxStillRelevant(t *transaction.Transaction, txpool *memp

}

func (bc *Blockchain) isNotaryRequestStillRelevant(r *payload.P2PNotaryRequest, txpool *mempool.Pool) bool {
if r.MainTransaction.ValidUntilBlock <= bc.BlockHeight() {
return false
}
if txpool.ContainsKey(r.MainTransaction.Hash()) {
return false
}
if txpool.ContainsKey(r.FallbackTransaction.Hash()) {
return false
}
return true
}

// AddStateRoot add new (possibly unverified) state root to the blockchain.
func (bc *Blockchain) AddStateRoot(r *state.MPTRoot) error {
our, err := bc.GetStateRoot(r.Index)
Expand Down Expand Up @@ -1548,6 +1579,46 @@ 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()

payer := r.FallbackTransaction.Signers[1].Account
if err := bc.VerifyWitness(payer, r, &r.Witness, bc.contracts.Policy.GetMaxVerificationGas(bc.dao)); err != nil {
return fmt.Errorf("bad P2PNotaryRequest payload witness: %w", err)
}
if r.FallbackTransaction.Sender() != bc.contracts.Notary.Hash {
return errors.New("P2PNotary contract should be a sender of the fallback transaction")
}
if err := bc.VerifyWitness(payer, r.FallbackTransaction, &r.FallbackTransaction.Scripts[1], bc.contracts.Policy.GetMaxVerificationGas(bc.dao)); err != nil {
return fmt.Errorf("bad fallback transaction witness #1: %s", err.Error())
}
nvbFallback := r.FallbackTransaction.GetAttributes(transaction.NotValidBeforeT)[0].Value.(*transaction.NotValidBefore).Height
maxNVBDelta := uint32(bc.contracts.Policy.GetMaxNotValidBeforeDelta(bc.dao))
if bc.BlockHeight()+maxNVBDelta < nvbFallback {
return fmt.Errorf("fallback transaction should have NotValidBefore not more then %d, got %d", bc.BlockHeight()+maxNVBDelta, nvbFallback)
}
if r.FallbackTransaction.ValidUntilBlock+maxNVBDelta < nvbFallback {
return fmt.Errorf("payload should be valid at least %d blocks after fallback's transaction NotValidBefore", maxNVBDelta)
}
depositExpiration := bc.contracts.Notary.ExpirationOf(bc.dao, payer)
if nvbFallback+maxNVBDelta >= depositExpiration {
return fmt.Errorf("fallback transaction is valid after deposit is unlocked: NotValidBefore is %d, MaxNotValidBeforeDelta is %d, deposit lock expires at %d", nvbFallback, maxNVBDelta, depositExpiration)
}
return bc.notaryRequestPool.Add(r, bc)
}

// HasP2PNotaryRequestPayload returns true in case if payload with the specified hash is in the pool.
func (bc *Blockchain) HasP2PNotaryRequestPayload(hash util.Uint256) bool {
return bc.notaryRequestPool.HasPayload(hash)
}

// GetP2PNotaryRequestPayload returns P2PNotaryRequest payload with the specified hash from the pool.
func (bc *Blockchain) GetP2PNotaryRequestPayload(hash util.Uint256) *payload.P2PNotaryRequest {
return bc.notaryRequestPool.GetPayload(hash)
}

//GetStandByValidators returns validators from the configuration.
func (bc *Blockchain) GetStandByValidators() keys.PublicKeys {
return bc.sbCommittee[:bc.config.ValidatorsCount].Copy()
Expand Down
61 changes: 61 additions & 0 deletions pkg/core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"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/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/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
Expand Down Expand Up @@ -1093,6 +1094,66 @@ func TestIsTxStillRelevant(t *testing.T) {
})
}

func TestIsNotaryRequestPayloadStillRelevant(t *testing.T) {
bc := newTestChain(t)
defer bc.Close()

mp := bc.notaryRequestPool
require.NotNil(t, mp)

newTx := func(t *testing.T) *transaction.Transaction {
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.RET)}, 100)
tx.ValidUntilBlock = bc.BlockHeight() + 1
tx.Signers = []transaction.Signer{{
Account: neoOwner,
Scopes: transaction.CalledByEntry,
}}
return tx
}

t.Run("expired payload", func(t *testing.T) {
require.NoError(t, bc.AddBlock(bc.newBlock()))
mainTx := newTx(t)
mainTx.ValidUntilBlock = bc.BlockHeight()
require.False(t, bc.isNotaryRequestStillRelevant(&payload.P2PNotaryRequest{
MainTransaction: mainTx,
}, nil))
})
t.Run("mainTx is in persisted block", func(t *testing.T) {
txPool := mempool.New(1)
mainTx := newTx(t)
addSigners(mainTx)
require.NoError(t, testchain.SignTx(bc, mainTx))
require.NoError(t, bc.verifyAndPoolTx(mainTx, txPool))

require.False(t, bc.isNotaryRequestStillRelevant(&payload.P2PNotaryRequest{
MainTransaction: mainTx,
}, txPool))
})
t.Run("fallbackTx is in persisted block", func(t *testing.T) {
txPool := mempool.New(1)
fallbackTx := newTx(t)
addSigners(fallbackTx)
require.NoError(t, testchain.SignTx(bc, fallbackTx))
require.NoError(t, bc.verifyAndPoolTx(fallbackTx, txPool))

require.False(t, bc.isNotaryRequestStillRelevant(&payload.P2PNotaryRequest{
FallbackTransaction: fallbackTx,
MainTransaction: newTx(t),
}, txPool))
})
t.Run("good", func(t *testing.T) {
txPool := mempool.New(1)
fallbackTx := newTx(t)
mainTx := newTx(t)

require.True(t, bc.isNotaryRequestStillRelevant(&payload.P2PNotaryRequest{
FallbackTransaction: fallbackTx,
MainTransaction: mainTx,
}, txPool))
})
}

func TestMemPoolRemoval(t *testing.T) {
const added = 16
const notAdded = 32
Expand Down
4 changes: 4 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 @@ -38,11 +39,13 @@ type Blockchainer interface {
CurrentHeaderHash() util.Uint256
CurrentBlockHash() util.Uint256
HasBlock(util.Uint256) bool
HasP2PNotaryRequestPayload(uint160 util.Uint256) bool
HasTransaction(util.Uint256) bool
GetAppExecResults(util.Uint256, trigger.Type) ([]state.AppExecResult, error)
GetNativeContractScriptHash(string) (util.Uint160, error)
GetNextBlockValidators() ([]*keys.PublicKey, error)
GetNEP17Balances(util.Uint160) *state.NEP17Balances
GetP2PNotaryRequestPayload(h util.Uint256) *payload.P2PNotaryRequest
GetValidators() ([]*keys.PublicKey, error)
GetStandByCommittee() keys.PublicKeys
GetStandByValidators() keys.PublicKeys
Expand All @@ -56,6 +59,7 @@ type Blockchainer interface {
GetMaxBlockSize() uint32
GetMaxBlockSystemFee() int64
PoolTx(t *transaction.Transaction, pools ...*mempool.Pool) error
PoolP2PNotaryRequestPayload(r *payload.P2PNotaryRequest) error
SubscribeForBlocks(ch chan<- *block.Block)
SubscribeForExecutions(ch chan<- *state.AppExecResult)
SubscribeForNotifications(ch chan<- *state.NotificationEvent)
Expand Down
12 changes: 12 additions & 0 deletions pkg/core/mempool/depositor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package mempool

import (
"math/big"

"github.com/nspcc-dev/neo-go/pkg/util"
)

// Depositor is an interface that abstract the implementation of the deposit calculation.
type Depositor interface {
GetDepositFor(account util.Uint160) *big.Int
}
Loading

0 comments on commit 8b459e0

Please sign in to comment.