vm: zero GAS means no GAS, use fee data to properly limit execution

We were accepting transactions with zero system fee, but we shouldn't do
that. Also, transaction's verification execution has to be limited by network
fee.
This commit is contained in:
Roman Khimov 2020-07-13 20:12:13 +03:00
parent a5d6c76928
commit db027ad9c5
10 changed files with 28 additions and 19 deletions

View file

@ -24,7 +24,7 @@ import (
func TestNewService(t *testing.T) {
srv := newTestService(t)
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 100000)
tx.ValidUntilBlock = 1
addSender(t, tx)
signTx(t, srv.Chain.FeePerByte(), tx)
@ -42,7 +42,7 @@ func TestService_GetVerified(t *testing.T) {
srv.dbft.Start()
var txs []*transaction.Transaction
for i := 0; i < 4; i++ {
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 100000)
tx.Nonce = 123 + uint32(i)
tx.ValidUntilBlock = 1
txs = append(txs, tx)

View file

@ -568,6 +568,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
if block.Index > 0 {
systemInterop := bc.newInteropContext(trigger.System, cache, block, nil)
v := SpawnVM(systemInterop)
v.GasLimit = -1
v.LoadScriptWithFlags(bc.contracts.GetPersistScript(), smartcontract.AllowModifyStates|smartcontract.AllowCall)
v.SetPriceGetter(getPrice)
if err := v.Run(); err != nil {
@ -1358,7 +1359,7 @@ func (bc *Blockchain) verifyTxWitnesses(t *transaction.Transaction, block *block
sort.Slice(witnesses, func(i, j int) bool { return witnesses[i].ScriptHash().Less(witnesses[j].ScriptHash()) })
interopCtx := bc.newInteropContext(trigger.Verification, bc.dao, block, t)
for i := 0; i < len(hashes); i++ {
err := bc.verifyHashAgainstScript(hashes[i], &witnesses[i], interopCtx, false, 0)
err := bc.verifyHashAgainstScript(hashes[i], &witnesses[i], interopCtx, false, t.NetworkFee)
if err != nil {
numStr := fmt.Sprintf("witness #%d", i)
return errors.Wrap(err, numStr)

View file

@ -257,7 +257,7 @@ func TestCreateBasicChain(t *testing.T) {
script = io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(script.BinWriter, hash.Hash160(avm), "Put", "testkey", "testvalue")
txInv := transaction.New(testchain.Network(), script.Bytes(), 0)
txInv := transaction.New(testchain.Network(), script.Bytes(), 1*native.GASFactor)
txInv.Nonce = getNextNonce()
txInv.ValidUntilBlock = validUntilBlock
txInv.Sender = priv0ScriptHash
@ -288,7 +288,7 @@ func TestCreateBasicChain(t *testing.T) {
sh := hash.Hash160(avm)
w := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(w.BinWriter, sh, "init")
initTx := transaction.New(testchain.Network(), w.Bytes(), 0)
initTx := transaction.New(testchain.Network(), w.Bytes(), 1*native.GASFactor)
initTx.Nonce = getNextNonce()
initTx.ValidUntilBlock = validUntilBlock
initTx.Sender = priv0ScriptHash
@ -378,7 +378,7 @@ func newNEP5Transfer(sc, from, to util.Uint160, amount int64) *transaction.Trans
emit.Opcode(w.BinWriter, opcode.ASSERT)
script := w.Bytes()
return transaction.New(testchain.Network(), script, 0)
return transaction.New(testchain.Network(), script, 10000000)
}
func addSender(txs ...*transaction.Transaction) error {

View file

@ -60,6 +60,7 @@ func initCHECKMULTISIGVM(t *testing.T, n int, ik, is []int) *vm.VM {
binary.LittleEndian.PutUint32(buf[1:], ecdsaCheckMultisigID)
v := vm.New()
v.GasLimit = -1
ic := &interop.Context{Trigger: trigger.Verification}
v.RegisterInteropGetter(GetInterop(ic))
v.LoadScript(buf)

View file

@ -227,7 +227,7 @@ func invokeNativePolicyMethod(chain *Blockchain, method string, args ...interfac
return nil, w.Err
}
script := w.Bytes()
tx := transaction.New(chain.GetConfig().Magic, script, 0)
tx := transaction.New(chain.GetConfig().Magic, script, 10000000)
validUntil := chain.blockHeight + 1
tx.ValidUntilBlock = validUntil
err := addSender(tx)

View file

@ -52,7 +52,7 @@ type rpcTestCase struct {
}
const testContractHash = "10e262ef80c76bdecca287a2c047841fc02c3129"
const deploymentTxHash = "ad8b149c799d4b2337162b0ad23e0ba8845cddb9cfca8a45587ee207015d2a7c"
const deploymentTxHash = "4843700d16be3e6507a25909d3b2aa1472dcd0526be2520f2bfdd53d768bfebf"
var rpcTestCases = map[string][]rpcTestCase{
"getapplicationlog": {
@ -148,7 +148,7 @@ var rpcTestCases = map[string][]rpcTestCase{
},
{
Asset: e.chain.UtilityTokenHash(),
Amount: "918.01738700",
Amount: "915.79002700",
LastUpdated: 6,
}},
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
@ -720,7 +720,7 @@ var rpcTestCases = map[string][]rpcTestCase{
"sendrawtransaction": {
{
name: "positive",
params: `["000a000000316e851039019d39dfc2c37d6c3fee19fd5809870000000000000000f2ad050000000000b00400000001316e851039019d39dfc2c37d6c3fee19fd580987015d0300e87648170000000c1420728274afafc36f43a071d328cfa3e629d9cbb00c14316e851039019d39dfc2c37d6c3fee19fd58098713c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801420c409803db41e66a94e0bd6fdd3d7a7b1e106c1e2281c9782a231f32df036bb80c7f19155cdb9a52a45cf8d93ec9c1e8df69d6ee35625f352d1710c7cc6eee26003c290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"]`,
params: `["000a000000316e851039019d39dfc2c37d6c3fee19fd58098780969800000000009269130000000000b00400000001316e851039019d39dfc2c37d6c3fee19fd580987015d0300e87648170000000c1420728274afafc36f43a071d328cfa3e629d9cbb00c14316e851039019d39dfc2c37d6c3fee19fd58098713c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801420c4037c8c002c3352329c5f2995567a355d57372447c156ea544844d5f8027babf4cd86142d986681992805e3f62f2625b551f18cf05897e6ea7c135f22537bb740e290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"]`,
result: func(e *executor) interface{} {
v := true
return &v

Binary file not shown.

View file

@ -111,6 +111,7 @@ func TestParameterContext_AddSignatureMultisig(t *testing.T) {
func newTestVM(w *transaction.Witness, tx *transaction.Transaction) *vm.VM {
v := vm.New()
v.GasLimit = -1
v.RegisterInteropGetter(crypto.GetInterop(&interop.Context{Container: tx}))
v.LoadScript(w.VerificationScript)
v.LoadScript(w.InvocationScript)

View file

@ -133,7 +133,7 @@ func (v *VM) GasConsumed() int64 {
// AddGas consumes specified amount of gas. It returns true iff gas limit wasn't exceeded.
func (v *VM) AddGas(gas int64) bool {
v.gasConsumed += gas
return v.GasLimit == 0 || v.gasConsumed <= v.GasLimit
return v.GasLimit < 0 || v.gasConsumed <= v.GasLimit
}
// Estack returns the evaluation stack so interop hooks can utilize this.
@ -523,7 +523,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if v.getPrice != nil && ctx.ip < len(ctx.prog) {
v.gasConsumed += v.getPrice(v, op, parameter)
if v.GasLimit > 0 && v.gasConsumed > v.GasLimit {
if v.GasLimit >= 0 && v.gasConsumed > v.GasLimit {
panic("gas limit is exceeded")
}
}

View file

@ -35,7 +35,7 @@ func fooInteropGetter(id uint32) *InteropFuncPrice {
}
func TestInteropHook(t *testing.T) {
v := New()
v := newTestVM()
v.RegisterInteropGetter(fooInteropGetter)
buf := io.NewBufBinWriter()
@ -48,14 +48,14 @@ func TestInteropHook(t *testing.T) {
}
func TestRegisterInteropGetter(t *testing.T) {
v := New()
v := newTestVM()
currRegistered := len(v.getInterop)
v.RegisterInteropGetter(fooInteropGetter)
assert.Equal(t, currRegistered+1, len(v.getInterop))
}
func TestVM_SetPriceGetter(t *testing.T) {
v := New()
v := newTestVM()
prog := []byte{
byte(opcode.PUSH4), byte(opcode.PUSH2),
byte(opcode.PUSHDATA1), 0x01, 0x01,
@ -103,7 +103,7 @@ func TestVM_SetPriceGetter(t *testing.T) {
}
func TestAddGas(t *testing.T) {
v := New()
v := newTestVM()
v.GasLimit = 10
require.True(t, v.AddGas(5))
require.True(t, v.AddGas(5))
@ -111,7 +111,7 @@ func TestAddGas(t *testing.T) {
}
func TestBytesToPublicKey(t *testing.T) {
v := New()
v := newTestVM()
cache := v.GetPublicKeys()
assert.Equal(t, 0, len(cache))
keyHex := "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"
@ -819,7 +819,7 @@ func TestSerializeInterop(t *testing.T) {
func getTestCallFlagsFunc(syscall []byte, flags smartcontract.CallFlag, result interface{}) func(t *testing.T) {
return func(t *testing.T) {
script := append([]byte{byte(opcode.SYSCALL)}, syscall...)
v := New()
v := newTestVM()
v.RegisterInteropGetter(getTestingInterop)
v.LoadScriptWithFlags(script, flags)
if result == nil {
@ -2518,7 +2518,7 @@ func makeProgram(opcodes ...opcode.Opcode) []byte {
}
func load(prog []byte) *VM {
vm := New()
vm := newTestVM()
if len(prog) != 0 {
vm.LoadScript(prog)
}
@ -2533,3 +2533,9 @@ func randomBytes(n int) []byte {
}
return b
}
func newTestVM() *VM {
v := New()
v.GasLimit = -1
return v
}