mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-26 19:43:48 +00:00
core: use dao-binded cache for native contracts
All native cached values are binded to DAO, so that it's possible to properly handle historic calls.
This commit is contained in:
parent
812fa3f76a
commit
aa886f67ce
16 changed files with 475 additions and 282 deletions
|
@ -201,7 +201,8 @@ func TestAppCall(t *testing.T) {
|
|||
}
|
||||
|
||||
fc := fakechain.NewFakeChain()
|
||||
ic := interop.NewContext(trigger.Application, fc, dao.NewSimple(storage.NewMemoryStore(), false, false), interop.DefaultBaseExecFee, native.DefaultStoragePrice, contractGetter, nil, nil, nil, zaptest.NewLogger(t))
|
||||
ic := interop.NewContext(trigger.Application, fc, dao.NewSimple(storage.NewMemoryStore(), false, false),
|
||||
interop.DefaultBaseExecFee, native.DefaultStoragePrice, contractGetter, nil, nil, nil, zaptest.NewLogger(t))
|
||||
|
||||
t.Run("valid script", func(t *testing.T) {
|
||||
src := getAppCallScript(fmt.Sprintf("%#v", ih.BytesBE()))
|
||||
|
|
|
@ -594,7 +594,18 @@ func (bc *Blockchain) initializeNativeCache(d *dao.Simple) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("can't init cache for Management native contract: %w", err)
|
||||
}
|
||||
bc.contracts.Designate.InitializeCache()
|
||||
bc.contracts.Designate.InitializeCache(d)
|
||||
bc.contracts.Oracle.InitializeCache(d)
|
||||
if bc.P2PSigExtensionsEnabled() {
|
||||
err = bc.contracts.Notary.InitializeCache(d)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't init cache for Notary native contract: %w", err)
|
||||
}
|
||||
}
|
||||
err = bc.contracts.Policy.InitializeCache(d)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't init cache for Policy native contract: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1223,14 +1234,14 @@ func (bc *Blockchain) updateExtensibleWhitelist(height uint32) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
newList := []util.Uint160{bc.contracts.NEO.GetCommitteeAddress()}
|
||||
nextVals := bc.contracts.NEO.GetNextBlockValidatorsInternal()
|
||||
newList := []util.Uint160{bc.contracts.NEO.GetCommitteeAddress(bc.dao)}
|
||||
nextVals := bc.contracts.NEO.GetNextBlockValidatorsInternal(bc.dao)
|
||||
script, err := smartcontract.CreateDefaultMultiSigRedeemScript(nextVals)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newList = append(newList, hash.Hash160(script))
|
||||
bc.updateExtensibleList(&newList, bc.contracts.NEO.GetNextBlockValidatorsInternal())
|
||||
bc.updateExtensibleList(&newList, bc.contracts.NEO.GetNextBlockValidatorsInternal(bc.dao))
|
||||
|
||||
if len(stateVals) > 0 {
|
||||
h, err := bc.contracts.Designate.GetLastDesignatedHash(bc.dao, noderoles.StateValidator)
|
||||
|
@ -1454,12 +1465,12 @@ func (bc *Blockchain) ForEachNEP11Transfer(acc util.Uint160, newestTimestamp uin
|
|||
|
||||
// GetNEP17Contracts returns the list of deployed NEP-17 contracts.
|
||||
func (bc *Blockchain) GetNEP17Contracts() []util.Uint160 {
|
||||
return bc.contracts.Management.GetNEP17Contracts()
|
||||
return bc.contracts.Management.GetNEP17Contracts(bc.dao)
|
||||
}
|
||||
|
||||
// GetNEP11Contracts returns the list of deployed NEP-11 contracts.
|
||||
func (bc *Blockchain) GetNEP11Contracts() []util.Uint160 {
|
||||
return bc.contracts.Management.GetNEP11Contracts()
|
||||
return bc.contracts.Management.GetNEP11Contracts(bc.dao)
|
||||
}
|
||||
|
||||
// GetTokenLastUpdated returns a set of contract ids with the corresponding last updated
|
||||
|
@ -1826,7 +1837,7 @@ func (bc *Blockchain) ApplyPolicyToTxSet(txes []*transaction.Transaction) []*tra
|
|||
curVC := bc.config.GetNumOfCNs(bc.BlockHeight() + 1)
|
||||
if oldVC == nil || oldVC != curVC {
|
||||
m := smartcontract.GetDefaultHonestNodeCount(curVC)
|
||||
verification, _ := smartcontract.CreateDefaultMultiSigRedeemScript(bc.contracts.NEO.GetNextBlockValidatorsInternal())
|
||||
verification, _ := smartcontract.CreateDefaultMultiSigRedeemScript(bc.contracts.NEO.GetNextBlockValidatorsInternal(bc.dao))
|
||||
defaultWitness = transaction.Witness{
|
||||
InvocationScript: make([]byte, 66*m),
|
||||
VerificationScript: verification,
|
||||
|
@ -1942,7 +1953,7 @@ func (bc *Blockchain) verifyAndPoolTx(t *transaction.Transaction, pool *mempool.
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := bc.verifyTxAttributes(t, isPartialTx); err != nil {
|
||||
if err := bc.verifyTxAttributes(bc.dao, t, isPartialTx); err != nil {
|
||||
return err
|
||||
}
|
||||
err = pool.Add(t, feer, data...)
|
||||
|
@ -1966,11 +1977,11 @@ func (bc *Blockchain) verifyAndPoolTx(t *transaction.Transaction, pool *mempool.
|
|||
return nil
|
||||
}
|
||||
|
||||
func (bc *Blockchain) verifyTxAttributes(tx *transaction.Transaction, isPartialTx bool) error {
|
||||
func (bc *Blockchain) verifyTxAttributes(d *dao.Simple, tx *transaction.Transaction, isPartialTx bool) error {
|
||||
for i := range tx.Attributes {
|
||||
switch attrType := tx.Attributes[i].Type; attrType {
|
||||
case transaction.HighPriority:
|
||||
h := bc.contracts.NEO.GetCommitteeAddress()
|
||||
h := bc.contracts.NEO.GetCommitteeAddress(d)
|
||||
if !tx.HasSigner(h) {
|
||||
return fmt.Errorf("%w: high priority tx is not signed by committee", ErrInvalidAttribute)
|
||||
}
|
||||
|
@ -2064,7 +2075,7 @@ func (bc *Blockchain) IsTxStillRelevant(t *transaction.Transaction, txpool *memp
|
|||
} else if txpool.HasConflicts(t, bc) {
|
||||
return false
|
||||
}
|
||||
if err := bc.verifyTxAttributes(t, isPartialTx); err != nil {
|
||||
if err := bc.verifyTxAttributes(bc.dao, t, isPartialTx); err != nil {
|
||||
return false
|
||||
}
|
||||
for i := range t.Scripts {
|
||||
|
@ -2122,7 +2133,7 @@ func (bc *Blockchain) PoolTxWithData(t *transaction.Transaction, data interface{
|
|||
|
||||
// GetCommittee returns the sorted list of public keys of nodes in committee.
|
||||
func (bc *Blockchain) GetCommittee() (keys.PublicKeys, error) {
|
||||
pubs := bc.contracts.NEO.GetCommitteeMembers()
|
||||
pubs := bc.contracts.NEO.GetCommitteeMembers(bc.dao)
|
||||
sort.Sort(pubs)
|
||||
return pubs, nil
|
||||
}
|
||||
|
@ -2134,7 +2145,7 @@ func (bc *Blockchain) GetValidators() ([]*keys.PublicKey, error) {
|
|||
|
||||
// GetNextBlockValidators returns next block validators.
|
||||
func (bc *Blockchain) GetNextBlockValidators() ([]*keys.PublicKey, error) {
|
||||
return bc.contracts.NEO.GetNextBlockValidatorsInternal(), nil
|
||||
return bc.contracts.NEO.GetNextBlockValidatorsInternal(bc.dao), nil
|
||||
}
|
||||
|
||||
// GetEnrollments returns all registered validators.
|
||||
|
@ -2173,6 +2184,12 @@ func (bc *Blockchain) GetTestHistoricVM(t trigger.Type, tx *transaction.Transact
|
|||
s := mpt.NewTrieStore(sr.Root, mode, storage.NewPrivateMemCachedStore(bc.dao.Store))
|
||||
dTrie := dao.NewSimple(s, bc.config.StateRootInHeader, bc.config.P2PSigExtensions)
|
||||
dTrie.Version = bc.dao.Version
|
||||
// Initialize native cache before passing DAO to interop context constructor, because
|
||||
// the constructor will call BaseExecFee/StoragePrice policy methods on the passed DAO.
|
||||
err = bc.initializeNativeCache(dTrie)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize native cache backed by historic DAO: %w", err)
|
||||
}
|
||||
systemInterop := bc.newInteropContext(t, dTrie, b, tx)
|
||||
vm := systemInterop.SpawnVM()
|
||||
vm.SetPriceGetter(systemInterop.GetPrice)
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||
"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/dao"
|
||||
"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"
|
||||
|
@ -329,7 +328,7 @@ func TestBlockchain_BaseExecFeeBaseStoragePrice_Compat(t *testing.T) {
|
|||
bc := newTestChain(t)
|
||||
|
||||
check := func(t *testing.T) {
|
||||
ic := bc.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), bc.config.StateRootInHeader, bc.config.P2PSigExtensions), bc.topBlock.Load().(*block.Block), nil)
|
||||
ic := bc.newInteropContext(trigger.Application, bc.dao, bc.topBlock.Load().(*block.Block), nil)
|
||||
require.Equal(t, bc.GetBaseExecFee(), ic.BaseExecFee())
|
||||
require.Equal(t, bc.GetStoragePrice(), ic.BaseStorageFee())
|
||||
}
|
||||
|
|
|
@ -530,10 +530,10 @@ func TestStorageFind(t *testing.T) {
|
|||
|
||||
// Helper functions to create VM, InteropContext, TX, Account, Contract.
|
||||
|
||||
func createVM(t *testing.T) (*vm.VM, *interop.Context, *Blockchain) {
|
||||
func createVM(t testing.TB) (*vm.VM, *interop.Context, *Blockchain) {
|
||||
chain := newTestChain(t)
|
||||
context := chain.newInteropContext(trigger.Application,
|
||||
dao.NewSimple(storage.NewMemoryStore(), chain.config.StateRootInHeader, chain.config.P2PSigExtensions), nil, nil)
|
||||
dao.NewSimple(chain.dao.Store, chain.config.StateRootInHeader, chain.config.P2PSigExtensions), nil, nil)
|
||||
v := context.SpawnVM()
|
||||
return v, context, chain
|
||||
}
|
||||
|
@ -552,10 +552,7 @@ func createVMAndContractState(t testing.TB) (*vm.VM, *state.Contract, *interop.C
|
|||
},
|
||||
}
|
||||
|
||||
chain := newTestChain(t)
|
||||
d := dao.NewSimple(storage.NewMemoryStore(), chain.config.StateRootInHeader, chain.config.P2PSigExtensions)
|
||||
context := chain.newInteropContext(trigger.Application, d, nil, nil)
|
||||
v := context.SpawnVM()
|
||||
v, context, chain := createVM(t)
|
||||
return v, contractState, context, chain
|
||||
}
|
||||
|
||||
|
|
|
@ -5,9 +5,7 @@ import (
|
|||
"runtime"
|
||||
"testing"
|
||||
|
||||
"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/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -17,8 +15,7 @@ func testNonInterop(t *testing.T, value interface{}, f func(*interop.Context) er
|
|||
v := vm.New()
|
||||
v.Estack().PushVal(value)
|
||||
chain := newTestChain(t)
|
||||
d := dao.NewSimple(storage.NewMemoryStore(), chain.config.StateRootInHeader, chain.config.P2PSigExtensions)
|
||||
context := chain.newInteropContext(trigger.Application, d, nil, nil)
|
||||
context := chain.newInteropContext(trigger.Application, chain.dao, nil, nil)
|
||||
context.VM = v
|
||||
require.Error(t, f(context))
|
||||
}
|
||||
|
|
|
@ -31,12 +31,6 @@ type Designate struct {
|
|||
interop.ContractMD
|
||||
NEO *NEO
|
||||
|
||||
rolesChangedFlag atomic.Value
|
||||
oracles atomic.Value
|
||||
stateVals atomic.Value
|
||||
neofsAlphabet atomic.Value
|
||||
notaries atomic.Value
|
||||
|
||||
// p2pSigExtensionsEnabled defines whether the P2P signature extensions logic is relevant.
|
||||
p2pSigExtensionsEnabled bool
|
||||
|
||||
|
@ -53,6 +47,14 @@ type roleData struct {
|
|||
height uint32
|
||||
}
|
||||
|
||||
type DesignationCache struct {
|
||||
rolesChangedFlag atomic.Value
|
||||
oracles atomic.Value
|
||||
stateVals atomic.Value
|
||||
neofsAlphabet atomic.Value
|
||||
notaries atomic.Value
|
||||
}
|
||||
|
||||
const (
|
||||
designateContractID = -8
|
||||
|
||||
|
@ -104,6 +106,9 @@ func newDesignate(p2pSigExtensionsEnabled bool) *Designate {
|
|||
|
||||
// Initialize initializes Oracle contract.
|
||||
func (s *Designate) Initialize(ic *interop.Context) error {
|
||||
cache := &DesignationCache{}
|
||||
cache.rolesChangedFlag.Store(true)
|
||||
ic.DAO.Store.SetCache(s.ID, cache)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -114,26 +119,27 @@ func (s *Designate) OnPersist(ic *interop.Context) error {
|
|||
|
||||
// PostPersist implements Contract interface.
|
||||
func (s *Designate) PostPersist(ic *interop.Context) error {
|
||||
if !s.rolesChanged() {
|
||||
cache := ic.DAO.Store.GetCache(s.ID).(*DesignationCache)
|
||||
if !rolesChanged(cache) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.updateCachedRoleData(&s.oracles, ic.DAO, noderoles.Oracle); err != nil {
|
||||
if err := s.updateCachedRoleData(&cache.oracles, ic.DAO, noderoles.Oracle); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.updateCachedRoleData(&s.stateVals, ic.DAO, noderoles.StateValidator); err != nil {
|
||||
if err := s.updateCachedRoleData(&cache.stateVals, ic.DAO, noderoles.StateValidator); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.updateCachedRoleData(&s.neofsAlphabet, ic.DAO, noderoles.NeoFSAlphabet); err != nil {
|
||||
if err := s.updateCachedRoleData(&cache.neofsAlphabet, ic.DAO, noderoles.NeoFSAlphabet); err != nil {
|
||||
return err
|
||||
}
|
||||
if s.p2pSigExtensionsEnabled {
|
||||
if err := s.updateCachedRoleData(&s.notaries, ic.DAO, noderoles.P2PNotary); err != nil {
|
||||
if err := s.updateCachedRoleData(&cache.notaries, ic.DAO, noderoles.P2PNotary); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s.rolesChangedFlag.Store(false)
|
||||
cache.rolesChangedFlag.Store(false)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -162,8 +168,8 @@ func (s *Designate) getDesignatedByRole(ic *interop.Context, args []stackitem.It
|
|||
return pubsToArray(pubs)
|
||||
}
|
||||
|
||||
func (s *Designate) rolesChanged() bool {
|
||||
rc := s.rolesChangedFlag.Load()
|
||||
func rolesChanged(cache *DesignationCache) bool {
|
||||
rc := cache.rolesChangedFlag.Load()
|
||||
return rc == nil || rc.(bool)
|
||||
}
|
||||
|
||||
|
@ -208,17 +214,17 @@ func (s *Designate) updateCachedRoleData(v *atomic.Value, d *dao.Simple, r noder
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Designate) getCachedRoleData(r noderoles.Role) *roleData {
|
||||
func getCachedRoleData(cache *DesignationCache, r noderoles.Role) *roleData {
|
||||
var val interface{}
|
||||
switch r {
|
||||
case noderoles.Oracle:
|
||||
val = s.oracles.Load()
|
||||
val = cache.oracles.Load()
|
||||
case noderoles.StateValidator:
|
||||
val = s.stateVals.Load()
|
||||
val = cache.stateVals.Load()
|
||||
case noderoles.NeoFSAlphabet:
|
||||
val = s.neofsAlphabet.Load()
|
||||
val = cache.neofsAlphabet.Load()
|
||||
case noderoles.P2PNotary:
|
||||
val = s.notaries.Load()
|
||||
val = cache.notaries.Load()
|
||||
}
|
||||
if val != nil {
|
||||
return val.(*roleData)
|
||||
|
@ -231,8 +237,9 @@ func (s *Designate) GetLastDesignatedHash(d *dao.Simple, r noderoles.Role) (util
|
|||
if !s.isValidRole(r) {
|
||||
return util.Uint160{}, ErrInvalidRole
|
||||
}
|
||||
if !s.rolesChanged() {
|
||||
if val := s.getCachedRoleData(r); val != nil {
|
||||
cache := d.Store.GetCache(s.ID).(*DesignationCache)
|
||||
if !rolesChanged(cache) {
|
||||
if val := getCachedRoleData(cache, r); val != nil {
|
||||
return val.addr, nil
|
||||
}
|
||||
}
|
||||
|
@ -249,8 +256,9 @@ func (s *Designate) GetDesignatedByRole(d *dao.Simple, r noderoles.Role, index u
|
|||
if !s.isValidRole(r) {
|
||||
return nil, 0, ErrInvalidRole
|
||||
}
|
||||
if !s.rolesChanged() {
|
||||
if val := s.getCachedRoleData(r); val != nil && val.height <= index {
|
||||
cache := d.Store.GetCache(s.ID).(*DesignationCache)
|
||||
if !rolesChanged(cache) {
|
||||
if val := getCachedRoleData(cache, r); val != nil && val.height <= index {
|
||||
return val.nodes.Copy(), val.height, nil
|
||||
}
|
||||
}
|
||||
|
@ -310,7 +318,7 @@ func (s *Designate) DesignateAsRole(ic *interop.Context, r noderoles.Role, pubs
|
|||
if !s.isValidRole(r) {
|
||||
return ErrInvalidRole
|
||||
}
|
||||
h := s.NEO.GetCommitteeAddress()
|
||||
h := s.NEO.GetCommitteeAddress(ic.DAO)
|
||||
if ok, err := runtime.CheckHashedWitness(ic, h); err != nil || !ok {
|
||||
return ErrInvalidWitness
|
||||
}
|
||||
|
@ -327,7 +335,7 @@ func (s *Designate) DesignateAsRole(ic *interop.Context, r noderoles.Role, pubs
|
|||
}
|
||||
sort.Sort(pubs)
|
||||
nl := NodeList(pubs)
|
||||
s.rolesChangedFlag.Store(true)
|
||||
ic.DAO.Store.GetCache(s.ID).(*DesignationCache).rolesChangedFlag.Store(true)
|
||||
err := putConvertibleToDAO(s.ID, ic.DAO, key, &nl)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -357,6 +365,8 @@ func (s *Designate) getRole(item stackitem.Item) (noderoles.Role, bool) {
|
|||
}
|
||||
|
||||
// InitializeCache invalidates native Designate cache.
|
||||
func (s *Designate) InitializeCache() {
|
||||
s.rolesChangedFlag.Store(true)
|
||||
func (s *Designate) InitializeCache(d *dao.Simple) {
|
||||
cache := &DesignationCache{}
|
||||
cache.rolesChangedFlag.Store(true)
|
||||
d.Store.SetCache(s.ID, cache)
|
||||
}
|
||||
|
|
|
@ -30,7 +30,9 @@ import (
|
|||
type Management struct {
|
||||
interop.ContractMD
|
||||
NEO *NEO
|
||||
}
|
||||
|
||||
type ManagementCache struct {
|
||||
mtx sync.RWMutex
|
||||
contracts map[util.Uint160]*state.Contract
|
||||
// nep11 is a map of NEP11-compliant contracts which is updated with every PostPersist.
|
||||
|
@ -66,9 +68,6 @@ func MakeContractKey(h util.Uint160) []byte {
|
|||
func newManagement() *Management {
|
||||
var m = &Management{
|
||||
ContractMD: *interop.NewContractMD(nativenames.Management, ManagementContractID),
|
||||
contracts: make(map[util.Uint160]*state.Contract),
|
||||
nep11: make(map[util.Uint160]struct{}),
|
||||
nep17: make(map[util.Uint160]struct{}),
|
||||
}
|
||||
defer m.UpdateHash()
|
||||
|
||||
|
@ -146,9 +145,10 @@ func (m *Management) getContract(ic *interop.Context, args []stackitem.Item) sta
|
|||
|
||||
// GetContract returns contract with given hash from given DAO.
|
||||
func (m *Management) GetContract(d *dao.Simple, hash util.Uint160) (*state.Contract, error) {
|
||||
m.mtx.RLock()
|
||||
cs, ok := m.contracts[hash]
|
||||
m.mtx.RUnlock()
|
||||
cache := d.Store.GetCache(m.ID).(*ManagementCache)
|
||||
cache.mtx.RLock()
|
||||
cs, ok := cache.contracts[hash]
|
||||
cache.mtx.RUnlock()
|
||||
if !ok {
|
||||
return nil, storage.ErrKeyNotFound
|
||||
} else if cs != nil {
|
||||
|
@ -260,11 +260,12 @@ func (m *Management) deployWithData(ic *interop.Context, args []stackitem.Item)
|
|||
return contractToStack(newcontract)
|
||||
}
|
||||
|
||||
func (m *Management) markUpdated(h util.Uint160) {
|
||||
m.mtx.Lock()
|
||||
func (m *Management) markUpdated(d *dao.Simple, h util.Uint160) {
|
||||
cache := d.Store.GetCache(m.ID).(*ManagementCache)
|
||||
cache.mtx.Lock()
|
||||
// Just set it to nil, to refresh cache in `PostPersist`.
|
||||
m.contracts[h] = nil
|
||||
m.mtx.Unlock()
|
||||
cache.contracts[h] = nil
|
||||
cache.mtx.Unlock()
|
||||
}
|
||||
|
||||
// Deploy creates contract's hash/ID and saves new contract into the given DAO.
|
||||
|
@ -300,7 +301,7 @@ func (m *Management) Deploy(d *dao.Simple, sender util.Uint160, neff *nef.File,
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.markUpdated(newcontract.Hash)
|
||||
m.markUpdated(d, newcontract.Hash)
|
||||
return newcontract, nil
|
||||
}
|
||||
|
||||
|
@ -340,7 +341,7 @@ func (m *Management) Update(d *dao.Simple, hash util.Uint160, neff *nef.File, ma
|
|||
contract = *oldcontract // Make a copy, don't ruin (potentially) cached contract.
|
||||
// if NEF was provided, update the contract script
|
||||
if neff != nil {
|
||||
m.markUpdated(hash)
|
||||
m.markUpdated(d, hash)
|
||||
contract.NEF = *neff
|
||||
}
|
||||
// if manifest was provided, update the contract manifest
|
||||
|
@ -352,7 +353,7 @@ func (m *Management) Update(d *dao.Simple, hash util.Uint160, neff *nef.File, ma
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid manifest: %w", err)
|
||||
}
|
||||
m.markUpdated(hash)
|
||||
m.markUpdated(d, hash)
|
||||
contract.Manifest = *manif
|
||||
}
|
||||
err = checkScriptAndMethods(contract.NEF.Script, contract.Manifest.ABI.Methods)
|
||||
|
@ -393,7 +394,7 @@ func (m *Management) Destroy(d *dao.Simple, hash util.Uint160) error {
|
|||
d.DeleteStorageItem(contract.ID, k)
|
||||
return true
|
||||
})
|
||||
m.markUpdated(hash)
|
||||
m.markUpdated(d, hash)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -444,18 +445,19 @@ func (m *Management) Metadata() *interop.ContractMD {
|
|||
|
||||
// updateContractCache saves contract in the common and NEP-related caches. It's
|
||||
// an internal method that must be called with m.mtx lock taken.
|
||||
func (m *Management) updateContractCache(cs *state.Contract) {
|
||||
m.contracts[cs.Hash] = cs
|
||||
func updateContractCache(cache *ManagementCache, cs *state.Contract) {
|
||||
cache.contracts[cs.Hash] = cs
|
||||
if cs.Manifest.IsStandardSupported(manifest.NEP11StandardName) {
|
||||
m.nep11[cs.Hash] = struct{}{}
|
||||
cache.nep11[cs.Hash] = struct{}{}
|
||||
}
|
||||
if cs.Manifest.IsStandardSupported(manifest.NEP17StandardName) {
|
||||
m.nep17[cs.Hash] = struct{}{}
|
||||
cache.nep17[cs.Hash] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// OnPersist implements Contract interface.
|
||||
func (m *Management) OnPersist(ic *interop.Context) error {
|
||||
var cache *ManagementCache
|
||||
for _, native := range ic.Natives {
|
||||
md := native.Metadata()
|
||||
history := md.UpdateHistory
|
||||
|
@ -466,16 +468,19 @@ func (m *Management) OnPersist(ic *interop.Context) error {
|
|||
cs := &state.Contract{
|
||||
ContractBase: md.ContractBase,
|
||||
}
|
||||
if err := native.Initialize(ic); err != nil {
|
||||
return fmt.Errorf("initializing %s native contract: %w", md.Name, err)
|
||||
}
|
||||
err := m.PutContractState(ic.DAO, cs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := native.Initialize(ic); err != nil {
|
||||
return fmt.Errorf("initializing %s native contract: %w", md.Name, err)
|
||||
if cache == nil {
|
||||
cache = ic.DAO.Store.GetCache(m.ID).(*ManagementCache)
|
||||
}
|
||||
m.mtx.Lock()
|
||||
m.updateContractCache(cs)
|
||||
m.mtx.Unlock()
|
||||
cache.mtx.Lock()
|
||||
updateContractCache(cache, cs)
|
||||
cache.mtx.Unlock()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -485,8 +490,11 @@ func (m *Management) OnPersist(ic *interop.Context) error {
|
|||
// Cache initialisation should be done apart from Initialize because Initialize is
|
||||
// called only when deploying native contracts.
|
||||
func (m *Management) InitializeCache(d *dao.Simple) error {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
cache := &ManagementCache{
|
||||
contracts: make(map[util.Uint160]*state.Contract),
|
||||
nep11: make(map[util.Uint160]struct{}),
|
||||
nep17: make(map[util.Uint160]struct{}),
|
||||
}
|
||||
|
||||
var initErr error
|
||||
d.Seek(m.ID, storage.SeekRange{Prefix: []byte{prefixContract}}, func(_, v []byte) bool {
|
||||
|
@ -495,56 +503,63 @@ func (m *Management) InitializeCache(d *dao.Simple) error {
|
|||
if initErr != nil {
|
||||
return false
|
||||
}
|
||||
m.updateContractCache(cs)
|
||||
updateContractCache(cache, cs)
|
||||
return true
|
||||
})
|
||||
return initErr
|
||||
if initErr != nil {
|
||||
return initErr
|
||||
}
|
||||
d.Store.SetCache(m.ID, cache)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PostPersist implements Contract interface.
|
||||
func (m *Management) PostPersist(ic *interop.Context) error {
|
||||
m.mtx.Lock()
|
||||
for h, cs := range m.contracts {
|
||||
cache := ic.DAO.Store.GetCache(m.ID).(*ManagementCache)
|
||||
cache.mtx.Lock()
|
||||
defer cache.mtx.Unlock()
|
||||
for h, cs := range cache.contracts {
|
||||
if cs != nil {
|
||||
continue
|
||||
}
|
||||
delete(m.nep11, h)
|
||||
delete(m.nep17, h)
|
||||
delete(cache.nep11, h)
|
||||
delete(cache.nep17, h)
|
||||
newCs, err := m.getContractFromDAO(ic.DAO, h)
|
||||
if err != nil {
|
||||
// Contract was destroyed.
|
||||
delete(m.contracts, h)
|
||||
delete(cache.contracts, h)
|
||||
continue
|
||||
}
|
||||
m.updateContractCache(newCs)
|
||||
updateContractCache(cache, newCs)
|
||||
}
|
||||
m.mtx.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNEP11Contracts returns hashes of all deployed contracts that support NEP-11 standard. The list
|
||||
// is updated every PostPersist, so until PostPersist is called, the result for the previous block
|
||||
// is returned.
|
||||
func (m *Management) GetNEP11Contracts() []util.Uint160 {
|
||||
m.mtx.RLock()
|
||||
result := make([]util.Uint160, 0, len(m.nep11))
|
||||
for h := range m.nep11 {
|
||||
func (m *Management) GetNEP11Contracts(d *dao.Simple) []util.Uint160 {
|
||||
cache := d.Store.GetCache(m.ID).(*ManagementCache)
|
||||
cache.mtx.RLock()
|
||||
result := make([]util.Uint160, 0, len(cache.nep11))
|
||||
for h := range cache.nep11 {
|
||||
result = append(result, h)
|
||||
}
|
||||
m.mtx.RUnlock()
|
||||
cache.mtx.RUnlock()
|
||||
return result
|
||||
}
|
||||
|
||||
// GetNEP17Contracts returns hashes of all deployed contracts that support NEP-17 standard. The list
|
||||
// is updated every PostPersist, so until PostPersist is called, the result for the previous block
|
||||
// is returned.
|
||||
func (m *Management) GetNEP17Contracts() []util.Uint160 {
|
||||
m.mtx.RLock()
|
||||
result := make([]util.Uint160, 0, len(m.nep17))
|
||||
for h := range m.nep17 {
|
||||
func (m *Management) GetNEP17Contracts(d *dao.Simple) []util.Uint160 {
|
||||
cache := d.Store.GetCache(m.ID).(*ManagementCache)
|
||||
cache.mtx.RLock()
|
||||
result := make([]util.Uint160, 0, len(cache.nep17))
|
||||
for h := range cache.nep17 {
|
||||
result = append(result, h)
|
||||
}
|
||||
m.mtx.RUnlock()
|
||||
cache.mtx.RUnlock()
|
||||
return result
|
||||
}
|
||||
|
||||
|
@ -552,6 +567,13 @@ func (m *Management) GetNEP17Contracts() []util.Uint160 {
|
|||
func (m *Management) Initialize(ic *interop.Context) error {
|
||||
setIntWithKey(m.ID, ic.DAO, keyMinimumDeploymentFee, defaultMinimumDeploymentFee)
|
||||
setIntWithKey(m.ID, ic.DAO, keyNextAvailableID, 1)
|
||||
|
||||
cache := &ManagementCache{
|
||||
contracts: make(map[util.Uint160]*state.Contract),
|
||||
nep11: make(map[util.Uint160]struct{}),
|
||||
nep17: make(map[util.Uint160]struct{}),
|
||||
}
|
||||
ic.DAO.Store.SetCache(m.ID, cache)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -561,7 +583,7 @@ func (m *Management) PutContractState(d *dao.Simple, cs *state.Contract) error {
|
|||
if err := putConvertibleToDAO(m.ID, d, key, cs); err != nil {
|
||||
return err
|
||||
}
|
||||
m.markUpdated(cs.Hash)
|
||||
m.markUpdated(d, cs.Hash)
|
||||
if cs.UpdateCounter != 0 { // Update.
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -89,8 +89,10 @@ func TestManagement_GetNEP17Contracts(t *testing.T) {
|
|||
d := dao.NewSimple(storage.NewMemoryStore(), false, false)
|
||||
err := mgmt.Initialize(&interop.Context{DAO: d})
|
||||
require.NoError(t, err)
|
||||
err = mgmt.InitializeCache(d)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Empty(t, mgmt.GetNEP17Contracts())
|
||||
require.Empty(t, mgmt.GetNEP17Contracts(d))
|
||||
|
||||
// Deploy NEP-17 contract
|
||||
script := []byte{byte(opcode.RET)}
|
||||
|
@ -108,11 +110,11 @@ func TestManagement_GetNEP17Contracts(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
// PostPersist is not yet called, thus no NEP-17 contracts are expected
|
||||
require.Empty(t, mgmt.GetNEP17Contracts())
|
||||
require.Empty(t, mgmt.GetNEP17Contracts(d))
|
||||
|
||||
// Call PostPersist, check c1 contract hash is returned
|
||||
require.NoError(t, mgmt.PostPersist(&interop.Context{DAO: d}))
|
||||
require.Equal(t, []util.Uint160{c1.Hash}, mgmt.GetNEP17Contracts())
|
||||
require.Equal(t, []util.Uint160{c1.Hash}, mgmt.GetNEP17Contracts(d))
|
||||
|
||||
// Update contract
|
||||
manif.ABI.Methods = append(manif.ABI.Methods, manifest.Method{
|
||||
|
@ -124,9 +126,9 @@ func TestManagement_GetNEP17Contracts(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
// No changes expected before PostPersist call.
|
||||
require.Equal(t, []util.Uint160{c1.Hash}, mgmt.GetNEP17Contracts())
|
||||
require.Equal(t, []util.Uint160{c1.Hash}, mgmt.GetNEP17Contracts(d))
|
||||
|
||||
// Call PostPersist, check c2 contract hash is returned
|
||||
require.NoError(t, mgmt.PostPersist(&interop.Context{DAO: d}))
|
||||
require.Equal(t, []util.Uint160{c2.Hash}, mgmt.GetNEP17Contracts())
|
||||
require.Equal(t, []util.Uint160{c2.Hash}, mgmt.GetNEP17Contracts(d))
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ func (g *GAS) OnPersist(ic *interop.Context) error {
|
|||
absAmount := big.NewInt(tx.SystemFee + tx.NetworkFee)
|
||||
g.burn(ic, tx.Sender(), absAmount)
|
||||
}
|
||||
validators := g.NEO.GetNextBlockValidatorsInternal()
|
||||
validators := g.NEO.GetNextBlockValidatorsInternal(ic.DAO)
|
||||
primary := validators[ic.Block.PrimaryIndex].GetScriptHash()
|
||||
var netFee int64
|
||||
for _, tx := range ic.Block.Transactions {
|
||||
|
|
|
@ -35,6 +35,13 @@ type NEO struct {
|
|||
GAS *GAS
|
||||
Policy *Policy
|
||||
|
||||
// Configuration and standby keys are set in constructor and then
|
||||
// only read from.
|
||||
cfg config.ProtocolConfiguration
|
||||
standbyKeys keys.PublicKeys
|
||||
}
|
||||
|
||||
type NeoCache struct {
|
||||
// gasPerBlock represents current value of generated gas per block.
|
||||
// It is append-only and doesn't need to be copied when used.
|
||||
gasPerBlock atomic.Value
|
||||
|
@ -58,11 +65,6 @@ type NEO struct {
|
|||
// It is set in state-modifying methods only and read in `PostPersist` thus is not protected
|
||||
// by any mutex.
|
||||
gasPerVoteCache map[string]big.Int
|
||||
|
||||
// Configuration and standby keys are set in constructor and then
|
||||
// only read from.
|
||||
cfg config.ProtocolConfiguration
|
||||
standbyKeys keys.PublicKeys
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -129,13 +131,6 @@ func newNEO(cfg config.ProtocolConfiguration) *NEO {
|
|||
nep17.balFromBytes = n.balanceFromBytes
|
||||
|
||||
n.nep17TokenNative = *nep17
|
||||
n.votesChanged.Store(true)
|
||||
n.nextValidators.Store(keys.PublicKeys(nil))
|
||||
n.validators.Store(keys.PublicKeys(nil))
|
||||
n.committee.Store(keysWithVotes(nil))
|
||||
n.committeeHash.Store(util.Uint160{})
|
||||
n.registerPriceChanged.Store(true)
|
||||
n.gasPerVoteCache = make(map[string]big.Int)
|
||||
|
||||
err := n.initConfigCache(cfg)
|
||||
if err != nil {
|
||||
|
@ -213,9 +208,22 @@ func (n *NEO) Initialize(ic *interop.Context) error {
|
|||
return errors.New("already initialized")
|
||||
}
|
||||
|
||||
cache := &NeoCache{
|
||||
gasPerVoteCache: make(map[string]big.Int),
|
||||
}
|
||||
cache.votesChanged.Store(true)
|
||||
cache.nextValidators.Store(keys.PublicKeys(nil))
|
||||
cache.validators.Store(keys.PublicKeys(nil))
|
||||
cache.committee.Store(keysWithVotes(nil))
|
||||
cache.committeeHash.Store(util.Uint160{})
|
||||
cache.registerPriceChanged.Store(true)
|
||||
|
||||
// We need cache to be present in DAO before the subsequent call to `mint`.
|
||||
ic.DAO.Store.SetCache(n.ID, cache)
|
||||
|
||||
committee0 := n.standbyKeys[:n.cfg.GetCommitteeSize(ic.Block.Index)]
|
||||
cvs := toKeysWithVotes(committee0)
|
||||
err := n.updateCache(cvs, ic.Chain)
|
||||
err := n.updateCache(cache, cvs, ic.Chain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -233,13 +241,14 @@ func (n *NEO) Initialize(ic *interop.Context) error {
|
|||
n.putGASRecord(ic.DAO, index, value)
|
||||
|
||||
gr := &gasRecord{{Index: index, GASPerBlock: *value}}
|
||||
n.gasPerBlock.Store(*gr)
|
||||
n.gasPerBlockChanged.Store(false)
|
||||
cache.gasPerBlock.Store(*gr)
|
||||
cache.gasPerBlockChanged.Store(false)
|
||||
ic.DAO.PutStorageItem(n.ID, []byte{prefixVotersCount}, state.StorageItem{})
|
||||
|
||||
setIntWithKey(n.ID, ic.DAO, []byte{prefixRegisterPrice}, DefaultRegisterPrice)
|
||||
n.registerPrice.Store(int64(DefaultRegisterPrice))
|
||||
n.registerPriceChanged.Store(false)
|
||||
cache.registerPrice.Store(int64(DefaultRegisterPrice))
|
||||
cache.registerPriceChanged.Store(false)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -247,18 +256,30 @@ func (n *NEO) Initialize(ic *interop.Context) error {
|
|||
// Cache initialisation should be done apart from Initialize because Initialize is
|
||||
// called only when deploying native contracts.
|
||||
func (n *NEO) InitializeCache(bc interop.Ledger, d *dao.Simple) error {
|
||||
cache := &NeoCache{
|
||||
gasPerVoteCache: make(map[string]big.Int),
|
||||
}
|
||||
|
||||
cache.votesChanged.Store(true)
|
||||
cache.nextValidators.Store(keys.PublicKeys(nil))
|
||||
cache.validators.Store(keys.PublicKeys(nil))
|
||||
cache.committee.Store(keysWithVotes(nil))
|
||||
cache.committeeHash.Store(util.Uint160{})
|
||||
cache.registerPriceChanged.Store(true)
|
||||
|
||||
var committee = keysWithVotes{}
|
||||
si := d.GetStorageItem(n.ID, prefixCommittee)
|
||||
if err := committee.DecodeBytes(si); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to decode committee: %w", err)
|
||||
}
|
||||
if err := n.updateCache(committee, bc); err != nil {
|
||||
return err
|
||||
if err := n.updateCache(cache, committee, bc); err != nil {
|
||||
return fmt.Errorf("failed to update cache: %w", err)
|
||||
}
|
||||
|
||||
n.gasPerBlock.Store(n.getSortedGASRecordFromDAO(d))
|
||||
n.gasPerBlockChanged.Store(false)
|
||||
cache.gasPerBlock.Store(n.getSortedGASRecordFromDAO(d))
|
||||
cache.gasPerBlockChanged.Store(false)
|
||||
|
||||
d.Store.SetCache(n.ID, cache)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -270,27 +291,28 @@ func (n *NEO) initConfigCache(cfg config.ProtocolConfiguration) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (n *NEO) updateCache(cvs keysWithVotes, bc interop.Ledger) error {
|
||||
n.committee.Store(cvs)
|
||||
func (n *NEO) updateCache(cache *NeoCache, cvs keysWithVotes, bc interop.Ledger) error {
|
||||
cache.committee.Store(cvs)
|
||||
|
||||
var committee = n.GetCommitteeMembers()
|
||||
var committee = getCommitteeMembers(cache)
|
||||
script, err := smartcontract.CreateMajorityMultiSigRedeemScript(committee.Copy())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.committeeHash.Store(hash.Hash160(script))
|
||||
cache.committeeHash.Store(hash.Hash160(script))
|
||||
|
||||
// TODO: use block height from interop context for proper historical calls handling.
|
||||
nextVals := committee[:n.cfg.GetNumOfCNs(bc.BlockHeight()+1)].Copy()
|
||||
sort.Sort(nextVals)
|
||||
n.nextValidators.Store(nextVals)
|
||||
cache.nextValidators.Store(nextVals)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NEO) updateCommittee(ic *interop.Context) error {
|
||||
votesChanged := n.votesChanged.Load().(bool)
|
||||
func (n *NEO) updateCommittee(cache *NeoCache, ic *interop.Context) error {
|
||||
votesChanged := cache.votesChanged.Load().(bool)
|
||||
if !votesChanged {
|
||||
// We need to put in storage anyway, as it affects dumps
|
||||
committee := n.committee.Load().(keysWithVotes)
|
||||
committee := cache.committee.Load().(keysWithVotes)
|
||||
ic.DAO.PutStorageItem(n.ID, prefixCommittee, committee.Bytes())
|
||||
return nil
|
||||
}
|
||||
|
@ -299,10 +321,10 @@ func (n *NEO) updateCommittee(ic *interop.Context) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := n.updateCache(cvs, ic.Chain); err != nil {
|
||||
if err := n.updateCache(cache, cvs, ic.Chain); err != nil {
|
||||
return err
|
||||
}
|
||||
n.votesChanged.Store(false)
|
||||
cache.votesChanged.Store(false)
|
||||
ic.DAO.PutStorageItem(n.ID, prefixCommittee, cvs.Bytes())
|
||||
return nil
|
||||
}
|
||||
|
@ -310,13 +332,14 @@ func (n *NEO) updateCommittee(ic *interop.Context) error {
|
|||
// OnPersist implements Contract interface.
|
||||
func (n *NEO) OnPersist(ic *interop.Context) error {
|
||||
if n.cfg.ShouldUpdateCommitteeAt(ic.Block.Index) {
|
||||
oldKeys := n.nextValidators.Load().(keys.PublicKeys)
|
||||
oldCom := n.committee.Load().(keysWithVotes)
|
||||
cache := ic.DAO.Store.GetCache(n.ID).(*NeoCache)
|
||||
oldKeys := cache.nextValidators.Load().(keys.PublicKeys)
|
||||
oldCom := cache.committee.Load().(keysWithVotes)
|
||||
if n.cfg.GetNumOfCNs(ic.Block.Index) != len(oldKeys) ||
|
||||
n.cfg.GetCommitteeSize(ic.Block.Index) != len(oldCom) {
|
||||
n.votesChanged.Store(true)
|
||||
cache.votesChanged.Store(true)
|
||||
}
|
||||
if err := n.updateCommittee(ic); err != nil {
|
||||
if err := n.updateCommittee(cache, ic); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -326,7 +349,8 @@ func (n *NEO) OnPersist(ic *interop.Context) error {
|
|||
// PostPersist implements Contract interface.
|
||||
func (n *NEO) PostPersist(ic *interop.Context) error {
|
||||
gas := n.GetGASPerBlock(ic.DAO, ic.Block.Index)
|
||||
pubs := n.GetCommitteeMembers()
|
||||
cache := ic.DAO.Store.GetCache(n.ID).(*NeoCache)
|
||||
pubs := getCommitteeMembers(cache)
|
||||
committeeSize := n.cfg.GetCommitteeSize(ic.Block.Index)
|
||||
index := int(ic.Block.Index) % committeeSize
|
||||
committeeReward := new(big.Int).Mul(gas, bigCommitteeRewardRatio)
|
||||
|
@ -340,7 +364,7 @@ func (n *NEO) PostPersist(ic *interop.Context) error {
|
|||
voterReward.Div(voterReward, big.NewInt(int64(committeeSize+validatorsCount)))
|
||||
voterReward.Div(voterReward, big100)
|
||||
|
||||
var cs = n.committee.Load().(keysWithVotes)
|
||||
var cs = cache.committee.Load().(keysWithVotes)
|
||||
var key = make([]byte, 38)
|
||||
for i := range cs {
|
||||
if cs[i].Votes.Sign() > 0 {
|
||||
|
@ -356,7 +380,7 @@ func (n *NEO) PostPersist(ic *interop.Context) error {
|
|||
key = makeVoterKey([]byte(cs[i].Key), key)
|
||||
|
||||
var r *big.Int
|
||||
if g, ok := n.gasPerVoteCache[cs[i].Key]; ok {
|
||||
if g, ok := cache.gasPerVoteCache[cs[i].Key]; ok {
|
||||
r = &g
|
||||
} else {
|
||||
reward := n.getGASPerVote(ic.DAO, key[:34], []uint32{ic.Block.Index + 1})
|
||||
|
@ -365,21 +389,21 @@ func (n *NEO) PostPersist(ic *interop.Context) error {
|
|||
tmp.Add(tmp, r)
|
||||
|
||||
binary.BigEndian.PutUint32(key[34:], ic.Block.Index+1)
|
||||
n.gasPerVoteCache[cs[i].Key] = *tmp
|
||||
cache.gasPerVoteCache[cs[i].Key] = *tmp
|
||||
|
||||
ic.DAO.PutStorageItem(n.ID, key, bigint.ToBytes(tmp))
|
||||
}
|
||||
}
|
||||
}
|
||||
if n.gasPerBlockChanged.Load().(bool) {
|
||||
n.gasPerBlock.Store(n.getSortedGASRecordFromDAO(ic.DAO))
|
||||
n.gasPerBlockChanged.Store(false)
|
||||
if cache.gasPerBlockChanged.Load().(bool) {
|
||||
cache.gasPerBlock.Store(n.getSortedGASRecordFromDAO(ic.DAO))
|
||||
cache.gasPerBlockChanged.Store(false)
|
||||
}
|
||||
|
||||
if n.registerPriceChanged.Load().(bool) {
|
||||
if cache.registerPriceChanged.Load().(bool) {
|
||||
p := getIntWithKey(n.ID, ic.DAO, []byte{prefixRegisterPrice})
|
||||
n.registerPrice.Store(p)
|
||||
n.registerPriceChanged.Store(false)
|
||||
cache.registerPrice.Store(p)
|
||||
cache.registerPriceChanged.Store(false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -502,11 +526,12 @@ func (n *NEO) getSortedGASRecordFromDAO(d *dao.Simple) gasRecord {
|
|||
|
||||
// GetGASPerBlock returns gas generated for block with provided index.
|
||||
func (n *NEO) GetGASPerBlock(d *dao.Simple, index uint32) *big.Int {
|
||||
cache := d.Store.GetCache(n.ID).(*NeoCache)
|
||||
var gr gasRecord
|
||||
if n.gasPerBlockChanged.Load().(bool) {
|
||||
if cache.gasPerBlockChanged.Load().(bool) {
|
||||
gr = n.getSortedGASRecordFromDAO(d)
|
||||
} else {
|
||||
gr = n.gasPerBlock.Load().(gasRecord)
|
||||
gr = cache.gasPerBlock.Load().(gasRecord)
|
||||
}
|
||||
for i := len(gr) - 1; i >= 0; i-- {
|
||||
if gr[i].Index <= index {
|
||||
|
@ -518,12 +543,13 @@ func (n *NEO) GetGASPerBlock(d *dao.Simple, index uint32) *big.Int {
|
|||
}
|
||||
|
||||
// GetCommitteeAddress returns address of the committee.
|
||||
func (n *NEO) GetCommitteeAddress() util.Uint160 {
|
||||
return n.committeeHash.Load().(util.Uint160)
|
||||
func (n *NEO) GetCommitteeAddress(d *dao.Simple) util.Uint160 {
|
||||
cache := d.Store.GetCache(n.ID).(*NeoCache)
|
||||
return cache.committeeHash.Load().(util.Uint160)
|
||||
}
|
||||
|
||||
func (n *NEO) checkCommittee(ic *interop.Context) bool {
|
||||
ok, err := runtime.CheckHashedWitness(ic, n.GetCommitteeAddress())
|
||||
ok, err := runtime.CheckHashedWitness(ic, n.GetCommitteeAddress(ic.DAO))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -547,7 +573,8 @@ func (n *NEO) SetGASPerBlock(ic *interop.Context, index uint32, gas *big.Int) er
|
|||
if !n.checkCommittee(ic) {
|
||||
return errors.New("invalid committee signature")
|
||||
}
|
||||
n.gasPerBlockChanged.Store(true)
|
||||
cache := ic.DAO.Store.GetCache(n.ID).(*NeoCache)
|
||||
cache.gasPerBlockChanged.Store(true)
|
||||
n.putGASRecord(ic.DAO, index, gas)
|
||||
return nil
|
||||
}
|
||||
|
@ -557,8 +584,9 @@ func (n *NEO) getRegisterPrice(ic *interop.Context, _ []stackitem.Item) stackite
|
|||
}
|
||||
|
||||
func (n *NEO) getRegisterPriceInternal(d *dao.Simple) int64 {
|
||||
if !n.registerPriceChanged.Load().(bool) {
|
||||
return n.registerPrice.Load().(int64)
|
||||
cache := d.Store.GetCache(n.ID).(*NeoCache)
|
||||
if !cache.registerPriceChanged.Load().(bool) {
|
||||
return cache.registerPrice.Load().(int64)
|
||||
}
|
||||
return getIntWithKey(n.ID, d, []byte{prefixRegisterPrice})
|
||||
}
|
||||
|
@ -573,11 +601,12 @@ func (n *NEO) setRegisterPrice(ic *interop.Context, args []stackitem.Item) stack
|
|||
}
|
||||
|
||||
setIntWithKey(n.ID, ic.DAO, []byte{prefixRegisterPrice}, price.Int64())
|
||||
n.registerPriceChanged.Store(true)
|
||||
cache := ic.DAO.Store.GetCache(n.ID).(*NeoCache)
|
||||
cache.registerPriceChanged.Store(true)
|
||||
return stackitem.Null{}
|
||||
}
|
||||
|
||||
func (n *NEO) dropCandidateIfZero(d *dao.Simple, pub *keys.PublicKey, c *candidate) (bool, error) {
|
||||
func (n *NEO) dropCandidateIfZero(d *dao.Simple, cache *NeoCache, pub *keys.PublicKey, c *candidate) (bool, error) {
|
||||
if c.Registered || c.Votes.Sign() != 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
@ -588,7 +617,7 @@ func (n *NEO) dropCandidateIfZero(d *dao.Simple, pub *keys.PublicKey, c *candida
|
|||
d.DeleteStorageItem(n.ID, append(voterKey, k...)) // d.Seek cuts prefix, thus need to append it again.
|
||||
return true
|
||||
})
|
||||
delete(n.gasPerVoteCache, string(voterKey))
|
||||
delete(cache.gasPerVoteCache, string(voterKey))
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
@ -643,8 +672,9 @@ func (n *NEO) CalculateNEOHolderReward(d *dao.Simple, value *big.Int, start, end
|
|||
return nil, errors.New("negative value")
|
||||
}
|
||||
var gr gasRecord
|
||||
if !n.gasPerBlockChanged.Load().(bool) {
|
||||
gr = n.gasPerBlock.Load().(gasRecord)
|
||||
cache := d.Store.GetCache(n.ID).(*NeoCache)
|
||||
if !cache.gasPerBlockChanged.Load().(bool) {
|
||||
gr = cache.gasPerBlock.Load().(gasRecord)
|
||||
} else {
|
||||
gr = n.getSortedGASRecordFromDAO(d)
|
||||
}
|
||||
|
@ -717,10 +747,11 @@ func (n *NEO) UnregisterCandidateInternal(ic *interop.Context, pub *keys.PublicK
|
|||
if si == nil {
|
||||
return nil
|
||||
}
|
||||
n.validators.Store(keys.PublicKeys(nil))
|
||||
cache := ic.DAO.Store.GetCache(n.ID).(*NeoCache)
|
||||
cache.validators.Store(keys.PublicKeys(nil))
|
||||
c := new(candidate).FromBytes(si)
|
||||
c.Registered = false
|
||||
ok, err := n.dropCandidateIfZero(ic.DAO, pub, c)
|
||||
ok, err := n.dropCandidateIfZero(ic.DAO, cache, pub, c)
|
||||
if ok {
|
||||
return err
|
||||
}
|
||||
|
@ -796,7 +827,8 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pub *keys.Public
|
|||
// ModifyAccountVotes modifies votes of the specified account by value (can be negative).
|
||||
// typ specifies if this modify is occurring during transfer or vote (with old or new validator).
|
||||
func (n *NEO) ModifyAccountVotes(acc *state.NEOBalance, d *dao.Simple, value *big.Int, isNewVote bool) error {
|
||||
n.votesChanged.Store(true)
|
||||
cache := d.Store.GetCache(n.ID).(*NeoCache)
|
||||
cache.votesChanged.Store(true)
|
||||
if acc.VoteTo != nil {
|
||||
key := makeValidatorKey(acc.VoteTo)
|
||||
si := d.GetStorageItem(n.ID, key)
|
||||
|
@ -806,12 +838,12 @@ func (n *NEO) ModifyAccountVotes(acc *state.NEOBalance, d *dao.Simple, value *bi
|
|||
cd := new(candidate).FromBytes(si)
|
||||
cd.Votes.Add(&cd.Votes, value)
|
||||
if !isNewVote {
|
||||
ok, err := n.dropCandidateIfZero(d, acc.VoteTo, cd)
|
||||
ok, err := n.dropCandidateIfZero(d, cache, acc.VoteTo, cd)
|
||||
if ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
n.validators.Store(keys.PublicKeys(nil))
|
||||
cache.validators.Store(keys.PublicKeys(nil))
|
||||
return putConvertibleToDAO(n.ID, d, key, cd)
|
||||
}
|
||||
return nil
|
||||
|
@ -906,7 +938,8 @@ func (n *NEO) getAccountState(ic *interop.Context, args []stackitem.Item) stacki
|
|||
// ComputeNextBlockValidators returns an actual list of current validators.
|
||||
func (n *NEO) ComputeNextBlockValidators(bc interop.Ledger, d *dao.Simple) (keys.PublicKeys, error) {
|
||||
numOfCNs := n.cfg.GetNumOfCNs(bc.BlockHeight() + 1)
|
||||
if vals := n.validators.Load().(keys.PublicKeys); vals != nil && numOfCNs == len(vals) {
|
||||
cache := d.Store.GetCache(n.ID).(*NeoCache)
|
||||
if vals := cache.validators.Load().(keys.PublicKeys); vals != nil && numOfCNs == len(vals) {
|
||||
return vals.Copy(), nil
|
||||
}
|
||||
result, _, err := n.computeCommitteeMembers(bc, d)
|
||||
|
@ -915,12 +948,12 @@ func (n *NEO) ComputeNextBlockValidators(bc interop.Ledger, d *dao.Simple) (keys
|
|||
}
|
||||
result = result[:numOfCNs]
|
||||
sort.Sort(result)
|
||||
n.validators.Store(result)
|
||||
cache.validators.Store(result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (n *NEO) getCommittee(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||
pubs := n.GetCommitteeMembers()
|
||||
pubs := n.GetCommitteeMembers(ic.DAO)
|
||||
sort.Sort(pubs)
|
||||
return pubsToArray(pubs)
|
||||
}
|
||||
|
@ -939,8 +972,13 @@ func (n *NEO) modifyVoterTurnout(d *dao.Simple, amount *big.Int) error {
|
|||
}
|
||||
|
||||
// GetCommitteeMembers returns public keys of nodes in committee using cached value.
|
||||
func (n *NEO) GetCommitteeMembers() keys.PublicKeys {
|
||||
var cvs = n.committee.Load().(keysWithVotes)
|
||||
func (n *NEO) GetCommitteeMembers(d *dao.Simple) keys.PublicKeys {
|
||||
cache := d.Store.GetCache(n.ID).(*NeoCache)
|
||||
return getCommitteeMembers(cache)
|
||||
}
|
||||
|
||||
func getCommitteeMembers(cache *NeoCache) keys.PublicKeys {
|
||||
var cvs = cache.committee.Load().(keysWithVotes)
|
||||
var committee = make(keys.PublicKeys, len(cvs))
|
||||
var err error
|
||||
for i := range committee {
|
||||
|
@ -1009,13 +1047,14 @@ func (n *NEO) computeCommitteeMembers(bc interop.Ledger, d *dao.Simple) (keys.Pu
|
|||
}
|
||||
|
||||
func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||
result := n.GetNextBlockValidatorsInternal()
|
||||
result := n.GetNextBlockValidatorsInternal(ic.DAO)
|
||||
return pubsToArray(result)
|
||||
}
|
||||
|
||||
// GetNextBlockValidatorsInternal returns next block validators.
|
||||
func (n *NEO) GetNextBlockValidatorsInternal() keys.PublicKeys {
|
||||
return n.nextValidators.Load().(keys.PublicKeys).Copy()
|
||||
func (n *NEO) GetNextBlockValidatorsInternal(d *dao.Simple) keys.PublicKeys {
|
||||
cache := d.Store.GetCache(n.ID).(*NeoCache)
|
||||
return cache.nextValidators.Load().(keys.PublicKeys).Copy()
|
||||
}
|
||||
|
||||
// BalanceOf returns native NEO token balance for the acc.
|
||||
|
|
|
@ -32,7 +32,9 @@ type Notary struct {
|
|||
GAS *GAS
|
||||
NEO *NEO
|
||||
Desig *Designate
|
||||
}
|
||||
|
||||
type NotaryCache struct {
|
||||
lock sync.RWMutex
|
||||
// isValid defies whether cached values were changed during the current
|
||||
// consensus iteration. If false, these values will be updated after
|
||||
|
@ -125,9 +127,23 @@ func (n *Notary) Metadata() *interop.ContractMD {
|
|||
func (n *Notary) Initialize(ic *interop.Context) error {
|
||||
setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, defaultMaxNotValidBeforeDelta)
|
||||
setIntWithKey(n.ID, ic.DAO, notaryServiceFeeKey, defaultNotaryServiceFeePerKey)
|
||||
n.isValid = true
|
||||
n.maxNotValidBeforeDelta = defaultMaxNotValidBeforeDelta
|
||||
n.notaryServiceFeePerKey = defaultNotaryServiceFeePerKey
|
||||
|
||||
cache := &NotaryCache{
|
||||
isValid: true,
|
||||
maxNotValidBeforeDelta: defaultMaxNotValidBeforeDelta,
|
||||
notaryServiceFeePerKey: defaultNotaryServiceFeePerKey,
|
||||
}
|
||||
ic.DAO.Store.SetCache(n.ID, cache)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Notary) InitializeCache(d *dao.Simple) error {
|
||||
cache := &NotaryCache{isValid: true}
|
||||
|
||||
cache.maxNotValidBeforeDelta = uint32(getIntWithKey(n.ID, d, maxNotValidBeforeDeltaKey))
|
||||
cache.notaryServiceFeePerKey = getIntWithKey(n.ID, d, notaryServiceFeeKey)
|
||||
|
||||
d.Store.SetCache(n.ID, cache)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -176,15 +192,16 @@ func (n *Notary) OnPersist(ic *interop.Context) error {
|
|||
|
||||
// PostPersist implements Contract interface.
|
||||
func (n *Notary) PostPersist(ic *interop.Context) error {
|
||||
n.lock.Lock()
|
||||
defer n.lock.Unlock()
|
||||
if n.isValid {
|
||||
cache := ic.DAO.Store.GetCache(n.ID).(*NotaryCache)
|
||||
cache.lock.Lock()
|
||||
defer cache.lock.Unlock()
|
||||
if cache.isValid {
|
||||
return nil
|
||||
}
|
||||
|
||||
n.maxNotValidBeforeDelta = uint32(getIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey))
|
||||
n.notaryServiceFeePerKey = getIntWithKey(n.ID, ic.DAO, notaryServiceFeeKey)
|
||||
n.isValid = true
|
||||
cache.maxNotValidBeforeDelta = uint32(getIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey))
|
||||
cache.notaryServiceFeePerKey = getIntWithKey(n.ID, ic.DAO, notaryServiceFeeKey)
|
||||
cache.isValid = true
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -391,10 +408,11 @@ func (n *Notary) getMaxNotValidBeforeDelta(ic *interop.Context, _ []stackitem.It
|
|||
|
||||
// GetMaxNotValidBeforeDelta is an internal representation of Notary getMaxNotValidBeforeDelta method.
|
||||
func (n *Notary) GetMaxNotValidBeforeDelta(dao *dao.Simple) uint32 {
|
||||
n.lock.RLock()
|
||||
defer n.lock.RUnlock()
|
||||
if n.isValid {
|
||||
return n.maxNotValidBeforeDelta
|
||||
cache := dao.Store.GetCache(n.ID).(*NotaryCache)
|
||||
cache.lock.RLock()
|
||||
defer cache.lock.RUnlock()
|
||||
if cache.isValid {
|
||||
return cache.maxNotValidBeforeDelta
|
||||
}
|
||||
return uint32(getIntWithKey(n.ID, dao, maxNotValidBeforeDeltaKey))
|
||||
}
|
||||
|
@ -410,10 +428,11 @@ func (n *Notary) setMaxNotValidBeforeDelta(ic *interop.Context, args []stackitem
|
|||
if !n.NEO.checkCommittee(ic) {
|
||||
panic("invalid committee signature")
|
||||
}
|
||||
n.lock.Lock()
|
||||
defer n.lock.Unlock()
|
||||
cache := ic.DAO.Store.GetCache(n.ID).(*NotaryCache)
|
||||
cache.lock.Lock()
|
||||
defer cache.lock.Unlock()
|
||||
setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, int64(value))
|
||||
n.isValid = false
|
||||
cache.isValid = false
|
||||
return stackitem.Null{}
|
||||
}
|
||||
|
||||
|
@ -424,10 +443,11 @@ func (n *Notary) getNotaryServiceFeePerKey(ic *interop.Context, _ []stackitem.It
|
|||
|
||||
// GetNotaryServiceFeePerKey is an internal representation of Notary getNotaryServiceFeePerKey method.
|
||||
func (n *Notary) GetNotaryServiceFeePerKey(dao *dao.Simple) int64 {
|
||||
n.lock.RLock()
|
||||
defer n.lock.RUnlock()
|
||||
if n.isValid {
|
||||
return n.notaryServiceFeePerKey
|
||||
cache := dao.Store.GetCache(n.ID).(*NotaryCache)
|
||||
cache.lock.RLock()
|
||||
defer cache.lock.RUnlock()
|
||||
if cache.isValid {
|
||||
return cache.notaryServiceFeePerKey
|
||||
}
|
||||
return getIntWithKey(n.ID, dao, notaryServiceFeeKey)
|
||||
}
|
||||
|
@ -441,10 +461,11 @@ func (n *Notary) setNotaryServiceFeePerKey(ic *interop.Context, args []stackitem
|
|||
if !n.NEO.checkCommittee(ic) {
|
||||
panic("invalid committee signature")
|
||||
}
|
||||
n.lock.Lock()
|
||||
defer n.lock.Unlock()
|
||||
cache := ic.DAO.Store.GetCache(n.ID).(*NotaryCache)
|
||||
cache.lock.Lock()
|
||||
defer cache.lock.Unlock()
|
||||
setIntWithKey(n.ID, ic.DAO, notaryServiceFeeKey, int64(value))
|
||||
n.isValid = false
|
||||
cache.isValid = false
|
||||
return stackitem.Null{}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,15 +40,17 @@ type Oracle struct {
|
|||
Desig *Designate
|
||||
oracleScript []byte
|
||||
|
||||
requestPrice atomic.Value
|
||||
requestPriceChanged atomic.Value
|
||||
|
||||
// Module is an oracle module capable of talking with the external world.
|
||||
Module atomic.Value
|
||||
// newRequests contains new requests created during current block.
|
||||
newRequests map[uint64]*state.OracleRequest
|
||||
}
|
||||
|
||||
type OracleCache struct {
|
||||
requestPrice atomic.Value
|
||||
requestPriceChanged atomic.Value
|
||||
}
|
||||
|
||||
const (
|
||||
oracleContractID = -9
|
||||
maxURLLength = 256
|
||||
|
@ -121,8 +123,6 @@ func newOracle() *Oracle {
|
|||
md = newMethodAndPrice(o.setPrice, 1<<15, callflag.States)
|
||||
o.AddMethod(md, desc)
|
||||
|
||||
o.requestPriceChanged.Store(true)
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
|
@ -143,9 +143,10 @@ func (o *Oracle) OnPersist(ic *interop.Context) error {
|
|||
// PostPersist represents `postPersist` method.
|
||||
func (o *Oracle) PostPersist(ic *interop.Context) error {
|
||||
p := o.getPriceInternal(ic.DAO)
|
||||
if o.requestPriceChanged.Load().(bool) {
|
||||
o.requestPrice.Store(p)
|
||||
o.requestPriceChanged.Store(false)
|
||||
cache := ic.DAO.Store.GetCache(o.ID).(*OracleCache)
|
||||
if cache.requestPriceChanged.Load().(bool) {
|
||||
cache.requestPrice.Store(p)
|
||||
cache.requestPriceChanged.Store(false)
|
||||
}
|
||||
|
||||
var nodes keys.PublicKeys
|
||||
|
@ -220,11 +221,21 @@ func (o *Oracle) Metadata() *interop.ContractMD {
|
|||
func (o *Oracle) Initialize(ic *interop.Context) error {
|
||||
setIntWithKey(o.ID, ic.DAO, prefixRequestID, 0)
|
||||
setIntWithKey(o.ID, ic.DAO, prefixRequestPrice, DefaultOracleRequestPrice)
|
||||
o.requestPrice.Store(int64(DefaultOracleRequestPrice))
|
||||
o.requestPriceChanged.Store(false)
|
||||
|
||||
cache := &OracleCache{}
|
||||
cache.requestPrice.Store(int64(DefaultOracleRequestPrice))
|
||||
cache.requestPriceChanged.Store(false)
|
||||
ic.DAO.Store.SetCache(o.ID, cache)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Oracle) InitializeCache(d *dao.Simple) {
|
||||
cache := &OracleCache{}
|
||||
cache.requestPrice.Store(getIntWithKey(o.ID, d, prefixRequestPrice))
|
||||
cache.requestPriceChanged.Store(false)
|
||||
d.Store.SetCache(o.ID, cache)
|
||||
}
|
||||
|
||||
func getResponse(tx *transaction.Transaction) *transaction.OracleResponse {
|
||||
for i := range tx.Attributes {
|
||||
if tx.Attributes[i].Type == transaction.OracleResponseT {
|
||||
|
@ -439,8 +450,9 @@ func (o *Oracle) getPrice(ic *interop.Context, _ []stackitem.Item) stackitem.Ite
|
|||
}
|
||||
|
||||
func (o *Oracle) getPriceInternal(d *dao.Simple) int64 {
|
||||
if !o.requestPriceChanged.Load().(bool) {
|
||||
return o.requestPrice.Load().(int64)
|
||||
cache := d.Store.GetCache(o.ID).(*OracleCache)
|
||||
if !cache.requestPriceChanged.Load().(bool) {
|
||||
return cache.requestPrice.Load().(int64)
|
||||
}
|
||||
return getIntWithKey(o.ID, d, prefixRequestPrice)
|
||||
}
|
||||
|
@ -454,7 +466,8 @@ func (o *Oracle) setPrice(ic *interop.Context, args []stackitem.Item) stackitem.
|
|||
panic("invalid committee signature")
|
||||
}
|
||||
setIntWithKey(o.ID, ic.DAO, prefixRequestPrice, price.Int64())
|
||||
o.requestPriceChanged.Store(true)
|
||||
cache := ic.DAO.Store.GetCache(o.ID).(*OracleCache)
|
||||
cache.requestPriceChanged.Store(true)
|
||||
return stackitem.Null{}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,10 @@ var (
|
|||
// Policy represents Policy native contract.
|
||||
type Policy struct {
|
||||
interop.ContractMD
|
||||
NEO *NEO
|
||||
NEO *NEO
|
||||
}
|
||||
|
||||
type PolicyCache struct {
|
||||
lock sync.RWMutex
|
||||
// isValid defies whether cached values were changed during the current
|
||||
// consensus iteration. If false, these values will be updated after
|
||||
|
@ -128,16 +131,52 @@ func (p *Policy) Initialize(ic *interop.Context) error {
|
|||
setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, defaultExecFeeFactor)
|
||||
setIntWithKey(p.ID, ic.DAO, storagePriceKey, DefaultStoragePrice)
|
||||
|
||||
p.isValid = true
|
||||
p.execFeeFactor = defaultExecFeeFactor
|
||||
p.feePerByte = defaultFeePerByte
|
||||
p.maxVerificationGas = defaultMaxVerificationGas
|
||||
p.storagePrice = DefaultStoragePrice
|
||||
p.blockedAccounts = make([]util.Uint160, 0)
|
||||
cache := &PolicyCache{}
|
||||
cache.isValid = true
|
||||
cache.execFeeFactor = defaultExecFeeFactor
|
||||
cache.feePerByte = defaultFeePerByte
|
||||
cache.maxVerificationGas = defaultMaxVerificationGas
|
||||
cache.storagePrice = DefaultStoragePrice
|
||||
cache.blockedAccounts = make([]util.Uint160, 0)
|
||||
ic.DAO.Store.SetCache(p.ID, cache)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Policy) InitializeCache(d *dao.Simple) error {
|
||||
cache := &PolicyCache{}
|
||||
err := p.fillCacheFromDAO(cache, d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Store.SetCache(p.ID, cache)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Policy) fillCacheFromDAO(cache *PolicyCache, d *dao.Simple) error {
|
||||
cache.execFeeFactor = uint32(getIntWithKey(p.ID, d, execFeeFactorKey))
|
||||
cache.feePerByte = getIntWithKey(p.ID, d, feePerByteKey)
|
||||
cache.maxVerificationGas = defaultMaxVerificationGas
|
||||
cache.storagePrice = uint32(getIntWithKey(p.ID, d, storagePriceKey))
|
||||
|
||||
cache.blockedAccounts = make([]util.Uint160, 0)
|
||||
var fErr error
|
||||
d.Seek(p.ID, storage.SeekRange{Prefix: []byte{blockedAccountPrefix}}, func(k, _ []byte) bool {
|
||||
hash, err := util.Uint160DecodeBytesBE(k)
|
||||
if err != nil {
|
||||
fErr = fmt.Errorf("failed to decode blocked account hash: %w", err)
|
||||
return false
|
||||
}
|
||||
cache.blockedAccounts = append(cache.blockedAccounts, hash)
|
||||
return true
|
||||
})
|
||||
if fErr != nil {
|
||||
return fmt.Errorf("failed to initialize blocked accounts: %w", fErr)
|
||||
}
|
||||
cache.isValid = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnPersist implements Contract interface.
|
||||
func (p *Policy) OnPersist(ic *interop.Context) error {
|
||||
return nil
|
||||
|
@ -145,32 +184,14 @@ func (p *Policy) OnPersist(ic *interop.Context) error {
|
|||
|
||||
// PostPersist implements Contract interface.
|
||||
func (p *Policy) PostPersist(ic *interop.Context) error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
if p.isValid {
|
||||
cache := ic.DAO.Store.GetCache(p.ID).(*PolicyCache)
|
||||
cache.lock.Lock()
|
||||
defer cache.lock.Unlock()
|
||||
if cache.isValid {
|
||||
return nil
|
||||
}
|
||||
|
||||
p.execFeeFactor = uint32(getIntWithKey(p.ID, ic.DAO, execFeeFactorKey))
|
||||
p.feePerByte = getIntWithKey(p.ID, ic.DAO, feePerByteKey)
|
||||
p.maxVerificationGas = defaultMaxVerificationGas
|
||||
p.storagePrice = uint32(getIntWithKey(p.ID, ic.DAO, storagePriceKey))
|
||||
|
||||
p.blockedAccounts = make([]util.Uint160, 0)
|
||||
var fErr error
|
||||
ic.DAO.Seek(p.ID, storage.SeekRange{Prefix: []byte{blockedAccountPrefix}}, func(k, _ []byte) bool {
|
||||
hash, err := util.Uint160DecodeBytesBE(k)
|
||||
if err != nil {
|
||||
fErr = fmt.Errorf("failed to decode blocked account hash: %w", err)
|
||||
return false
|
||||
}
|
||||
p.blockedAccounts = append(p.blockedAccounts, hash)
|
||||
return true
|
||||
})
|
||||
if fErr == nil {
|
||||
p.isValid = true
|
||||
}
|
||||
return fErr
|
||||
return p.fillCacheFromDAO(cache, ic.DAO)
|
||||
}
|
||||
|
||||
// getFeePerByte is Policy contract method and returns required transaction's fee
|
||||
|
@ -181,18 +202,22 @@ func (p *Policy) getFeePerByte(ic *interop.Context, _ []stackitem.Item) stackite
|
|||
|
||||
// GetFeePerByteInternal returns required transaction's fee per byte.
|
||||
func (p *Policy) GetFeePerByteInternal(dao *dao.Simple) int64 {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
if p.isValid {
|
||||
return p.feePerByte
|
||||
cache := dao.Store.GetCache(p.ID).(*PolicyCache)
|
||||
cache.lock.RLock()
|
||||
defer cache.lock.RUnlock()
|
||||
if cache.isValid {
|
||||
return cache.feePerByte
|
||||
}
|
||||
return getIntWithKey(p.ID, dao, feePerByteKey)
|
||||
}
|
||||
|
||||
// GetMaxVerificationGas returns maximum gas allowed to be burned during verificaion.
|
||||
func (p *Policy) GetMaxVerificationGas(_ *dao.Simple) int64 {
|
||||
if p.isValid {
|
||||
return p.maxVerificationGas
|
||||
func (p *Policy) GetMaxVerificationGas(dao *dao.Simple) int64 {
|
||||
cache := dao.Store.GetCache(p.ID).(*PolicyCache)
|
||||
cache.lock.RLock()
|
||||
defer cache.lock.RUnlock()
|
||||
if cache.isValid {
|
||||
return cache.maxVerificationGas
|
||||
}
|
||||
return defaultMaxVerificationGas
|
||||
}
|
||||
|
@ -203,10 +228,11 @@ func (p *Policy) getExecFeeFactor(ic *interop.Context, _ []stackitem.Item) stack
|
|||
|
||||
// GetExecFeeFactorInternal returns current execution fee factor.
|
||||
func (p *Policy) GetExecFeeFactorInternal(d *dao.Simple) int64 {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
if p.isValid {
|
||||
return int64(p.execFeeFactor)
|
||||
cache := d.Store.GetCache(p.ID).(*PolicyCache)
|
||||
cache.lock.RLock()
|
||||
defer cache.lock.RUnlock()
|
||||
if cache.isValid {
|
||||
return int64(cache.execFeeFactor)
|
||||
}
|
||||
return getIntWithKey(p.ID, d, execFeeFactorKey)
|
||||
}
|
||||
|
@ -219,10 +245,11 @@ func (p *Policy) setExecFeeFactor(ic *interop.Context, args []stackitem.Item) st
|
|||
if !p.NEO.checkCommittee(ic) {
|
||||
panic("invalid committee signature")
|
||||
}
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
cache := ic.DAO.Store.GetCache(p.ID).(*PolicyCache)
|
||||
cache.lock.Lock()
|
||||
defer cache.lock.Unlock()
|
||||
setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, int64(value))
|
||||
p.isValid = false
|
||||
cache.isValid = false
|
||||
return stackitem.Null{}
|
||||
}
|
||||
|
||||
|
@ -234,14 +261,15 @@ func (p *Policy) isBlocked(ic *interop.Context, args []stackitem.Item) stackitem
|
|||
|
||||
// IsBlockedInternal checks whether provided account is blocked.
|
||||
func (p *Policy) IsBlockedInternal(dao *dao.Simple, hash util.Uint160) bool {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
if p.isValid {
|
||||
length := len(p.blockedAccounts)
|
||||
cache := dao.Store.GetCache(p.ID).(*PolicyCache)
|
||||
cache.lock.RLock()
|
||||
defer cache.lock.RUnlock()
|
||||
if cache.isValid {
|
||||
length := len(cache.blockedAccounts)
|
||||
i := sort.Search(length, func(i int) bool {
|
||||
return !p.blockedAccounts[i].Less(hash)
|
||||
return !cache.blockedAccounts[i].Less(hash)
|
||||
})
|
||||
if length != 0 && i != length && p.blockedAccounts[i].Equals(hash) {
|
||||
if length != 0 && i != length && cache.blockedAccounts[i].Equals(hash) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -256,10 +284,11 @@ func (p *Policy) getStoragePrice(ic *interop.Context, _ []stackitem.Item) stacki
|
|||
|
||||
// GetStoragePriceInternal returns current execution fee factor.
|
||||
func (p *Policy) GetStoragePriceInternal(d *dao.Simple) int64 {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
if p.isValid {
|
||||
return int64(p.storagePrice)
|
||||
cache := d.Store.GetCache(p.ID).(*PolicyCache)
|
||||
cache.lock.RLock()
|
||||
defer cache.lock.RUnlock()
|
||||
if cache.isValid {
|
||||
return int64(cache.storagePrice)
|
||||
}
|
||||
return getIntWithKey(p.ID, d, storagePriceKey)
|
||||
}
|
||||
|
@ -272,10 +301,11 @@ func (p *Policy) setStoragePrice(ic *interop.Context, args []stackitem.Item) sta
|
|||
if !p.NEO.checkCommittee(ic) {
|
||||
panic("invalid committee signature")
|
||||
}
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
cache := ic.DAO.Store.GetCache(p.ID).(*PolicyCache)
|
||||
cache.lock.Lock()
|
||||
defer cache.lock.Unlock()
|
||||
setIntWithKey(p.ID, ic.DAO, storagePriceKey, int64(value))
|
||||
p.isValid = false
|
||||
cache.isValid = false
|
||||
return stackitem.Null{}
|
||||
}
|
||||
|
||||
|
@ -288,10 +318,11 @@ func (p *Policy) setFeePerByte(ic *interop.Context, args []stackitem.Item) stack
|
|||
if !p.NEO.checkCommittee(ic) {
|
||||
panic("invalid committee signature")
|
||||
}
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
cache := ic.DAO.Store.GetCache(p.ID).(*PolicyCache)
|
||||
cache.lock.Lock()
|
||||
defer cache.lock.Unlock()
|
||||
setIntWithKey(p.ID, ic.DAO, feePerByteKey, value)
|
||||
p.isValid = false
|
||||
cache.isValid = false
|
||||
return stackitem.Null{}
|
||||
}
|
||||
|
||||
|
@ -311,10 +342,11 @@ func (p *Policy) blockAccount(ic *interop.Context, args []stackitem.Item) stacki
|
|||
return stackitem.NewBool(false)
|
||||
}
|
||||
key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...)
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
cache := ic.DAO.Store.GetCache(p.ID).(*PolicyCache)
|
||||
cache.lock.Lock()
|
||||
defer cache.lock.Unlock()
|
||||
ic.DAO.PutStorageItem(p.ID, key, state.StorageItem{})
|
||||
p.isValid = false
|
||||
cache.isValid = false
|
||||
return stackitem.NewBool(true)
|
||||
}
|
||||
|
||||
|
@ -329,10 +361,11 @@ func (p *Policy) unblockAccount(ic *interop.Context, args []stackitem.Item) stac
|
|||
return stackitem.NewBool(false)
|
||||
}
|
||||
key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...)
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
cache := ic.DAO.Store.GetCache(p.ID).(*PolicyCache)
|
||||
cache.lock.Lock()
|
||||
defer cache.lock.Unlock()
|
||||
ic.DAO.DeleteStorageItem(p.ID, key)
|
||||
p.isValid = false
|
||||
cache.isValid = false
|
||||
return stackitem.NewBool(true)
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,9 @@ import (
|
|||
type MemCachedStore struct {
|
||||
MemoryStore
|
||||
|
||||
nativeCacheLock sync.RWMutex
|
||||
nativeCache map[int32]*NativeCacheItem
|
||||
|
||||
private bool
|
||||
// plock protects Persist from double entrance.
|
||||
plock sync.Mutex
|
||||
|
@ -22,6 +25,10 @@ type MemCachedStore struct {
|
|||
ps Store
|
||||
}
|
||||
|
||||
type NativeCacheItem struct {
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
type (
|
||||
// KeyValue represents key-value pair.
|
||||
KeyValue struct {
|
||||
|
@ -46,8 +53,15 @@ type (
|
|||
|
||||
// NewMemCachedStore creates a new MemCachedStore object.
|
||||
func NewMemCachedStore(lower Store) *MemCachedStore {
|
||||
var cache map[int32]*NativeCacheItem
|
||||
if cached, ok := lower.(*MemCachedStore); ok {
|
||||
cache = cached.nativeCache
|
||||
} else {
|
||||
cache = make(map[int32]*NativeCacheItem)
|
||||
}
|
||||
return &MemCachedStore{
|
||||
MemoryStore: *NewMemoryStore(),
|
||||
nativeCache: cache,
|
||||
ps: lower,
|
||||
}
|
||||
}
|
||||
|
@ -55,8 +69,15 @@ func NewMemCachedStore(lower Store) *MemCachedStore {
|
|||
// NewPrivateMemCachedStore creates a new private (unlocked) MemCachedStore object.
|
||||
// Private cached stores are closed after Persist.
|
||||
func NewPrivateMemCachedStore(lower Store) *MemCachedStore {
|
||||
var cache map[int32]*NativeCacheItem
|
||||
if cached, ok := lower.(*MemCachedStore); ok {
|
||||
cache = cached.nativeCache
|
||||
} else {
|
||||
cache = make(map[int32]*NativeCacheItem)
|
||||
}
|
||||
return &MemCachedStore{
|
||||
MemoryStore: *NewMemoryStore(),
|
||||
nativeCache: cache,
|
||||
private: true,
|
||||
ps: lower,
|
||||
}
|
||||
|
@ -393,6 +414,25 @@ func (s *MemCachedStore) persist(isSync bool) (int, error) {
|
|||
return keys, err
|
||||
}
|
||||
|
||||
func (s *MemCachedStore) GetCache(k int32) interface{} {
|
||||
s.nativeCacheLock.RLock()
|
||||
defer s.nativeCacheLock.RUnlock()
|
||||
|
||||
if itm, ok := s.nativeCache[k]; ok {
|
||||
return itm.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MemCachedStore) SetCache(k int32, v interface{}) {
|
||||
s.nativeCacheLock.Lock()
|
||||
defer s.nativeCacheLock.Unlock()
|
||||
|
||||
s.nativeCache[k] = &NativeCacheItem{
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
|
||||
// Close implements Store interface, clears up memory and closes the lower layer
|
||||
// Store.
|
||||
func (s *MemCachedStore) Close() error {
|
||||
|
|
|
@ -4,9 +4,11 @@ import (
|
|||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
|
@ -798,13 +800,13 @@ func TestInvokeVerify(t *testing.T) {
|
|||
var h uint32 = 1
|
||||
_, err = c.InvokeContractVerifyAtHeight(h, contract, smartcontract.Params{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}})
|
||||
require.Error(t, err)
|
||||
// TODO: check that error is `ErrUnknownVerificationContract`
|
||||
require.True(t, strings.Contains(err.Error(), core.ErrUnknownVerificationContract.Error())) // contract wasn't deployed at block #1 yet
|
||||
})
|
||||
|
||||
t.Run("bad, historic, by block: contract not found", func(t *testing.T) {
|
||||
_, err = c.InvokeContractVerifyAtBlock(chain.GetHeaderHash(1), contract, smartcontract.Params{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}})
|
||||
require.Error(t, err)
|
||||
// TODO: check that error is `ErrUnknownVerificationContract`
|
||||
require.True(t, strings.Contains(err.Error(), core.ErrUnknownVerificationContract.Error())) // contract wasn't deployed at block #1 yet
|
||||
})
|
||||
|
||||
t.Run("bad, historic, by stateroot: contract not found", func(t *testing.T) {
|
||||
|
@ -813,7 +815,7 @@ func TestInvokeVerify(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
_, err = c.InvokeContractVerifyWithState(sr.Root, contract, smartcontract.Params{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}})
|
||||
require.Error(t, err)
|
||||
// TODO: check that error is `ErrUnknownVerificationContract`
|
||||
require.True(t, strings.Contains(err.Error(), core.ErrUnknownVerificationContract.Error())) // contract wasn't deployed at block #1 yet
|
||||
})
|
||||
|
||||
t.Run("positive, with signer and witness", func(t *testing.T) {
|
||||
|
|
|
@ -1840,7 +1840,7 @@ func (s *Server) runScriptInVM(t trigger.Type, script []byte, contractScriptHash
|
|||
|
||||
err = s.chain.InitVerificationContext(ic, contractScriptHash, &transaction.Witness{InvocationScript: script, VerificationScript: []byte{}})
|
||||
if err != nil {
|
||||
return nil, response.NewInternalServerError("can't prepare verification VM", err)
|
||||
return nil, response.NewInternalServerError(fmt.Sprintf("can't prepare verification VM: %s", err.Error()), err)
|
||||
}
|
||||
} else {
|
||||
ic.VM.LoadScriptWithFlags(script, callflag.All)
|
||||
|
|
Loading…
Reference in a new issue