Merge pull request #1624 from nspcc-dev/core/managment_fix

core: managment contract updates
This commit is contained in:
Roman Khimov 2020-12-20 00:21:42 +03:00 committed by GitHub
commit 0ce948332d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 152 additions and 49 deletions

View file

@ -41,7 +41,7 @@ ApplicationConfiguration:
Password: "one" Password: "one"
RPC: RPC:
Address: 127.0.0.1 Address: 127.0.0.1
MaxGasInvoke: 10 MaxGasInvoke: 15
Enabled: true Enabled: true
EnableCORSWorkaround: false EnableCORSWorkaround: false
Port: 0 # let the system choose port dynamically Port: 0 # let the system choose port dynamically

View file

@ -48,7 +48,7 @@ ApplicationConfiguration:
MinPeers: 1 MinPeers: 1
RPC: RPC:
Address: 127.0.0.1 Address: 127.0.0.1
MaxGasInvoke: 10 MaxGasInvoke: 15
Enabled: true Enabled: true
EnableCORSWorkaround: false EnableCORSWorkaround: false
Port: 0 # let the system choose port dynamically Port: 0 # let the system choose port dynamically

View file

@ -37,10 +37,19 @@ const StoragePrice = 100000
const ( const (
prefixContract = 8 prefixContract = 8
defaultMinimumDeploymentFee = 10_00000000
contractDeployNotificationName = "Deploy"
contractUpdateNotificationName = "Update"
contractDestroyNotificationName = "Destroy"
) )
var errGasLimitExceeded = errors.New("gas limit exceeded") var (
var keyNextAvailableID = []byte{15} errGasLimitExceeded = errors.New("gas limit exceeded")
keyNextAvailableID = []byte{15}
keyMinimumDeploymentFee = []byte{20}
)
// makeContractKey creates a key from account script hash. // makeContractKey creates a key from account script hash.
func makeContractKey(h util.Uint160) []byte { func makeContractKey(h util.Uint160) []byte {
@ -62,19 +71,32 @@ func newManagement() *Management {
desc = newDescriptor("deploy", smartcontract.ArrayType, desc = newDescriptor("deploy", smartcontract.ArrayType,
manifest.NewParameter("script", smartcontract.ByteArrayType), manifest.NewParameter("script", smartcontract.ByteArrayType),
manifest.NewParameter("manifest", smartcontract.ByteArrayType)) manifest.NewParameter("manifest", smartcontract.ByteArrayType))
md = newMethodAndPrice(m.deploy, 0, smartcontract.WriteStates) md = newMethodAndPrice(m.deploy, 0, smartcontract.WriteStates|smartcontract.AllowNotify)
m.AddMethod(md, desc) m.AddMethod(md, desc)
desc = newDescriptor("update", smartcontract.VoidType, desc = newDescriptor("update", smartcontract.VoidType,
manifest.NewParameter("script", smartcontract.ByteArrayType), manifest.NewParameter("script", smartcontract.ByteArrayType),
manifest.NewParameter("manifest", smartcontract.ByteArrayType)) manifest.NewParameter("manifest", smartcontract.ByteArrayType))
md = newMethodAndPrice(m.update, 0, smartcontract.WriteStates) md = newMethodAndPrice(m.update, 0, smartcontract.WriteStates|smartcontract.AllowNotify)
m.AddMethod(md, desc) m.AddMethod(md, desc)
desc = newDescriptor("destroy", smartcontract.VoidType) desc = newDescriptor("destroy", smartcontract.VoidType)
md = newMethodAndPrice(m.destroy, 10000000, smartcontract.WriteStates) md = newMethodAndPrice(m.destroy, 1000000, smartcontract.WriteStates|smartcontract.AllowNotify)
m.AddMethod(md, desc) m.AddMethod(md, desc)
desc = newDescriptor("getMinimumDeploymentFee", smartcontract.IntegerType)
md = newMethodAndPrice(m.getMinimumDeploymentFee, 100_0000, smartcontract.ReadStates)
m.AddMethod(md, desc)
desc = newDescriptor("setMinimumDeploymentFee", smartcontract.BoolType,
manifest.NewParameter("value", smartcontract.IntegerType))
md = newMethodAndPrice(m.setMinimumDeploymentFee, 300_0000, smartcontract.WriteStates)
m.AddMethod(md, desc)
hashParam := manifest.NewParameter("Hash", smartcontract.Hash160Type)
m.AddEvent(contractDeployNotificationName, hashParam)
m.AddEvent(contractUpdateNotificationName, hashParam)
m.AddEvent(contractDestroyNotificationName, hashParam)
return m return m
} }
@ -140,7 +162,7 @@ func getLimitedSlice(arg stackitem.Item, max int) ([]byte, error) {
// getNefAndManifestFromItems converts input arguments into NEF and manifest // getNefAndManifestFromItems converts input arguments into NEF and manifest
// adding appropriate deployment GAS price and sanitizing inputs. // adding appropriate deployment GAS price and sanitizing inputs.
func getNefAndManifestFromItems(ic *interop.Context, args []stackitem.Item) (*nef.File, *manifest.Manifest, error) { func (m *Management) getNefAndManifestFromItems(ic *interop.Context, args []stackitem.Item, isDeploy bool) (*nef.File, *manifest.Manifest, error) {
nefBytes, err := getLimitedSlice(args[0], math.MaxInt32) // Upper limits are checked during NEF deserialization. nefBytes, err := getLimitedSlice(args[0], math.MaxInt32) // Upper limits are checked during NEF deserialization.
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("invalid NEF file: %w", err) return nil, nil, fmt.Errorf("invalid NEF file: %w", err)
@ -150,7 +172,14 @@ func getNefAndManifestFromItems(ic *interop.Context, args []stackitem.Item) (*ne
return nil, nil, fmt.Errorf("invalid manifest: %w", err) return nil, nil, fmt.Errorf("invalid manifest: %w", err)
} }
if !ic.VM.AddGas(ic.Chain.GetPolicer().GetStoragePrice() * int64(len(nefBytes)+len(manifestBytes))) { gas := ic.Chain.GetPolicer().GetStoragePrice() * int64(len(nefBytes)+len(manifestBytes))
if isDeploy {
fee := m.GetMinimumDeploymentFee(ic.DAO)
if fee > gas {
gas = fee
}
}
if !ic.VM.AddGas(gas) {
return nil, nil, errGasLimitExceeded return nil, nil, errGasLimitExceeded
} }
var resManifest *manifest.Manifest var resManifest *manifest.Manifest
@ -175,7 +204,7 @@ func getNefAndManifestFromItems(ic *interop.Context, args []stackitem.Item) (*ne
// deploy is an implementation of public deploy method, it's run under // deploy is an implementation of public deploy method, it's run under
// VM protections, so it's OK for it to panic instead of returning errors. // VM protections, so it's OK for it to panic instead of returning errors.
func (m *Management) deploy(ic *interop.Context, args []stackitem.Item) stackitem.Item { func (m *Management) deploy(ic *interop.Context, args []stackitem.Item) stackitem.Item {
neff, manif, err := getNefAndManifestFromItems(ic, args) neff, manif, err := m.getNefAndManifestFromItems(ic, args, true)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -193,6 +222,7 @@ func (m *Management) deploy(ic *interop.Context, args []stackitem.Item) stackite
panic(err) panic(err)
} }
callDeploy(ic, newcontract, false) callDeploy(ic, newcontract, false)
m.emitNotification(ic, contractDeployNotificationName, newcontract.Hash)
return contractToStack(newcontract) return contractToStack(newcontract)
} }
@ -204,7 +234,7 @@ func (m *Management) markUpdated(h util.Uint160) {
} }
// 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.
// It doesn't run _deploy method. // It doesn't run _deploy method and doesn't emit notification.
func (m *Management) Deploy(d dao.DAO, sender util.Uint160, neff *nef.File, manif *manifest.Manifest) (*state.Contract, error) { func (m *Management) Deploy(d dao.DAO, sender util.Uint160, neff *nef.File, manif *manifest.Manifest) (*state.Contract, error) {
h := state.CreateContractHash(sender, neff.Script) h := state.CreateContractHash(sender, neff.Script)
key := makeContractKey(h) key := makeContractKey(h)
@ -236,7 +266,7 @@ func (m *Management) Deploy(d dao.DAO, sender util.Uint160, neff *nef.File, mani
// update is an implementation of public update method, it's run under // update is an implementation of public update method, it's run under
// VM protections, so it's OK for it to panic instead of returning errors. // VM protections, so it's OK for it to panic instead of returning errors.
func (m *Management) update(ic *interop.Context, args []stackitem.Item) stackitem.Item { func (m *Management) update(ic *interop.Context, args []stackitem.Item) stackitem.Item {
neff, manif, err := getNefAndManifestFromItems(ic, args) neff, manif, err := m.getNefAndManifestFromItems(ic, args, false)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -248,11 +278,12 @@ func (m *Management) update(ic *interop.Context, args []stackitem.Item) stackite
panic(err) panic(err)
} }
callDeploy(ic, contract, true) callDeploy(ic, contract, true)
m.emitNotification(ic, contractUpdateNotificationName, contract.Hash)
return stackitem.Null{} return stackitem.Null{}
} }
// Update updates contract's script and/or manifest in the given DAO. // Update updates contract's script and/or manifest in the given DAO.
// It doesn't run _deploy method. // It doesn't run _deploy method and doesn't emit notification.
func (m *Management) Update(d dao.DAO, hash util.Uint160, neff *nef.File, manif *manifest.Manifest) (*state.Contract, error) { func (m *Management) Update(d dao.DAO, hash util.Uint160, neff *nef.File, manif *manifest.Manifest) (*state.Contract, error) {
contract, err := m.GetContract(d, hash) contract, err := m.GetContract(d, hash)
if err != nil { if err != nil {
@ -287,10 +318,11 @@ func (m *Management) destroy(ic *interop.Context, sis []stackitem.Item) stackite
if err != nil { if err != nil {
panic(err) panic(err)
} }
m.emitNotification(ic, contractDestroyNotificationName, hash)
return stackitem.Null{} return stackitem.Null{}
} }
// Destroy drops given contract from DAO along with its storage. // Destroy drops given contract from DAO along with its storage. It doesn't emit notification.
func (m *Management) Destroy(d dao.DAO, hash util.Uint160) error { func (m *Management) Destroy(d dao.DAO, hash util.Uint160) error {
contract, err := m.GetContract(d, hash) contract, err := m.GetContract(d, hash)
if err != nil { if err != nil {
@ -319,6 +351,34 @@ func (m *Management) Destroy(d dao.DAO, hash util.Uint160) error {
return nil return nil
} }
func (m *Management) getMinimumDeploymentFee(ic *interop.Context, args []stackitem.Item) stackitem.Item {
return stackitem.NewBigInteger(big.NewInt(m.GetMinimumDeploymentFee(ic.DAO)))
}
// GetMinimumDeploymentFee returns the minimum required fee for contract deploy.
func (m *Management) GetMinimumDeploymentFee(dao dao.DAO) int64 {
return getInt64WithKey(m.ContractID, dao, keyMinimumDeploymentFee, defaultMinimumDeploymentFee)
}
func (m *Management) setMinimumDeploymentFee(ic *interop.Context, args []stackitem.Item) stackitem.Item {
value := toBigInt(args[0]).Int64()
if value < 0 {
panic(fmt.Errorf("MinimumDeploymentFee cannot be negative"))
}
ok, err := checkValidators(ic)
if err != nil {
panic(err)
}
if !ok {
return stackitem.NewBool(false)
}
err = setInt64WithKey(m.ContractID, ic.DAO, keyMinimumDeploymentFee, value)
if err != nil {
panic(err)
}
return stackitem.NewBool(true)
}
func callDeploy(ic *interop.Context, cs *state.Contract, isUpdate bool) { func callDeploy(ic *interop.Context, cs *state.Contract, isUpdate bool) {
md := cs.Manifest.ABI.GetMethod(manifest.MethodDeploy) md := cs.Manifest.ABI.GetMethod(manifest.MethodDeploy)
if md != nil { if md != nil {
@ -422,8 +482,8 @@ func (m *Management) PostPersist(ic *interop.Context) error {
} }
// Initialize implements Contract interface. // Initialize implements Contract interface.
func (m *Management) Initialize(_ *interop.Context) error { func (m *Management) Initialize(ic *interop.Context) error {
return nil return setInt64WithKey(m.ContractID, ic.DAO, keyMinimumDeploymentFee, defaultMinimumDeploymentFee)
} }
// PutContractState saves given contract state into given DAO. // PutContractState saves given contract state into given DAO.
@ -453,3 +513,12 @@ func (m *Management) getNextContractID(d dao.DAO) (int32, error) {
si.Value = bigint.ToPreallocatedBytes(id, si.Value) si.Value = bigint.ToPreallocatedBytes(id, si.Value)
return ret, d.PutStorageItem(m.ContractID, keyNextAvailableID, si) return ret, d.PutStorageItem(m.ContractID, keyNextAvailableID, si)
} }
func (m *Management) emitNotification(ic *interop.Context, name string, hash util.Uint160) {
ne := state.NotificationEvent{
ScriptHash: m.Hash,
Name: name,
Item: stackitem.NewArray([]stackitem.Item{addrToStackItem(&hash)}),
}
ic.Notifications = append(ic.Notifications, ne)
}

View file

@ -89,42 +89,42 @@ func TestContractDeploy(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
t.Run("no NEF", func(t *testing.T) { t.Run("no NEF", func(t *testing.T) {
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nil, manif1) res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nil, manif1)
require.NoError(t, err) require.NoError(t, err)
checkFAULTState(t, res) checkFAULTState(t, res)
}) })
t.Run("no manifest", func(t *testing.T) { t.Run("no manifest", func(t *testing.T) {
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nef1b, nil) res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, nil)
require.NoError(t, err) require.NoError(t, err)
checkFAULTState(t, res) checkFAULTState(t, res)
}) })
t.Run("int for NEF", func(t *testing.T) { t.Run("int for NEF", func(t *testing.T) {
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", int64(1), manif1) res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", int64(1), manif1)
require.NoError(t, err) require.NoError(t, err)
checkFAULTState(t, res) checkFAULTState(t, res)
}) })
t.Run("zero-length NEF", func(t *testing.T) { t.Run("zero-length NEF", func(t *testing.T) {
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", []byte{}, manif1) res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", []byte{}, manif1)
require.NoError(t, err) require.NoError(t, err)
checkFAULTState(t, res) checkFAULTState(t, res)
}) })
t.Run("array for NEF", func(t *testing.T) { t.Run("array for NEF", func(t *testing.T) {
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", []interface{}{int64(1)}, manif1) res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", []interface{}{int64(1)}, manif1)
require.NoError(t, err) require.NoError(t, err)
checkFAULTState(t, res) checkFAULTState(t, res)
}) })
t.Run("int for manifest", func(t *testing.T) { t.Run("int for manifest", func(t *testing.T) {
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nef1b, int64(1)) res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, int64(1))
require.NoError(t, err) require.NoError(t, err)
checkFAULTState(t, res) checkFAULTState(t, res)
}) })
t.Run("zero-length manifest", func(t *testing.T) { t.Run("zero-length manifest", func(t *testing.T) {
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nef1b, []byte{}) res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, []byte{})
require.NoError(t, err) require.NoError(t, err)
checkFAULTState(t, res) checkFAULTState(t, res)
}) })
t.Run("array for manifest", func(t *testing.T) { t.Run("array for manifest", func(t *testing.T) {
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nef1b, []interface{}{int64(1)}) res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, []interface{}{int64(1)})
require.NoError(t, err) require.NoError(t, err)
checkFAULTState(t, res) checkFAULTState(t, res)
}) })
@ -137,7 +137,7 @@ func TestContractDeploy(t *testing.T) {
manifB, err := json.Marshal(badManifest) manifB, err := json.Marshal(badManifest)
require.NoError(t, err) require.NoError(t, err)
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nef1b, manifB) res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, manifB)
require.NoError(t, err) require.NoError(t, err)
checkFAULTState(t, res) checkFAULTState(t, res)
}) })
@ -147,7 +147,7 @@ func TestContractDeploy(t *testing.T) {
checkFAULTState(t, res) checkFAULTState(t, res)
}) })
t.Run("positive", func(t *testing.T) { t.Run("positive", func(t *testing.T) {
tx1, err := prepareContractMethodInvoke(bc, 10_00000000, mgmtHash, "deploy", nef1b, manif1) tx1, err := prepareContractMethodInvoke(bc, 11_00000000, mgmtHash, "deploy", nef1b, manif1)
require.NoError(t, err) require.NoError(t, err)
tx2, err := prepareContractMethodInvoke(bc, 1_00000000, mgmtHash, "getContract", cs1.Hash.BytesBE()) tx2, err := prepareContractMethodInvoke(bc, 1_00000000, mgmtHash, "getContract", cs1.Hash.BytesBE())
require.NoError(t, err) require.NoError(t, err)
@ -159,7 +159,11 @@ func TestContractDeploy(t *testing.T) {
require.Equal(t, 1, len(res.Stack)) require.Equal(t, 1, len(res.Stack))
compareContractStates(t, cs1, res.Stack[0]) compareContractStates(t, cs1, res.Stack[0])
} }
require.Equal(t, aers[0].Events, []state.NotificationEvent{{
ScriptHash: mgmtHash,
Name: "Deploy",
Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}),
}})
t.Run("_deploy called", func(t *testing.T) { t.Run("_deploy called", func(t *testing.T) {
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue") res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
require.NoError(t, err) require.NoError(t, err)
@ -184,7 +188,7 @@ func TestContractDeploy(t *testing.T) {
}) })
}) })
t.Run("contract already exists", func(t *testing.T) { t.Run("contract already exists", func(t *testing.T) {
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nef1b, manif1) res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, manif1)
require.NoError(t, err) require.NoError(t, err)
checkFAULTState(t, res) checkFAULTState(t, res)
}) })
@ -207,7 +211,7 @@ func TestContractDeploy(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
manifD, err := json.Marshal(m) manifD, err := json.Marshal(m)
require.NoError(t, err) require.NoError(t, err)
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nefDb, manifD) res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nefDb, manifD)
require.NoError(t, err) require.NoError(t, err)
checkFAULTState(t, res) checkFAULTState(t, res)
@ -236,7 +240,7 @@ func TestContractDeploy(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
manifD, err := json.Marshal(m) manifD, err := json.Marshal(m)
require.NoError(t, err) require.NoError(t, err)
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nefDb, manifD) res, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nefDb, manifD)
require.NoError(t, err) require.NoError(t, err)
checkFAULTState(t, res) checkFAULTState(t, res)
@ -337,7 +341,11 @@ func TestContractUpdate(t *testing.T) {
require.Equal(t, vm.HaltState, aers[1].VMState) require.Equal(t, vm.HaltState, aers[1].VMState)
require.Equal(t, 1, len(aers[1].Stack)) require.Equal(t, 1, len(aers[1].Stack))
compareContractStates(t, cs1, aers[1].Stack[0]) compareContractStates(t, cs1, aers[1].Stack[0])
require.Equal(t, aers[0].Events, []state.NotificationEvent{{
ScriptHash: mgmtHash,
Name: "Update",
Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}),
}})
t.Run("_deploy called", func(t *testing.T) { t.Run("_deploy called", func(t *testing.T) {
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue") res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
require.NoError(t, err) require.NoError(t, err)
@ -358,6 +366,11 @@ func TestContractUpdate(t *testing.T) {
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nil, manif1) res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nil, manif1)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, vm.HaltState, res.VMState) require.Equal(t, vm.HaltState, res.VMState)
require.Equal(t, res.Events, []state.NotificationEvent{{
ScriptHash: mgmtHash,
Name: "Update",
Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}),
}})
t.Run("check contract", func(t *testing.T) { t.Run("check contract", func(t *testing.T) {
checkContractState(t, bc, cs1.Hash, cs1) checkContractState(t, bc, cs1.Hash, cs1)
}) })
@ -377,6 +390,11 @@ func TestContractUpdate(t *testing.T) {
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nef1b, manif1) res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nef1b, manif1)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, vm.HaltState, res.VMState) require.Equal(t, vm.HaltState, res.VMState)
require.Equal(t, res.Events, []state.NotificationEvent{{
ScriptHash: mgmtHash,
Name: "Update",
Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}),
}})
t.Run("check contract", func(t *testing.T) { t.Run("check contract", func(t *testing.T) {
checkContractState(t, bc, cs1.Hash, cs1) checkContractState(t, bc, cs1.Hash, cs1)
}) })
@ -432,6 +450,11 @@ func TestContractDestroy(t *testing.T) {
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "destroy") res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "destroy")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, vm.HaltState, res.VMState) require.Equal(t, vm.HaltState, res.VMState)
require.Equal(t, res.Events, []state.NotificationEvent{{
ScriptHash: mgmtHash,
Name: "Destroy",
Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}),
}})
t.Run("check contract", func(t *testing.T) { t.Run("check contract", func(t *testing.T) {
res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "getContract", cs1.Hash.BytesBE()) res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "getContract", cs1.Hash.BytesBE())
require.NoError(t, err) require.NoError(t, err)
@ -455,3 +478,15 @@ func compareContractStates(t *testing.T, expected *state.Contract, actual stacki
require.Equal(t, expected.Script, act[3].Value().([]byte)) require.Equal(t, expected.Script, act[3].Value().([]byte))
require.Equal(t, expectedManifest, act[4].Value().([]byte)) require.Equal(t, expectedManifest, act[4].Value().([]byte))
} }
func TestMinimumDeploymentFee(t *testing.T) {
chain := newTestChain(t)
defer chain.Close()
t.Run("get, internal method", func(t *testing.T) {
n := chain.contracts.Management.GetMinimumDeploymentFee(chain.dao)
require.Equal(t, 10_00000000, int(n))
})
testGetSet(t, chain, chain.contracts.Management.Hash, "MinimumDeploymentFee", 10_00000000, 0, 0)
}

View file

@ -14,34 +14,33 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func testPolicyGetSet(t *testing.T, chain *Blockchain, name string, defaultValue, minValue, maxValue int64) { func testGetSet(t *testing.T, chain *Blockchain, hash util.Uint160, name string, defaultValue, minValue, maxValue int64) {
policyHash := chain.contracts.Policy.Metadata().Hash
getName := "get" + name getName := "get" + name
setName := "set" + name setName := "set" + name
t.Run("set, not signed by committee", func(t *testing.T) { t.Run("set, not signed by committee", func(t *testing.T) {
signer, err := wallet.NewAccount() signer, err := wallet.NewAccount()
require.NoError(t, err) require.NoError(t, err)
invokeRes, err := invokeContractMethodBy(t, chain, signer, policyHash, setName, minValue+1) invokeRes, err := invokeContractMethodBy(t, chain, signer, hash, setName, minValue+1)
checkResult(t, invokeRes, stackitem.NewBool(false)) checkResult(t, invokeRes, stackitem.NewBool(false))
}) })
t.Run("get", func(t *testing.T) { t.Run("get, defult value", func(t *testing.T) {
res, err := invokeContractMethod(chain, 100000000, policyHash, getName) res, err := invokeContractMethod(chain, 100000000, hash, getName)
require.NoError(t, err) require.NoError(t, err)
checkResult(t, res, stackitem.Make(defaultValue)) checkResult(t, res, stackitem.Make(defaultValue))
require.NoError(t, chain.persist()) require.NoError(t, chain.persist())
}) })
t.Run("set, zero fee", func(t *testing.T) { t.Run("set, too small value", func(t *testing.T) {
res, err := invokeContractMethod(chain, 100000000, policyHash, setName, minValue-1) res, err := invokeContractMethod(chain, 100000000, hash, setName, minValue-1)
require.NoError(t, err) require.NoError(t, err)
checkFAULTState(t, res) checkFAULTState(t, res)
}) })
if maxValue != 0 { if maxValue != 0 {
t.Run("set, too big fee", func(t *testing.T) { t.Run("set, too large value", func(t *testing.T) {
res, err := invokeContractMethod(chain, 100000000, policyHash, setName, maxValue+1) res, err := invokeContractMethod(chain, 100000000, hash, setName, maxValue+1)
require.NoError(t, err) require.NoError(t, err)
checkFAULTState(t, res) checkFAULTState(t, res)
}) })
@ -49,9 +48,9 @@ func testPolicyGetSet(t *testing.T, chain *Blockchain, name string, defaultValue
t.Run("set, success", func(t *testing.T) { t.Run("set, success", func(t *testing.T) {
// Set and get in the same block. // Set and get in the same block.
txSet, err := prepareContractMethodInvoke(chain, 100000000, policyHash, setName, defaultValue+1) txSet, err := prepareContractMethodInvoke(chain, 100000000, hash, setName, defaultValue+1)
require.NoError(t, err) require.NoError(t, err)
txGet1, err := prepareContractMethodInvoke(chain, 100000000, policyHash, getName) txGet1, err := prepareContractMethodInvoke(chain, 100000000, hash, getName)
require.NoError(t, err) require.NoError(t, err)
aers, err := persistBlock(chain, txSet, txGet1) aers, err := persistBlock(chain, txSet, txGet1)
require.NoError(t, err) require.NoError(t, err)
@ -60,7 +59,7 @@ func testPolicyGetSet(t *testing.T, chain *Blockchain, name string, defaultValue
require.NoError(t, chain.persist()) require.NoError(t, chain.persist())
// Get in the next block. // Get in the next block.
res, err := invokeContractMethod(chain, 100000000, policyHash, getName) res, err := invokeContractMethod(chain, 100000000, hash, getName)
require.NoError(t, err) require.NoError(t, err)
checkResult(t, res, stackitem.Make(defaultValue+1)) checkResult(t, res, stackitem.Make(defaultValue+1))
require.NoError(t, chain.persist()) require.NoError(t, chain.persist())
@ -76,7 +75,7 @@ func TestMaxTransactionsPerBlock(t *testing.T) {
require.Equal(t, 512, int(n)) require.Equal(t, 512, int(n))
}) })
testPolicyGetSet(t, chain, "MaxTransactionsPerBlock", 512, 0, block.MaxTransactionsPerBlock) testGetSet(t, chain, chain.contracts.Policy.Hash, "MaxTransactionsPerBlock", 512, 0, block.MaxTransactionsPerBlock)
} }
func TestMaxBlockSize(t *testing.T) { func TestMaxBlockSize(t *testing.T) {
@ -88,7 +87,7 @@ func TestMaxBlockSize(t *testing.T) {
require.Equal(t, 1024*256, int(n)) require.Equal(t, 1024*256, int(n))
}) })
testPolicyGetSet(t, chain, "MaxBlockSize", 1024*256, 0, payload.MaxSize) testGetSet(t, chain, chain.contracts.Policy.Hash, "MaxBlockSize", 1024*256, 0, payload.MaxSize)
} }
func TestFeePerByte(t *testing.T) { func TestFeePerByte(t *testing.T) {
@ -100,7 +99,7 @@ func TestFeePerByte(t *testing.T) {
require.Equal(t, 1000, int(n)) require.Equal(t, 1000, int(n))
}) })
testPolicyGetSet(t, chain, "FeePerByte", 1000, 0, 100_000_000) testGetSet(t, chain, chain.contracts.Policy.Hash, "FeePerByte", 1000, 0, 100_000_000)
} }
func TestExecFeeFactor(t *testing.T) { func TestExecFeeFactor(t *testing.T) {
@ -112,7 +111,7 @@ func TestExecFeeFactor(t *testing.T) {
require.EqualValues(t, interop.DefaultBaseExecFee, n) require.EqualValues(t, interop.DefaultBaseExecFee, n)
}) })
testPolicyGetSet(t, chain, "ExecFeeFactor", interop.DefaultBaseExecFee, 1, 1000) testGetSet(t, chain, chain.contracts.Policy.Hash, "ExecFeeFactor", interop.DefaultBaseExecFee, 1, 1000)
} }
func TestBlockSystemFee(t *testing.T) { func TestBlockSystemFee(t *testing.T) {
@ -124,7 +123,7 @@ func TestBlockSystemFee(t *testing.T) {
require.Equal(t, 9000*native.GASFactor, int(n)) require.Equal(t, 9000*native.GASFactor, int(n))
}) })
testPolicyGetSet(t, chain, "MaxBlockSystemFee", 9000*native.GASFactor, 4007600, 0) testGetSet(t, chain, chain.contracts.Policy.Hash, "MaxBlockSystemFee", 9000*native.GASFactor, 4007600, 0)
} }
func TestStoragePrice(t *testing.T) { func TestStoragePrice(t *testing.T) {
@ -136,7 +135,7 @@ func TestStoragePrice(t *testing.T) {
require.Equal(t, int64(native.StoragePrice), n) require.Equal(t, int64(native.StoragePrice), n)
}) })
testPolicyGetSet(t, chain, "StoragePrice", native.StoragePrice, 1, 10000000) testGetSet(t, chain, chain.contracts.Policy.Hash, "StoragePrice", native.StoragePrice, 1, 10000000)
} }
func TestBlockedAccounts(t *testing.T) { func TestBlockedAccounts(t *testing.T) {