mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-26 09:42:22 +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()
|
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) {
|
t.Run("valid script", func(t *testing.T) {
|
||||||
src := getAppCallScript(fmt.Sprintf("%#v", ih.BytesBE()))
|
src := getAppCallScript(fmt.Sprintf("%#v", ih.BytesBE()))
|
||||||
|
|
|
@ -594,7 +594,18 @@ func (bc *Blockchain) initializeNativeCache(d *dao.Simple) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't init cache for Management native contract: %w", err)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1223,14 +1234,14 @@ func (bc *Blockchain) updateExtensibleWhitelist(height uint32) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
newList := []util.Uint160{bc.contracts.NEO.GetCommitteeAddress()}
|
newList := []util.Uint160{bc.contracts.NEO.GetCommitteeAddress(bc.dao)}
|
||||||
nextVals := bc.contracts.NEO.GetNextBlockValidatorsInternal()
|
nextVals := bc.contracts.NEO.GetNextBlockValidatorsInternal(bc.dao)
|
||||||
script, err := smartcontract.CreateDefaultMultiSigRedeemScript(nextVals)
|
script, err := smartcontract.CreateDefaultMultiSigRedeemScript(nextVals)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
newList = append(newList, hash.Hash160(script))
|
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 {
|
if len(stateVals) > 0 {
|
||||||
h, err := bc.contracts.Designate.GetLastDesignatedHash(bc.dao, noderoles.StateValidator)
|
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.
|
// GetNEP17Contracts returns the list of deployed NEP-17 contracts.
|
||||||
func (bc *Blockchain) GetNEP17Contracts() []util.Uint160 {
|
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.
|
// GetNEP11Contracts returns the list of deployed NEP-11 contracts.
|
||||||
func (bc *Blockchain) GetNEP11Contracts() []util.Uint160 {
|
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
|
// 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)
|
curVC := bc.config.GetNumOfCNs(bc.BlockHeight() + 1)
|
||||||
if oldVC == nil || oldVC != curVC {
|
if oldVC == nil || oldVC != curVC {
|
||||||
m := smartcontract.GetDefaultHonestNodeCount(curVC)
|
m := smartcontract.GetDefaultHonestNodeCount(curVC)
|
||||||
verification, _ := smartcontract.CreateDefaultMultiSigRedeemScript(bc.contracts.NEO.GetNextBlockValidatorsInternal())
|
verification, _ := smartcontract.CreateDefaultMultiSigRedeemScript(bc.contracts.NEO.GetNextBlockValidatorsInternal(bc.dao))
|
||||||
defaultWitness = transaction.Witness{
|
defaultWitness = transaction.Witness{
|
||||||
InvocationScript: make([]byte, 66*m),
|
InvocationScript: make([]byte, 66*m),
|
||||||
VerificationScript: verification,
|
VerificationScript: verification,
|
||||||
|
@ -1942,7 +1953,7 @@ func (bc *Blockchain) verifyAndPoolTx(t *transaction.Transaction, pool *mempool.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := bc.verifyTxAttributes(t, isPartialTx); err != nil {
|
if err := bc.verifyTxAttributes(bc.dao, t, isPartialTx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = pool.Add(t, feer, data...)
|
err = pool.Add(t, feer, data...)
|
||||||
|
@ -1966,11 +1977,11 @@ func (bc *Blockchain) verifyAndPoolTx(t *transaction.Transaction, pool *mempool.
|
||||||
return nil
|
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 {
|
for i := range tx.Attributes {
|
||||||
switch attrType := tx.Attributes[i].Type; attrType {
|
switch attrType := tx.Attributes[i].Type; attrType {
|
||||||
case transaction.HighPriority:
|
case transaction.HighPriority:
|
||||||
h := bc.contracts.NEO.GetCommitteeAddress()
|
h := bc.contracts.NEO.GetCommitteeAddress(d)
|
||||||
if !tx.HasSigner(h) {
|
if !tx.HasSigner(h) {
|
||||||
return fmt.Errorf("%w: high priority tx is not signed by committee", ErrInvalidAttribute)
|
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) {
|
} else if txpool.HasConflicts(t, bc) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if err := bc.verifyTxAttributes(t, isPartialTx); err != nil {
|
if err := bc.verifyTxAttributes(bc.dao, t, isPartialTx); err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for i := range t.Scripts {
|
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.
|
// GetCommittee returns the sorted list of public keys of nodes in committee.
|
||||||
func (bc *Blockchain) GetCommittee() (keys.PublicKeys, error) {
|
func (bc *Blockchain) GetCommittee() (keys.PublicKeys, error) {
|
||||||
pubs := bc.contracts.NEO.GetCommitteeMembers()
|
pubs := bc.contracts.NEO.GetCommitteeMembers(bc.dao)
|
||||||
sort.Sort(pubs)
|
sort.Sort(pubs)
|
||||||
return pubs, nil
|
return pubs, nil
|
||||||
}
|
}
|
||||||
|
@ -2134,7 +2145,7 @@ func (bc *Blockchain) GetValidators() ([]*keys.PublicKey, error) {
|
||||||
|
|
||||||
// GetNextBlockValidators returns next block validators.
|
// GetNextBlockValidators returns next block validators.
|
||||||
func (bc *Blockchain) GetNextBlockValidators() ([]*keys.PublicKey, error) {
|
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.
|
// 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))
|
s := mpt.NewTrieStore(sr.Root, mode, storage.NewPrivateMemCachedStore(bc.dao.Store))
|
||||||
dTrie := dao.NewSimple(s, bc.config.StateRootInHeader, bc.config.P2PSigExtensions)
|
dTrie := dao.NewSimple(s, bc.config.StateRootInHeader, bc.config.P2PSigExtensions)
|
||||||
dTrie.Version = bc.dao.Version
|
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)
|
systemInterop := bc.newInteropContext(t, dTrie, b, tx)
|
||||||
vm := systemInterop.SpawnVM()
|
vm := systemInterop.SpawnVM()
|
||||||
vm.SetPriceGetter(systemInterop.GetPrice)
|
vm.SetPriceGetter(systemInterop.GetPrice)
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"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/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/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
@ -329,7 +328,7 @@ func TestBlockchain_BaseExecFeeBaseStoragePrice_Compat(t *testing.T) {
|
||||||
bc := newTestChain(t)
|
bc := newTestChain(t)
|
||||||
|
|
||||||
check := func(t *testing.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.GetBaseExecFee(), ic.BaseExecFee())
|
||||||
require.Equal(t, bc.GetStoragePrice(), ic.BaseStorageFee())
|
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.
|
// 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)
|
chain := newTestChain(t)
|
||||||
context := chain.newInteropContext(trigger.Application,
|
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()
|
v := context.SpawnVM()
|
||||||
return v, context, chain
|
return v, context, chain
|
||||||
}
|
}
|
||||||
|
@ -552,10 +552,7 @@ func createVMAndContractState(t testing.TB) (*vm.VM, *state.Contract, *interop.C
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
chain := newTestChain(t)
|
v, context, chain := createVM(t)
|
||||||
d := dao.NewSimple(storage.NewMemoryStore(), chain.config.StateRootInHeader, chain.config.P2PSigExtensions)
|
|
||||||
context := chain.newInteropContext(trigger.Application, d, nil, nil)
|
|
||||||
v := context.SpawnVM()
|
|
||||||
return v, contractState, context, chain
|
return v, contractState, context, chain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"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/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/smartcontract/trigger"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/stretchr/testify/require"
|
"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 := vm.New()
|
||||||
v.Estack().PushVal(value)
|
v.Estack().PushVal(value)
|
||||||
chain := newTestChain(t)
|
chain := newTestChain(t)
|
||||||
d := dao.NewSimple(storage.NewMemoryStore(), chain.config.StateRootInHeader, chain.config.P2PSigExtensions)
|
context := chain.newInteropContext(trigger.Application, chain.dao, nil, nil)
|
||||||
context := chain.newInteropContext(trigger.Application, d, nil, nil)
|
|
||||||
context.VM = v
|
context.VM = v
|
||||||
require.Error(t, f(context))
|
require.Error(t, f(context))
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,12 +31,6 @@ type Designate struct {
|
||||||
interop.ContractMD
|
interop.ContractMD
|
||||||
NEO *NEO
|
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 defines whether the P2P signature extensions logic is relevant.
|
||||||
p2pSigExtensionsEnabled bool
|
p2pSigExtensionsEnabled bool
|
||||||
|
|
||||||
|
@ -53,6 +47,14 @@ type roleData struct {
|
||||||
height uint32
|
height uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DesignationCache struct {
|
||||||
|
rolesChangedFlag atomic.Value
|
||||||
|
oracles atomic.Value
|
||||||
|
stateVals atomic.Value
|
||||||
|
neofsAlphabet atomic.Value
|
||||||
|
notaries atomic.Value
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
designateContractID = -8
|
designateContractID = -8
|
||||||
|
|
||||||
|
@ -104,6 +106,9 @@ func newDesignate(p2pSigExtensionsEnabled bool) *Designate {
|
||||||
|
|
||||||
// Initialize initializes Oracle contract.
|
// Initialize initializes Oracle contract.
|
||||||
func (s *Designate) Initialize(ic *interop.Context) error {
|
func (s *Designate) Initialize(ic *interop.Context) error {
|
||||||
|
cache := &DesignationCache{}
|
||||||
|
cache.rolesChangedFlag.Store(true)
|
||||||
|
ic.DAO.Store.SetCache(s.ID, cache)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,26 +119,27 @@ func (s *Designate) OnPersist(ic *interop.Context) error {
|
||||||
|
|
||||||
// PostPersist implements Contract interface.
|
// PostPersist implements Contract interface.
|
||||||
func (s *Designate) PostPersist(ic *interop.Context) error {
|
func (s *Designate) PostPersist(ic *interop.Context) error {
|
||||||
if !s.rolesChanged() {
|
cache := ic.DAO.Store.GetCache(s.ID).(*DesignationCache)
|
||||||
|
if !rolesChanged(cache) {
|
||||||
return nil
|
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
|
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
|
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
|
return err
|
||||||
}
|
}
|
||||||
if s.p2pSigExtensionsEnabled {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.rolesChangedFlag.Store(false)
|
cache.rolesChangedFlag.Store(false)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,8 +168,8 @@ func (s *Designate) getDesignatedByRole(ic *interop.Context, args []stackitem.It
|
||||||
return pubsToArray(pubs)
|
return pubsToArray(pubs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Designate) rolesChanged() bool {
|
func rolesChanged(cache *DesignationCache) bool {
|
||||||
rc := s.rolesChangedFlag.Load()
|
rc := cache.rolesChangedFlag.Load()
|
||||||
return rc == nil || rc.(bool)
|
return rc == nil || rc.(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,17 +214,17 @@ func (s *Designate) updateCachedRoleData(v *atomic.Value, d *dao.Simple, r noder
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Designate) getCachedRoleData(r noderoles.Role) *roleData {
|
func getCachedRoleData(cache *DesignationCache, r noderoles.Role) *roleData {
|
||||||
var val interface{}
|
var val interface{}
|
||||||
switch r {
|
switch r {
|
||||||
case noderoles.Oracle:
|
case noderoles.Oracle:
|
||||||
val = s.oracles.Load()
|
val = cache.oracles.Load()
|
||||||
case noderoles.StateValidator:
|
case noderoles.StateValidator:
|
||||||
val = s.stateVals.Load()
|
val = cache.stateVals.Load()
|
||||||
case noderoles.NeoFSAlphabet:
|
case noderoles.NeoFSAlphabet:
|
||||||
val = s.neofsAlphabet.Load()
|
val = cache.neofsAlphabet.Load()
|
||||||
case noderoles.P2PNotary:
|
case noderoles.P2PNotary:
|
||||||
val = s.notaries.Load()
|
val = cache.notaries.Load()
|
||||||
}
|
}
|
||||||
if val != nil {
|
if val != nil {
|
||||||
return val.(*roleData)
|
return val.(*roleData)
|
||||||
|
@ -231,8 +237,9 @@ func (s *Designate) GetLastDesignatedHash(d *dao.Simple, r noderoles.Role) (util
|
||||||
if !s.isValidRole(r) {
|
if !s.isValidRole(r) {
|
||||||
return util.Uint160{}, ErrInvalidRole
|
return util.Uint160{}, ErrInvalidRole
|
||||||
}
|
}
|
||||||
if !s.rolesChanged() {
|
cache := d.Store.GetCache(s.ID).(*DesignationCache)
|
||||||
if val := s.getCachedRoleData(r); val != nil {
|
if !rolesChanged(cache) {
|
||||||
|
if val := getCachedRoleData(cache, r); val != nil {
|
||||||
return val.addr, 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) {
|
if !s.isValidRole(r) {
|
||||||
return nil, 0, ErrInvalidRole
|
return nil, 0, ErrInvalidRole
|
||||||
}
|
}
|
||||||
if !s.rolesChanged() {
|
cache := d.Store.GetCache(s.ID).(*DesignationCache)
|
||||||
if val := s.getCachedRoleData(r); val != nil && val.height <= index {
|
if !rolesChanged(cache) {
|
||||||
|
if val := getCachedRoleData(cache, r); val != nil && val.height <= index {
|
||||||
return val.nodes.Copy(), val.height, nil
|
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) {
|
if !s.isValidRole(r) {
|
||||||
return ErrInvalidRole
|
return ErrInvalidRole
|
||||||
}
|
}
|
||||||
h := s.NEO.GetCommitteeAddress()
|
h := s.NEO.GetCommitteeAddress(ic.DAO)
|
||||||
if ok, err := runtime.CheckHashedWitness(ic, h); err != nil || !ok {
|
if ok, err := runtime.CheckHashedWitness(ic, h); err != nil || !ok {
|
||||||
return ErrInvalidWitness
|
return ErrInvalidWitness
|
||||||
}
|
}
|
||||||
|
@ -327,7 +335,7 @@ func (s *Designate) DesignateAsRole(ic *interop.Context, r noderoles.Role, pubs
|
||||||
}
|
}
|
||||||
sort.Sort(pubs)
|
sort.Sort(pubs)
|
||||||
nl := NodeList(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)
|
err := putConvertibleToDAO(s.ID, ic.DAO, key, &nl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -357,6 +365,8 @@ func (s *Designate) getRole(item stackitem.Item) (noderoles.Role, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitializeCache invalidates native Designate cache.
|
// InitializeCache invalidates native Designate cache.
|
||||||
func (s *Designate) InitializeCache() {
|
func (s *Designate) InitializeCache(d *dao.Simple) {
|
||||||
s.rolesChangedFlag.Store(true)
|
cache := &DesignationCache{}
|
||||||
|
cache.rolesChangedFlag.Store(true)
|
||||||
|
d.Store.SetCache(s.ID, cache)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,9 @@ import (
|
||||||
type Management struct {
|
type Management struct {
|
||||||
interop.ContractMD
|
interop.ContractMD
|
||||||
NEO *NEO
|
NEO *NEO
|
||||||
|
}
|
||||||
|
|
||||||
|
type ManagementCache struct {
|
||||||
mtx sync.RWMutex
|
mtx sync.RWMutex
|
||||||
contracts map[util.Uint160]*state.Contract
|
contracts map[util.Uint160]*state.Contract
|
||||||
// nep11 is a map of NEP11-compliant contracts which is updated with every PostPersist.
|
// 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 {
|
func newManagement() *Management {
|
||||||
var m = &Management{
|
var m = &Management{
|
||||||
ContractMD: *interop.NewContractMD(nativenames.Management, ManagementContractID),
|
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()
|
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.
|
// GetContract returns contract with given hash from given DAO.
|
||||||
func (m *Management) GetContract(d *dao.Simple, hash util.Uint160) (*state.Contract, error) {
|
func (m *Management) GetContract(d *dao.Simple, hash util.Uint160) (*state.Contract, error) {
|
||||||
m.mtx.RLock()
|
cache := d.Store.GetCache(m.ID).(*ManagementCache)
|
||||||
cs, ok := m.contracts[hash]
|
cache.mtx.RLock()
|
||||||
m.mtx.RUnlock()
|
cs, ok := cache.contracts[hash]
|
||||||
|
cache.mtx.RUnlock()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, storage.ErrKeyNotFound
|
return nil, storage.ErrKeyNotFound
|
||||||
} else if cs != nil {
|
} else if cs != nil {
|
||||||
|
@ -260,11 +260,12 @@ func (m *Management) deployWithData(ic *interop.Context, args []stackitem.Item)
|
||||||
return contractToStack(newcontract)
|
return contractToStack(newcontract)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Management) markUpdated(h util.Uint160) {
|
func (m *Management) markUpdated(d *dao.Simple, h util.Uint160) {
|
||||||
m.mtx.Lock()
|
cache := d.Store.GetCache(m.ID).(*ManagementCache)
|
||||||
|
cache.mtx.Lock()
|
||||||
// Just set it to nil, to refresh cache in `PostPersist`.
|
// Just set it to nil, to refresh cache in `PostPersist`.
|
||||||
m.contracts[h] = nil
|
cache.contracts[h] = nil
|
||||||
m.mtx.Unlock()
|
cache.mtx.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deploy creates contract's hash/ID and saves new contract into the given DAO.
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m.markUpdated(newcontract.Hash)
|
m.markUpdated(d, newcontract.Hash)
|
||||||
return newcontract, nil
|
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.
|
contract = *oldcontract // Make a copy, don't ruin (potentially) cached contract.
|
||||||
// if NEF was provided, update the contract script
|
// if NEF was provided, update the contract script
|
||||||
if neff != nil {
|
if neff != nil {
|
||||||
m.markUpdated(hash)
|
m.markUpdated(d, hash)
|
||||||
contract.NEF = *neff
|
contract.NEF = *neff
|
||||||
}
|
}
|
||||||
// if manifest was provided, update the contract manifest
|
// 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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid manifest: %w", err)
|
return nil, fmt.Errorf("invalid manifest: %w", err)
|
||||||
}
|
}
|
||||||
m.markUpdated(hash)
|
m.markUpdated(d, hash)
|
||||||
contract.Manifest = *manif
|
contract.Manifest = *manif
|
||||||
}
|
}
|
||||||
err = checkScriptAndMethods(contract.NEF.Script, contract.Manifest.ABI.Methods)
|
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)
|
d.DeleteStorageItem(contract.ID, k)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
m.markUpdated(hash)
|
m.markUpdated(d, hash)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,18 +445,19 @@ func (m *Management) Metadata() *interop.ContractMD {
|
||||||
|
|
||||||
// updateContractCache saves contract in the common and NEP-related caches. It's
|
// updateContractCache saves contract in the common and NEP-related caches. It's
|
||||||
// an internal method that must be called with m.mtx lock taken.
|
// an internal method that must be called with m.mtx lock taken.
|
||||||
func (m *Management) updateContractCache(cs *state.Contract) {
|
func updateContractCache(cache *ManagementCache, cs *state.Contract) {
|
||||||
m.contracts[cs.Hash] = cs
|
cache.contracts[cs.Hash] = cs
|
||||||
if cs.Manifest.IsStandardSupported(manifest.NEP11StandardName) {
|
if cs.Manifest.IsStandardSupported(manifest.NEP11StandardName) {
|
||||||
m.nep11[cs.Hash] = struct{}{}
|
cache.nep11[cs.Hash] = struct{}{}
|
||||||
}
|
}
|
||||||
if cs.Manifest.IsStandardSupported(manifest.NEP17StandardName) {
|
if cs.Manifest.IsStandardSupported(manifest.NEP17StandardName) {
|
||||||
m.nep17[cs.Hash] = struct{}{}
|
cache.nep17[cs.Hash] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPersist implements Contract interface.
|
// OnPersist implements Contract interface.
|
||||||
func (m *Management) OnPersist(ic *interop.Context) error {
|
func (m *Management) OnPersist(ic *interop.Context) error {
|
||||||
|
var cache *ManagementCache
|
||||||
for _, native := range ic.Natives {
|
for _, native := range ic.Natives {
|
||||||
md := native.Metadata()
|
md := native.Metadata()
|
||||||
history := md.UpdateHistory
|
history := md.UpdateHistory
|
||||||
|
@ -466,16 +468,19 @@ func (m *Management) OnPersist(ic *interop.Context) error {
|
||||||
cs := &state.Contract{
|
cs := &state.Contract{
|
||||||
ContractBase: md.ContractBase,
|
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)
|
err := m.PutContractState(ic.DAO, cs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := native.Initialize(ic); err != nil {
|
if cache == nil {
|
||||||
return fmt.Errorf("initializing %s native contract: %w", md.Name, err)
|
cache = ic.DAO.Store.GetCache(m.ID).(*ManagementCache)
|
||||||
}
|
}
|
||||||
m.mtx.Lock()
|
cache.mtx.Lock()
|
||||||
m.updateContractCache(cs)
|
updateContractCache(cache, cs)
|
||||||
m.mtx.Unlock()
|
cache.mtx.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
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
|
// Cache initialisation should be done apart from Initialize because Initialize is
|
||||||
// called only when deploying native contracts.
|
// called only when deploying native contracts.
|
||||||
func (m *Management) InitializeCache(d *dao.Simple) error {
|
func (m *Management) InitializeCache(d *dao.Simple) error {
|
||||||
m.mtx.Lock()
|
cache := &ManagementCache{
|
||||||
defer m.mtx.Unlock()
|
contracts: make(map[util.Uint160]*state.Contract),
|
||||||
|
nep11: make(map[util.Uint160]struct{}),
|
||||||
|
nep17: make(map[util.Uint160]struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
var initErr error
|
var initErr error
|
||||||
d.Seek(m.ID, storage.SeekRange{Prefix: []byte{prefixContract}}, func(_, v []byte) bool {
|
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 {
|
if initErr != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
m.updateContractCache(cs)
|
updateContractCache(cache, cs)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
if initErr != nil {
|
||||||
return initErr
|
return initErr
|
||||||
|
}
|
||||||
|
d.Store.SetCache(m.ID, cache)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostPersist implements Contract interface.
|
// PostPersist implements Contract interface.
|
||||||
func (m *Management) PostPersist(ic *interop.Context) error {
|
func (m *Management) PostPersist(ic *interop.Context) error {
|
||||||
m.mtx.Lock()
|
cache := ic.DAO.Store.GetCache(m.ID).(*ManagementCache)
|
||||||
for h, cs := range m.contracts {
|
cache.mtx.Lock()
|
||||||
|
defer cache.mtx.Unlock()
|
||||||
|
for h, cs := range cache.contracts {
|
||||||
if cs != nil {
|
if cs != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
delete(m.nep11, h)
|
delete(cache.nep11, h)
|
||||||
delete(m.nep17, h)
|
delete(cache.nep17, h)
|
||||||
newCs, err := m.getContractFromDAO(ic.DAO, h)
|
newCs, err := m.getContractFromDAO(ic.DAO, h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Contract was destroyed.
|
// Contract was destroyed.
|
||||||
delete(m.contracts, h)
|
delete(cache.contracts, h)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
m.updateContractCache(newCs)
|
updateContractCache(cache, newCs)
|
||||||
}
|
}
|
||||||
m.mtx.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNEP11Contracts returns hashes of all deployed contracts that support NEP-11 standard. The list
|
// 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 updated every PostPersist, so until PostPersist is called, the result for the previous block
|
||||||
// is returned.
|
// is returned.
|
||||||
func (m *Management) GetNEP11Contracts() []util.Uint160 {
|
func (m *Management) GetNEP11Contracts(d *dao.Simple) []util.Uint160 {
|
||||||
m.mtx.RLock()
|
cache := d.Store.GetCache(m.ID).(*ManagementCache)
|
||||||
result := make([]util.Uint160, 0, len(m.nep11))
|
cache.mtx.RLock()
|
||||||
for h := range m.nep11 {
|
result := make([]util.Uint160, 0, len(cache.nep11))
|
||||||
|
for h := range cache.nep11 {
|
||||||
result = append(result, h)
|
result = append(result, h)
|
||||||
}
|
}
|
||||||
m.mtx.RUnlock()
|
cache.mtx.RUnlock()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNEP17Contracts returns hashes of all deployed contracts that support NEP-17 standard. The list
|
// 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 updated every PostPersist, so until PostPersist is called, the result for the previous block
|
||||||
// is returned.
|
// is returned.
|
||||||
func (m *Management) GetNEP17Contracts() []util.Uint160 {
|
func (m *Management) GetNEP17Contracts(d *dao.Simple) []util.Uint160 {
|
||||||
m.mtx.RLock()
|
cache := d.Store.GetCache(m.ID).(*ManagementCache)
|
||||||
result := make([]util.Uint160, 0, len(m.nep17))
|
cache.mtx.RLock()
|
||||||
for h := range m.nep17 {
|
result := make([]util.Uint160, 0, len(cache.nep17))
|
||||||
|
for h := range cache.nep17 {
|
||||||
result = append(result, h)
|
result = append(result, h)
|
||||||
}
|
}
|
||||||
m.mtx.RUnlock()
|
cache.mtx.RUnlock()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,6 +567,13 @@ func (m *Management) GetNEP17Contracts() []util.Uint160 {
|
||||||
func (m *Management) Initialize(ic *interop.Context) error {
|
func (m *Management) Initialize(ic *interop.Context) error {
|
||||||
setIntWithKey(m.ID, ic.DAO, keyMinimumDeploymentFee, defaultMinimumDeploymentFee)
|
setIntWithKey(m.ID, ic.DAO, keyMinimumDeploymentFee, defaultMinimumDeploymentFee)
|
||||||
setIntWithKey(m.ID, ic.DAO, keyNextAvailableID, 1)
|
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
|
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 {
|
if err := putConvertibleToDAO(m.ID, d, key, cs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m.markUpdated(cs.Hash)
|
m.markUpdated(d, cs.Hash)
|
||||||
if cs.UpdateCounter != 0 { // Update.
|
if cs.UpdateCounter != 0 { // Update.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,8 +89,10 @@ func TestManagement_GetNEP17Contracts(t *testing.T) {
|
||||||
d := dao.NewSimple(storage.NewMemoryStore(), false, false)
|
d := dao.NewSimple(storage.NewMemoryStore(), false, false)
|
||||||
err := mgmt.Initialize(&interop.Context{DAO: d})
|
err := mgmt.Initialize(&interop.Context{DAO: d})
|
||||||
require.NoError(t, err)
|
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
|
// Deploy NEP-17 contract
|
||||||
script := []byte{byte(opcode.RET)}
|
script := []byte{byte(opcode.RET)}
|
||||||
|
@ -108,11 +110,11 @@ func TestManagement_GetNEP17Contracts(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// PostPersist is not yet called, thus no NEP-17 contracts are expected
|
// 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
|
// Call PostPersist, check c1 contract hash is returned
|
||||||
require.NoError(t, mgmt.PostPersist(&interop.Context{DAO: d}))
|
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
|
// Update contract
|
||||||
manif.ABI.Methods = append(manif.ABI.Methods, manifest.Method{
|
manif.ABI.Methods = append(manif.ABI.Methods, manifest.Method{
|
||||||
|
@ -124,9 +126,9 @@ func TestManagement_GetNEP17Contracts(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// No changes expected before PostPersist call.
|
// 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
|
// Call PostPersist, check c2 contract hash is returned
|
||||||
require.NoError(t, mgmt.PostPersist(&interop.Context{DAO: d}))
|
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)
|
absAmount := big.NewInt(tx.SystemFee + tx.NetworkFee)
|
||||||
g.burn(ic, tx.Sender(), absAmount)
|
g.burn(ic, tx.Sender(), absAmount)
|
||||||
}
|
}
|
||||||
validators := g.NEO.GetNextBlockValidatorsInternal()
|
validators := g.NEO.GetNextBlockValidatorsInternal(ic.DAO)
|
||||||
primary := validators[ic.Block.PrimaryIndex].GetScriptHash()
|
primary := validators[ic.Block.PrimaryIndex].GetScriptHash()
|
||||||
var netFee int64
|
var netFee int64
|
||||||
for _, tx := range ic.Block.Transactions {
|
for _, tx := range ic.Block.Transactions {
|
||||||
|
|
|
@ -35,6 +35,13 @@ type NEO struct {
|
||||||
GAS *GAS
|
GAS *GAS
|
||||||
Policy *Policy
|
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.
|
// gasPerBlock represents current value of generated gas per block.
|
||||||
// It is append-only and doesn't need to be copied when used.
|
// It is append-only and doesn't need to be copied when used.
|
||||||
gasPerBlock atomic.Value
|
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
|
// It is set in state-modifying methods only and read in `PostPersist` thus is not protected
|
||||||
// by any mutex.
|
// by any mutex.
|
||||||
gasPerVoteCache map[string]big.Int
|
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 (
|
const (
|
||||||
|
@ -129,13 +131,6 @@ func newNEO(cfg config.ProtocolConfiguration) *NEO {
|
||||||
nep17.balFromBytes = n.balanceFromBytes
|
nep17.balFromBytes = n.balanceFromBytes
|
||||||
|
|
||||||
n.nep17TokenNative = *nep17
|
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)
|
err := n.initConfigCache(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -213,9 +208,22 @@ func (n *NEO) Initialize(ic *interop.Context) error {
|
||||||
return errors.New("already initialized")
|
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)]
|
committee0 := n.standbyKeys[:n.cfg.GetCommitteeSize(ic.Block.Index)]
|
||||||
cvs := toKeysWithVotes(committee0)
|
cvs := toKeysWithVotes(committee0)
|
||||||
err := n.updateCache(cvs, ic.Chain)
|
err := n.updateCache(cache, cvs, ic.Chain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -233,13 +241,14 @@ func (n *NEO) Initialize(ic *interop.Context) error {
|
||||||
n.putGASRecord(ic.DAO, index, value)
|
n.putGASRecord(ic.DAO, index, value)
|
||||||
|
|
||||||
gr := &gasRecord{{Index: index, GASPerBlock: *value}}
|
gr := &gasRecord{{Index: index, GASPerBlock: *value}}
|
||||||
n.gasPerBlock.Store(*gr)
|
cache.gasPerBlock.Store(*gr)
|
||||||
n.gasPerBlockChanged.Store(false)
|
cache.gasPerBlockChanged.Store(false)
|
||||||
ic.DAO.PutStorageItem(n.ID, []byte{prefixVotersCount}, state.StorageItem{})
|
ic.DAO.PutStorageItem(n.ID, []byte{prefixVotersCount}, state.StorageItem{})
|
||||||
|
|
||||||
setIntWithKey(n.ID, ic.DAO, []byte{prefixRegisterPrice}, DefaultRegisterPrice)
|
setIntWithKey(n.ID, ic.DAO, []byte{prefixRegisterPrice}, DefaultRegisterPrice)
|
||||||
n.registerPrice.Store(int64(DefaultRegisterPrice))
|
cache.registerPrice.Store(int64(DefaultRegisterPrice))
|
||||||
n.registerPriceChanged.Store(false)
|
cache.registerPriceChanged.Store(false)
|
||||||
|
|
||||||
return nil
|
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
|
// Cache initialisation should be done apart from Initialize because Initialize is
|
||||||
// called only when deploying native contracts.
|
// called only when deploying native contracts.
|
||||||
func (n *NEO) InitializeCache(bc interop.Ledger, d *dao.Simple) error {
|
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{}
|
var committee = keysWithVotes{}
|
||||||
si := d.GetStorageItem(n.ID, prefixCommittee)
|
si := d.GetStorageItem(n.ID, prefixCommittee)
|
||||||
if err := committee.DecodeBytes(si); err != nil {
|
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 {
|
if err := n.updateCache(cache, committee, bc); err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to update cache: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
n.gasPerBlock.Store(n.getSortedGASRecordFromDAO(d))
|
cache.gasPerBlock.Store(n.getSortedGASRecordFromDAO(d))
|
||||||
n.gasPerBlockChanged.Store(false)
|
cache.gasPerBlockChanged.Store(false)
|
||||||
|
|
||||||
|
d.Store.SetCache(n.ID, cache)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,27 +291,28 @@ func (n *NEO) initConfigCache(cfg config.ProtocolConfiguration) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) updateCache(cvs keysWithVotes, bc interop.Ledger) error {
|
func (n *NEO) updateCache(cache *NeoCache, cvs keysWithVotes, bc interop.Ledger) error {
|
||||||
n.committee.Store(cvs)
|
cache.committee.Store(cvs)
|
||||||
|
|
||||||
var committee = n.GetCommitteeMembers()
|
var committee = getCommitteeMembers(cache)
|
||||||
script, err := smartcontract.CreateMajorityMultiSigRedeemScript(committee.Copy())
|
script, err := smartcontract.CreateMajorityMultiSigRedeemScript(committee.Copy())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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()
|
nextVals := committee[:n.cfg.GetNumOfCNs(bc.BlockHeight()+1)].Copy()
|
||||||
sort.Sort(nextVals)
|
sort.Sort(nextVals)
|
||||||
n.nextValidators.Store(nextVals)
|
cache.nextValidators.Store(nextVals)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) updateCommittee(ic *interop.Context) error {
|
func (n *NEO) updateCommittee(cache *NeoCache, ic *interop.Context) error {
|
||||||
votesChanged := n.votesChanged.Load().(bool)
|
votesChanged := cache.votesChanged.Load().(bool)
|
||||||
if !votesChanged {
|
if !votesChanged {
|
||||||
// We need to put in storage anyway, as it affects dumps
|
// 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())
|
ic.DAO.PutStorageItem(n.ID, prefixCommittee, committee.Bytes())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -299,10 +321,10 @@ func (n *NEO) updateCommittee(ic *interop.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := n.updateCache(cvs, ic.Chain); err != nil {
|
if err := n.updateCache(cache, cvs, ic.Chain); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
n.votesChanged.Store(false)
|
cache.votesChanged.Store(false)
|
||||||
ic.DAO.PutStorageItem(n.ID, prefixCommittee, cvs.Bytes())
|
ic.DAO.PutStorageItem(n.ID, prefixCommittee, cvs.Bytes())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -310,13 +332,14 @@ func (n *NEO) updateCommittee(ic *interop.Context) error {
|
||||||
// OnPersist implements Contract interface.
|
// OnPersist implements Contract interface.
|
||||||
func (n *NEO) OnPersist(ic *interop.Context) error {
|
func (n *NEO) OnPersist(ic *interop.Context) error {
|
||||||
if n.cfg.ShouldUpdateCommitteeAt(ic.Block.Index) {
|
if n.cfg.ShouldUpdateCommitteeAt(ic.Block.Index) {
|
||||||
oldKeys := n.nextValidators.Load().(keys.PublicKeys)
|
cache := ic.DAO.Store.GetCache(n.ID).(*NeoCache)
|
||||||
oldCom := n.committee.Load().(keysWithVotes)
|
oldKeys := cache.nextValidators.Load().(keys.PublicKeys)
|
||||||
|
oldCom := cache.committee.Load().(keysWithVotes)
|
||||||
if n.cfg.GetNumOfCNs(ic.Block.Index) != len(oldKeys) ||
|
if n.cfg.GetNumOfCNs(ic.Block.Index) != len(oldKeys) ||
|
||||||
n.cfg.GetCommitteeSize(ic.Block.Index) != len(oldCom) {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,7 +349,8 @@ func (n *NEO) OnPersist(ic *interop.Context) error {
|
||||||
// PostPersist implements Contract interface.
|
// PostPersist implements Contract interface.
|
||||||
func (n *NEO) PostPersist(ic *interop.Context) error {
|
func (n *NEO) PostPersist(ic *interop.Context) error {
|
||||||
gas := n.GetGASPerBlock(ic.DAO, ic.Block.Index)
|
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)
|
committeeSize := n.cfg.GetCommitteeSize(ic.Block.Index)
|
||||||
index := int(ic.Block.Index) % committeeSize
|
index := int(ic.Block.Index) % committeeSize
|
||||||
committeeReward := new(big.Int).Mul(gas, bigCommitteeRewardRatio)
|
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, big.NewInt(int64(committeeSize+validatorsCount)))
|
||||||
voterReward.Div(voterReward, big100)
|
voterReward.Div(voterReward, big100)
|
||||||
|
|
||||||
var cs = n.committee.Load().(keysWithVotes)
|
var cs = cache.committee.Load().(keysWithVotes)
|
||||||
var key = make([]byte, 38)
|
var key = make([]byte, 38)
|
||||||
for i := range cs {
|
for i := range cs {
|
||||||
if cs[i].Votes.Sign() > 0 {
|
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)
|
key = makeVoterKey([]byte(cs[i].Key), key)
|
||||||
|
|
||||||
var r *big.Int
|
var r *big.Int
|
||||||
if g, ok := n.gasPerVoteCache[cs[i].Key]; ok {
|
if g, ok := cache.gasPerVoteCache[cs[i].Key]; ok {
|
||||||
r = &g
|
r = &g
|
||||||
} else {
|
} else {
|
||||||
reward := n.getGASPerVote(ic.DAO, key[:34], []uint32{ic.Block.Index + 1})
|
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)
|
tmp.Add(tmp, r)
|
||||||
|
|
||||||
binary.BigEndian.PutUint32(key[34:], ic.Block.Index+1)
|
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))
|
ic.DAO.PutStorageItem(n.ID, key, bigint.ToBytes(tmp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if n.gasPerBlockChanged.Load().(bool) {
|
if cache.gasPerBlockChanged.Load().(bool) {
|
||||||
n.gasPerBlock.Store(n.getSortedGASRecordFromDAO(ic.DAO))
|
cache.gasPerBlock.Store(n.getSortedGASRecordFromDAO(ic.DAO))
|
||||||
n.gasPerBlockChanged.Store(false)
|
cache.gasPerBlockChanged.Store(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.registerPriceChanged.Load().(bool) {
|
if cache.registerPriceChanged.Load().(bool) {
|
||||||
p := getIntWithKey(n.ID, ic.DAO, []byte{prefixRegisterPrice})
|
p := getIntWithKey(n.ID, ic.DAO, []byte{prefixRegisterPrice})
|
||||||
n.registerPrice.Store(p)
|
cache.registerPrice.Store(p)
|
||||||
n.registerPriceChanged.Store(false)
|
cache.registerPriceChanged.Store(false)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -502,11 +526,12 @@ func (n *NEO) getSortedGASRecordFromDAO(d *dao.Simple) gasRecord {
|
||||||
|
|
||||||
// GetGASPerBlock returns gas generated for block with provided index.
|
// GetGASPerBlock returns gas generated for block with provided index.
|
||||||
func (n *NEO) GetGASPerBlock(d *dao.Simple, index uint32) *big.Int {
|
func (n *NEO) GetGASPerBlock(d *dao.Simple, index uint32) *big.Int {
|
||||||
|
cache := d.Store.GetCache(n.ID).(*NeoCache)
|
||||||
var gr gasRecord
|
var gr gasRecord
|
||||||
if n.gasPerBlockChanged.Load().(bool) {
|
if cache.gasPerBlockChanged.Load().(bool) {
|
||||||
gr = n.getSortedGASRecordFromDAO(d)
|
gr = n.getSortedGASRecordFromDAO(d)
|
||||||
} else {
|
} else {
|
||||||
gr = n.gasPerBlock.Load().(gasRecord)
|
gr = cache.gasPerBlock.Load().(gasRecord)
|
||||||
}
|
}
|
||||||
for i := len(gr) - 1; i >= 0; i-- {
|
for i := len(gr) - 1; i >= 0; i-- {
|
||||||
if gr[i].Index <= index {
|
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.
|
// GetCommitteeAddress returns address of the committee.
|
||||||
func (n *NEO) GetCommitteeAddress() util.Uint160 {
|
func (n *NEO) GetCommitteeAddress(d *dao.Simple) util.Uint160 {
|
||||||
return n.committeeHash.Load().(util.Uint160)
|
cache := d.Store.GetCache(n.ID).(*NeoCache)
|
||||||
|
return cache.committeeHash.Load().(util.Uint160)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) checkCommittee(ic *interop.Context) bool {
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -547,7 +573,8 @@ func (n *NEO) SetGASPerBlock(ic *interop.Context, index uint32, gas *big.Int) er
|
||||||
if !n.checkCommittee(ic) {
|
if !n.checkCommittee(ic) {
|
||||||
return errors.New("invalid committee signature")
|
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)
|
n.putGASRecord(ic.DAO, index, gas)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -557,8 +584,9 @@ func (n *NEO) getRegisterPrice(ic *interop.Context, _ []stackitem.Item) stackite
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) getRegisterPriceInternal(d *dao.Simple) int64 {
|
func (n *NEO) getRegisterPriceInternal(d *dao.Simple) int64 {
|
||||||
if !n.registerPriceChanged.Load().(bool) {
|
cache := d.Store.GetCache(n.ID).(*NeoCache)
|
||||||
return n.registerPrice.Load().(int64)
|
if !cache.registerPriceChanged.Load().(bool) {
|
||||||
|
return cache.registerPrice.Load().(int64)
|
||||||
}
|
}
|
||||||
return getIntWithKey(n.ID, d, []byte{prefixRegisterPrice})
|
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())
|
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{}
|
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 {
|
if c.Registered || c.Votes.Sign() != 0 {
|
||||||
return false, nil
|
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.
|
d.DeleteStorageItem(n.ID, append(voterKey, k...)) // d.Seek cuts prefix, thus need to append it again.
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
delete(n.gasPerVoteCache, string(voterKey))
|
delete(cache.gasPerVoteCache, string(voterKey))
|
||||||
|
|
||||||
return true, nil
|
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")
|
return nil, errors.New("negative value")
|
||||||
}
|
}
|
||||||
var gr gasRecord
|
var gr gasRecord
|
||||||
if !n.gasPerBlockChanged.Load().(bool) {
|
cache := d.Store.GetCache(n.ID).(*NeoCache)
|
||||||
gr = n.gasPerBlock.Load().(gasRecord)
|
if !cache.gasPerBlockChanged.Load().(bool) {
|
||||||
|
gr = cache.gasPerBlock.Load().(gasRecord)
|
||||||
} else {
|
} else {
|
||||||
gr = n.getSortedGASRecordFromDAO(d)
|
gr = n.getSortedGASRecordFromDAO(d)
|
||||||
}
|
}
|
||||||
|
@ -717,10 +747,11 @@ func (n *NEO) UnregisterCandidateInternal(ic *interop.Context, pub *keys.PublicK
|
||||||
if si == nil {
|
if si == nil {
|
||||||
return 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 := new(candidate).FromBytes(si)
|
||||||
c.Registered = false
|
c.Registered = false
|
||||||
ok, err := n.dropCandidateIfZero(ic.DAO, pub, c)
|
ok, err := n.dropCandidateIfZero(ic.DAO, cache, pub, c)
|
||||||
if ok {
|
if ok {
|
||||||
return err
|
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).
|
// 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).
|
// 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 {
|
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 {
|
if acc.VoteTo != nil {
|
||||||
key := makeValidatorKey(acc.VoteTo)
|
key := makeValidatorKey(acc.VoteTo)
|
||||||
si := d.GetStorageItem(n.ID, key)
|
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 := new(candidate).FromBytes(si)
|
||||||
cd.Votes.Add(&cd.Votes, value)
|
cd.Votes.Add(&cd.Votes, value)
|
||||||
if !isNewVote {
|
if !isNewVote {
|
||||||
ok, err := n.dropCandidateIfZero(d, acc.VoteTo, cd)
|
ok, err := n.dropCandidateIfZero(d, cache, acc.VoteTo, cd)
|
||||||
if ok {
|
if ok {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
n.validators.Store(keys.PublicKeys(nil))
|
cache.validators.Store(keys.PublicKeys(nil))
|
||||||
return putConvertibleToDAO(n.ID, d, key, cd)
|
return putConvertibleToDAO(n.ID, d, key, cd)
|
||||||
}
|
}
|
||||||
return nil
|
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.
|
// ComputeNextBlockValidators returns an actual list of current validators.
|
||||||
func (n *NEO) ComputeNextBlockValidators(bc interop.Ledger, d *dao.Simple) (keys.PublicKeys, error) {
|
func (n *NEO) ComputeNextBlockValidators(bc interop.Ledger, d *dao.Simple) (keys.PublicKeys, error) {
|
||||||
numOfCNs := n.cfg.GetNumOfCNs(bc.BlockHeight() + 1)
|
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
|
return vals.Copy(), nil
|
||||||
}
|
}
|
||||||
result, _, err := n.computeCommitteeMembers(bc, d)
|
result, _, err := n.computeCommitteeMembers(bc, d)
|
||||||
|
@ -915,12 +948,12 @@ func (n *NEO) ComputeNextBlockValidators(bc interop.Ledger, d *dao.Simple) (keys
|
||||||
}
|
}
|
||||||
result = result[:numOfCNs]
|
result = result[:numOfCNs]
|
||||||
sort.Sort(result)
|
sort.Sort(result)
|
||||||
n.validators.Store(result)
|
cache.validators.Store(result)
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) getCommittee(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
func (n *NEO) getCommittee(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
pubs := n.GetCommitteeMembers()
|
pubs := n.GetCommitteeMembers(ic.DAO)
|
||||||
sort.Sort(pubs)
|
sort.Sort(pubs)
|
||||||
return pubsToArray(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.
|
// GetCommitteeMembers returns public keys of nodes in committee using cached value.
|
||||||
func (n *NEO) GetCommitteeMembers() keys.PublicKeys {
|
func (n *NEO) GetCommitteeMembers(d *dao.Simple) keys.PublicKeys {
|
||||||
var cvs = n.committee.Load().(keysWithVotes)
|
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 committee = make(keys.PublicKeys, len(cvs))
|
||||||
var err error
|
var err error
|
||||||
for i := range committee {
|
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 {
|
func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
result := n.GetNextBlockValidatorsInternal()
|
result := n.GetNextBlockValidatorsInternal(ic.DAO)
|
||||||
return pubsToArray(result)
|
return pubsToArray(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNextBlockValidatorsInternal returns next block validators.
|
// GetNextBlockValidatorsInternal returns next block validators.
|
||||||
func (n *NEO) GetNextBlockValidatorsInternal() keys.PublicKeys {
|
func (n *NEO) GetNextBlockValidatorsInternal(d *dao.Simple) keys.PublicKeys {
|
||||||
return n.nextValidators.Load().(keys.PublicKeys).Copy()
|
cache := d.Store.GetCache(n.ID).(*NeoCache)
|
||||||
|
return cache.nextValidators.Load().(keys.PublicKeys).Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// BalanceOf returns native NEO token balance for the acc.
|
// BalanceOf returns native NEO token balance for the acc.
|
||||||
|
|
|
@ -32,7 +32,9 @@ type Notary struct {
|
||||||
GAS *GAS
|
GAS *GAS
|
||||||
NEO *NEO
|
NEO *NEO
|
||||||
Desig *Designate
|
Desig *Designate
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotaryCache struct {
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
// isValid defies whether cached values were changed during the current
|
// isValid defies whether cached values were changed during the current
|
||||||
// consensus iteration. If false, these values will be updated after
|
// 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 {
|
func (n *Notary) Initialize(ic *interop.Context) error {
|
||||||
setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, defaultMaxNotValidBeforeDelta)
|
setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, defaultMaxNotValidBeforeDelta)
|
||||||
setIntWithKey(n.ID, ic.DAO, notaryServiceFeeKey, defaultNotaryServiceFeePerKey)
|
setIntWithKey(n.ID, ic.DAO, notaryServiceFeeKey, defaultNotaryServiceFeePerKey)
|
||||||
n.isValid = true
|
|
||||||
n.maxNotValidBeforeDelta = defaultMaxNotValidBeforeDelta
|
cache := &NotaryCache{
|
||||||
n.notaryServiceFeePerKey = defaultNotaryServiceFeePerKey
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,15 +192,16 @@ func (n *Notary) OnPersist(ic *interop.Context) error {
|
||||||
|
|
||||||
// PostPersist implements Contract interface.
|
// PostPersist implements Contract interface.
|
||||||
func (n *Notary) PostPersist(ic *interop.Context) error {
|
func (n *Notary) PostPersist(ic *interop.Context) error {
|
||||||
n.lock.Lock()
|
cache := ic.DAO.Store.GetCache(n.ID).(*NotaryCache)
|
||||||
defer n.lock.Unlock()
|
cache.lock.Lock()
|
||||||
if n.isValid {
|
defer cache.lock.Unlock()
|
||||||
|
if cache.isValid {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
n.maxNotValidBeforeDelta = uint32(getIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey))
|
cache.maxNotValidBeforeDelta = uint32(getIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey))
|
||||||
n.notaryServiceFeePerKey = getIntWithKey(n.ID, ic.DAO, notaryServiceFeeKey)
|
cache.notaryServiceFeePerKey = getIntWithKey(n.ID, ic.DAO, notaryServiceFeeKey)
|
||||||
n.isValid = true
|
cache.isValid = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,10 +408,11 @@ func (n *Notary) getMaxNotValidBeforeDelta(ic *interop.Context, _ []stackitem.It
|
||||||
|
|
||||||
// GetMaxNotValidBeforeDelta is an internal representation of Notary getMaxNotValidBeforeDelta method.
|
// GetMaxNotValidBeforeDelta is an internal representation of Notary getMaxNotValidBeforeDelta method.
|
||||||
func (n *Notary) GetMaxNotValidBeforeDelta(dao *dao.Simple) uint32 {
|
func (n *Notary) GetMaxNotValidBeforeDelta(dao *dao.Simple) uint32 {
|
||||||
n.lock.RLock()
|
cache := dao.Store.GetCache(n.ID).(*NotaryCache)
|
||||||
defer n.lock.RUnlock()
|
cache.lock.RLock()
|
||||||
if n.isValid {
|
defer cache.lock.RUnlock()
|
||||||
return n.maxNotValidBeforeDelta
|
if cache.isValid {
|
||||||
|
return cache.maxNotValidBeforeDelta
|
||||||
}
|
}
|
||||||
return uint32(getIntWithKey(n.ID, dao, maxNotValidBeforeDeltaKey))
|
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) {
|
if !n.NEO.checkCommittee(ic) {
|
||||||
panic("invalid committee signature")
|
panic("invalid committee signature")
|
||||||
}
|
}
|
||||||
n.lock.Lock()
|
cache := ic.DAO.Store.GetCache(n.ID).(*NotaryCache)
|
||||||
defer n.lock.Unlock()
|
cache.lock.Lock()
|
||||||
|
defer cache.lock.Unlock()
|
||||||
setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, int64(value))
|
setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, int64(value))
|
||||||
n.isValid = false
|
cache.isValid = false
|
||||||
return stackitem.Null{}
|
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.
|
// GetNotaryServiceFeePerKey is an internal representation of Notary getNotaryServiceFeePerKey method.
|
||||||
func (n *Notary) GetNotaryServiceFeePerKey(dao *dao.Simple) int64 {
|
func (n *Notary) GetNotaryServiceFeePerKey(dao *dao.Simple) int64 {
|
||||||
n.lock.RLock()
|
cache := dao.Store.GetCache(n.ID).(*NotaryCache)
|
||||||
defer n.lock.RUnlock()
|
cache.lock.RLock()
|
||||||
if n.isValid {
|
defer cache.lock.RUnlock()
|
||||||
return n.notaryServiceFeePerKey
|
if cache.isValid {
|
||||||
|
return cache.notaryServiceFeePerKey
|
||||||
}
|
}
|
||||||
return getIntWithKey(n.ID, dao, notaryServiceFeeKey)
|
return getIntWithKey(n.ID, dao, notaryServiceFeeKey)
|
||||||
}
|
}
|
||||||
|
@ -441,10 +461,11 @@ func (n *Notary) setNotaryServiceFeePerKey(ic *interop.Context, args []stackitem
|
||||||
if !n.NEO.checkCommittee(ic) {
|
if !n.NEO.checkCommittee(ic) {
|
||||||
panic("invalid committee signature")
|
panic("invalid committee signature")
|
||||||
}
|
}
|
||||||
n.lock.Lock()
|
cache := ic.DAO.Store.GetCache(n.ID).(*NotaryCache)
|
||||||
defer n.lock.Unlock()
|
cache.lock.Lock()
|
||||||
|
defer cache.lock.Unlock()
|
||||||
setIntWithKey(n.ID, ic.DAO, notaryServiceFeeKey, int64(value))
|
setIntWithKey(n.ID, ic.DAO, notaryServiceFeeKey, int64(value))
|
||||||
n.isValid = false
|
cache.isValid = false
|
||||||
return stackitem.Null{}
|
return stackitem.Null{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,15 +40,17 @@ type Oracle struct {
|
||||||
Desig *Designate
|
Desig *Designate
|
||||||
oracleScript []byte
|
oracleScript []byte
|
||||||
|
|
||||||
requestPrice atomic.Value
|
|
||||||
requestPriceChanged atomic.Value
|
|
||||||
|
|
||||||
// Module is an oracle module capable of talking with the external world.
|
// Module is an oracle module capable of talking with the external world.
|
||||||
Module atomic.Value
|
Module atomic.Value
|
||||||
// newRequests contains new requests created during current block.
|
// newRequests contains new requests created during current block.
|
||||||
newRequests map[uint64]*state.OracleRequest
|
newRequests map[uint64]*state.OracleRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OracleCache struct {
|
||||||
|
requestPrice atomic.Value
|
||||||
|
requestPriceChanged atomic.Value
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
oracleContractID = -9
|
oracleContractID = -9
|
||||||
maxURLLength = 256
|
maxURLLength = 256
|
||||||
|
@ -121,8 +123,6 @@ func newOracle() *Oracle {
|
||||||
md = newMethodAndPrice(o.setPrice, 1<<15, callflag.States)
|
md = newMethodAndPrice(o.setPrice, 1<<15, callflag.States)
|
||||||
o.AddMethod(md, desc)
|
o.AddMethod(md, desc)
|
||||||
|
|
||||||
o.requestPriceChanged.Store(true)
|
|
||||||
|
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,9 +143,10 @@ func (o *Oracle) OnPersist(ic *interop.Context) error {
|
||||||
// PostPersist represents `postPersist` method.
|
// PostPersist represents `postPersist` method.
|
||||||
func (o *Oracle) PostPersist(ic *interop.Context) error {
|
func (o *Oracle) PostPersist(ic *interop.Context) error {
|
||||||
p := o.getPriceInternal(ic.DAO)
|
p := o.getPriceInternal(ic.DAO)
|
||||||
if o.requestPriceChanged.Load().(bool) {
|
cache := ic.DAO.Store.GetCache(o.ID).(*OracleCache)
|
||||||
o.requestPrice.Store(p)
|
if cache.requestPriceChanged.Load().(bool) {
|
||||||
o.requestPriceChanged.Store(false)
|
cache.requestPrice.Store(p)
|
||||||
|
cache.requestPriceChanged.Store(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
var nodes keys.PublicKeys
|
var nodes keys.PublicKeys
|
||||||
|
@ -220,11 +221,21 @@ func (o *Oracle) Metadata() *interop.ContractMD {
|
||||||
func (o *Oracle) Initialize(ic *interop.Context) error {
|
func (o *Oracle) Initialize(ic *interop.Context) error {
|
||||||
setIntWithKey(o.ID, ic.DAO, prefixRequestID, 0)
|
setIntWithKey(o.ID, ic.DAO, prefixRequestID, 0)
|
||||||
setIntWithKey(o.ID, ic.DAO, prefixRequestPrice, DefaultOracleRequestPrice)
|
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
|
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 {
|
func getResponse(tx *transaction.Transaction) *transaction.OracleResponse {
|
||||||
for i := range tx.Attributes {
|
for i := range tx.Attributes {
|
||||||
if tx.Attributes[i].Type == transaction.OracleResponseT {
|
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 {
|
func (o *Oracle) getPriceInternal(d *dao.Simple) int64 {
|
||||||
if !o.requestPriceChanged.Load().(bool) {
|
cache := d.Store.GetCache(o.ID).(*OracleCache)
|
||||||
return o.requestPrice.Load().(int64)
|
if !cache.requestPriceChanged.Load().(bool) {
|
||||||
|
return cache.requestPrice.Load().(int64)
|
||||||
}
|
}
|
||||||
return getIntWithKey(o.ID, d, prefixRequestPrice)
|
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")
|
panic("invalid committee signature")
|
||||||
}
|
}
|
||||||
setIntWithKey(o.ID, ic.DAO, prefixRequestPrice, price.Int64())
|
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{}
|
return stackitem.Null{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,9 @@ var (
|
||||||
type Policy struct {
|
type Policy struct {
|
||||||
interop.ContractMD
|
interop.ContractMD
|
||||||
NEO *NEO
|
NEO *NEO
|
||||||
|
}
|
||||||
|
|
||||||
|
type PolicyCache struct {
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
// isValid defies whether cached values were changed during the current
|
// isValid defies whether cached values were changed during the current
|
||||||
// consensus iteration. If false, these values will be updated after
|
// 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, execFeeFactorKey, defaultExecFeeFactor)
|
||||||
setIntWithKey(p.ID, ic.DAO, storagePriceKey, DefaultStoragePrice)
|
setIntWithKey(p.ID, ic.DAO, storagePriceKey, DefaultStoragePrice)
|
||||||
|
|
||||||
p.isValid = true
|
cache := &PolicyCache{}
|
||||||
p.execFeeFactor = defaultExecFeeFactor
|
cache.isValid = true
|
||||||
p.feePerByte = defaultFeePerByte
|
cache.execFeeFactor = defaultExecFeeFactor
|
||||||
p.maxVerificationGas = defaultMaxVerificationGas
|
cache.feePerByte = defaultFeePerByte
|
||||||
p.storagePrice = DefaultStoragePrice
|
cache.maxVerificationGas = defaultMaxVerificationGas
|
||||||
p.blockedAccounts = make([]util.Uint160, 0)
|
cache.storagePrice = DefaultStoragePrice
|
||||||
|
cache.blockedAccounts = make([]util.Uint160, 0)
|
||||||
|
ic.DAO.Store.SetCache(p.ID, cache)
|
||||||
|
|
||||||
return nil
|
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.
|
// OnPersist implements Contract interface.
|
||||||
func (p *Policy) OnPersist(ic *interop.Context) error {
|
func (p *Policy) OnPersist(ic *interop.Context) error {
|
||||||
return nil
|
return nil
|
||||||
|
@ -145,32 +184,14 @@ func (p *Policy) OnPersist(ic *interop.Context) error {
|
||||||
|
|
||||||
// PostPersist implements Contract interface.
|
// PostPersist implements Contract interface.
|
||||||
func (p *Policy) PostPersist(ic *interop.Context) error {
|
func (p *Policy) PostPersist(ic *interop.Context) error {
|
||||||
p.lock.Lock()
|
cache := ic.DAO.Store.GetCache(p.ID).(*PolicyCache)
|
||||||
defer p.lock.Unlock()
|
cache.lock.Lock()
|
||||||
if p.isValid {
|
defer cache.lock.Unlock()
|
||||||
|
if cache.isValid {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
p.execFeeFactor = uint32(getIntWithKey(p.ID, ic.DAO, execFeeFactorKey))
|
return p.fillCacheFromDAO(cache, ic.DAO)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFeePerByte is Policy contract method and returns required transaction's fee
|
// 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.
|
// GetFeePerByteInternal returns required transaction's fee per byte.
|
||||||
func (p *Policy) GetFeePerByteInternal(dao *dao.Simple) int64 {
|
func (p *Policy) GetFeePerByteInternal(dao *dao.Simple) int64 {
|
||||||
p.lock.RLock()
|
cache := dao.Store.GetCache(p.ID).(*PolicyCache)
|
||||||
defer p.lock.RUnlock()
|
cache.lock.RLock()
|
||||||
if p.isValid {
|
defer cache.lock.RUnlock()
|
||||||
return p.feePerByte
|
if cache.isValid {
|
||||||
|
return cache.feePerByte
|
||||||
}
|
}
|
||||||
return getIntWithKey(p.ID, dao, feePerByteKey)
|
return getIntWithKey(p.ID, dao, feePerByteKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMaxVerificationGas returns maximum gas allowed to be burned during verificaion.
|
// GetMaxVerificationGas returns maximum gas allowed to be burned during verificaion.
|
||||||
func (p *Policy) GetMaxVerificationGas(_ *dao.Simple) int64 {
|
func (p *Policy) GetMaxVerificationGas(dao *dao.Simple) int64 {
|
||||||
if p.isValid {
|
cache := dao.Store.GetCache(p.ID).(*PolicyCache)
|
||||||
return p.maxVerificationGas
|
cache.lock.RLock()
|
||||||
|
defer cache.lock.RUnlock()
|
||||||
|
if cache.isValid {
|
||||||
|
return cache.maxVerificationGas
|
||||||
}
|
}
|
||||||
return defaultMaxVerificationGas
|
return defaultMaxVerificationGas
|
||||||
}
|
}
|
||||||
|
@ -203,10 +228,11 @@ func (p *Policy) getExecFeeFactor(ic *interop.Context, _ []stackitem.Item) stack
|
||||||
|
|
||||||
// GetExecFeeFactorInternal returns current execution fee factor.
|
// GetExecFeeFactorInternal returns current execution fee factor.
|
||||||
func (p *Policy) GetExecFeeFactorInternal(d *dao.Simple) int64 {
|
func (p *Policy) GetExecFeeFactorInternal(d *dao.Simple) int64 {
|
||||||
p.lock.RLock()
|
cache := d.Store.GetCache(p.ID).(*PolicyCache)
|
||||||
defer p.lock.RUnlock()
|
cache.lock.RLock()
|
||||||
if p.isValid {
|
defer cache.lock.RUnlock()
|
||||||
return int64(p.execFeeFactor)
|
if cache.isValid {
|
||||||
|
return int64(cache.execFeeFactor)
|
||||||
}
|
}
|
||||||
return getIntWithKey(p.ID, d, execFeeFactorKey)
|
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) {
|
if !p.NEO.checkCommittee(ic) {
|
||||||
panic("invalid committee signature")
|
panic("invalid committee signature")
|
||||||
}
|
}
|
||||||
p.lock.Lock()
|
cache := ic.DAO.Store.GetCache(p.ID).(*PolicyCache)
|
||||||
defer p.lock.Unlock()
|
cache.lock.Lock()
|
||||||
|
defer cache.lock.Unlock()
|
||||||
setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, int64(value))
|
setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, int64(value))
|
||||||
p.isValid = false
|
cache.isValid = false
|
||||||
return stackitem.Null{}
|
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.
|
// IsBlockedInternal checks whether provided account is blocked.
|
||||||
func (p *Policy) IsBlockedInternal(dao *dao.Simple, hash util.Uint160) bool {
|
func (p *Policy) IsBlockedInternal(dao *dao.Simple, hash util.Uint160) bool {
|
||||||
p.lock.RLock()
|
cache := dao.Store.GetCache(p.ID).(*PolicyCache)
|
||||||
defer p.lock.RUnlock()
|
cache.lock.RLock()
|
||||||
if p.isValid {
|
defer cache.lock.RUnlock()
|
||||||
length := len(p.blockedAccounts)
|
if cache.isValid {
|
||||||
|
length := len(cache.blockedAccounts)
|
||||||
i := sort.Search(length, func(i int) bool {
|
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 true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -256,10 +284,11 @@ func (p *Policy) getStoragePrice(ic *interop.Context, _ []stackitem.Item) stacki
|
||||||
|
|
||||||
// GetStoragePriceInternal returns current execution fee factor.
|
// GetStoragePriceInternal returns current execution fee factor.
|
||||||
func (p *Policy) GetStoragePriceInternal(d *dao.Simple) int64 {
|
func (p *Policy) GetStoragePriceInternal(d *dao.Simple) int64 {
|
||||||
p.lock.RLock()
|
cache := d.Store.GetCache(p.ID).(*PolicyCache)
|
||||||
defer p.lock.RUnlock()
|
cache.lock.RLock()
|
||||||
if p.isValid {
|
defer cache.lock.RUnlock()
|
||||||
return int64(p.storagePrice)
|
if cache.isValid {
|
||||||
|
return int64(cache.storagePrice)
|
||||||
}
|
}
|
||||||
return getIntWithKey(p.ID, d, storagePriceKey)
|
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) {
|
if !p.NEO.checkCommittee(ic) {
|
||||||
panic("invalid committee signature")
|
panic("invalid committee signature")
|
||||||
}
|
}
|
||||||
p.lock.Lock()
|
cache := ic.DAO.Store.GetCache(p.ID).(*PolicyCache)
|
||||||
defer p.lock.Unlock()
|
cache.lock.Lock()
|
||||||
|
defer cache.lock.Unlock()
|
||||||
setIntWithKey(p.ID, ic.DAO, storagePriceKey, int64(value))
|
setIntWithKey(p.ID, ic.DAO, storagePriceKey, int64(value))
|
||||||
p.isValid = false
|
cache.isValid = false
|
||||||
return stackitem.Null{}
|
return stackitem.Null{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,10 +318,11 @@ func (p *Policy) setFeePerByte(ic *interop.Context, args []stackitem.Item) stack
|
||||||
if !p.NEO.checkCommittee(ic) {
|
if !p.NEO.checkCommittee(ic) {
|
||||||
panic("invalid committee signature")
|
panic("invalid committee signature")
|
||||||
}
|
}
|
||||||
p.lock.Lock()
|
cache := ic.DAO.Store.GetCache(p.ID).(*PolicyCache)
|
||||||
defer p.lock.Unlock()
|
cache.lock.Lock()
|
||||||
|
defer cache.lock.Unlock()
|
||||||
setIntWithKey(p.ID, ic.DAO, feePerByteKey, value)
|
setIntWithKey(p.ID, ic.DAO, feePerByteKey, value)
|
||||||
p.isValid = false
|
cache.isValid = false
|
||||||
return stackitem.Null{}
|
return stackitem.Null{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,10 +342,11 @@ func (p *Policy) blockAccount(ic *interop.Context, args []stackitem.Item) stacki
|
||||||
return stackitem.NewBool(false)
|
return stackitem.NewBool(false)
|
||||||
}
|
}
|
||||||
key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...)
|
key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...)
|
||||||
p.lock.Lock()
|
cache := ic.DAO.Store.GetCache(p.ID).(*PolicyCache)
|
||||||
defer p.lock.Unlock()
|
cache.lock.Lock()
|
||||||
|
defer cache.lock.Unlock()
|
||||||
ic.DAO.PutStorageItem(p.ID, key, state.StorageItem{})
|
ic.DAO.PutStorageItem(p.ID, key, state.StorageItem{})
|
||||||
p.isValid = false
|
cache.isValid = false
|
||||||
return stackitem.NewBool(true)
|
return stackitem.NewBool(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,10 +361,11 @@ func (p *Policy) unblockAccount(ic *interop.Context, args []stackitem.Item) stac
|
||||||
return stackitem.NewBool(false)
|
return stackitem.NewBool(false)
|
||||||
}
|
}
|
||||||
key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...)
|
key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...)
|
||||||
p.lock.Lock()
|
cache := ic.DAO.Store.GetCache(p.ID).(*PolicyCache)
|
||||||
defer p.lock.Unlock()
|
cache.lock.Lock()
|
||||||
|
defer cache.lock.Unlock()
|
||||||
ic.DAO.DeleteStorageItem(p.ID, key)
|
ic.DAO.DeleteStorageItem(p.ID, key)
|
||||||
p.isValid = false
|
cache.isValid = false
|
||||||
return stackitem.NewBool(true)
|
return stackitem.NewBool(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,9 @@ import (
|
||||||
type MemCachedStore struct {
|
type MemCachedStore struct {
|
||||||
MemoryStore
|
MemoryStore
|
||||||
|
|
||||||
|
nativeCacheLock sync.RWMutex
|
||||||
|
nativeCache map[int32]*NativeCacheItem
|
||||||
|
|
||||||
private bool
|
private bool
|
||||||
// plock protects Persist from double entrance.
|
// plock protects Persist from double entrance.
|
||||||
plock sync.Mutex
|
plock sync.Mutex
|
||||||
|
@ -22,6 +25,10 @@ type MemCachedStore struct {
|
||||||
ps Store
|
ps Store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NativeCacheItem struct {
|
||||||
|
Value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// KeyValue represents key-value pair.
|
// KeyValue represents key-value pair.
|
||||||
KeyValue struct {
|
KeyValue struct {
|
||||||
|
@ -46,8 +53,15 @@ type (
|
||||||
|
|
||||||
// NewMemCachedStore creates a new MemCachedStore object.
|
// NewMemCachedStore creates a new MemCachedStore object.
|
||||||
func NewMemCachedStore(lower Store) *MemCachedStore {
|
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{
|
return &MemCachedStore{
|
||||||
MemoryStore: *NewMemoryStore(),
|
MemoryStore: *NewMemoryStore(),
|
||||||
|
nativeCache: cache,
|
||||||
ps: lower,
|
ps: lower,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,8 +69,15 @@ func NewMemCachedStore(lower Store) *MemCachedStore {
|
||||||
// NewPrivateMemCachedStore creates a new private (unlocked) MemCachedStore object.
|
// NewPrivateMemCachedStore creates a new private (unlocked) MemCachedStore object.
|
||||||
// Private cached stores are closed after Persist.
|
// Private cached stores are closed after Persist.
|
||||||
func NewPrivateMemCachedStore(lower Store) *MemCachedStore {
|
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{
|
return &MemCachedStore{
|
||||||
MemoryStore: *NewMemoryStore(),
|
MemoryStore: *NewMemoryStore(),
|
||||||
|
nativeCache: cache,
|
||||||
private: true,
|
private: true,
|
||||||
ps: lower,
|
ps: lower,
|
||||||
}
|
}
|
||||||
|
@ -393,6 +414,25 @@ func (s *MemCachedStore) persist(isSync bool) (int, error) {
|
||||||
return keys, err
|
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
|
// Close implements Store interface, clears up memory and closes the lower layer
|
||||||
// Store.
|
// Store.
|
||||||
func (s *MemCachedStore) Close() error {
|
func (s *MemCachedStore) Close() error {
|
||||||
|
|
|
@ -4,9 +4,11 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
"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/fee"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
@ -798,13 +800,13 @@ func TestInvokeVerify(t *testing.T) {
|
||||||
var h uint32 = 1
|
var h uint32 = 1
|
||||||
_, err = c.InvokeContractVerifyAtHeight(h, contract, smartcontract.Params{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}})
|
_, err = c.InvokeContractVerifyAtHeight(h, contract, smartcontract.Params{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}})
|
||||||
require.Error(t, err)
|
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) {
|
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()}})
|
_, err = c.InvokeContractVerifyAtBlock(chain.GetHeaderHash(1), contract, smartcontract.Params{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}})
|
||||||
require.Error(t, err)
|
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) {
|
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)
|
require.NoError(t, err)
|
||||||
_, err = c.InvokeContractVerifyWithState(sr.Root, contract, smartcontract.Params{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}})
|
_, err = c.InvokeContractVerifyWithState(sr.Root, contract, smartcontract.Params{}, []transaction.Signer{{Account: testchain.PrivateKeyByID(0).PublicKey().GetScriptHash()}})
|
||||||
require.Error(t, err)
|
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) {
|
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{}})
|
err = s.chain.InitVerificationContext(ic, contractScriptHash, &transaction.Witness{InvocationScript: script, VerificationScript: []byte{}})
|
||||||
if err != nil {
|
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 {
|
} else {
|
||||||
ic.VM.LoadScriptWithFlags(script, callflag.All)
|
ic.VM.LoadScriptWithFlags(script, callflag.All)
|
||||||
|
|
Loading…
Reference in a new issue