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) { func TestNewService(t *testing.T) {
srv := newTestService(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 tx.ValidUntilBlock = 1
addSender(t, tx) addSender(t, tx)
signTx(t, srv.Chain.FeePerByte(), tx) signTx(t, srv.Chain.FeePerByte(), tx)
@ -42,7 +42,7 @@ func TestService_GetVerified(t *testing.T) {
srv.dbft.Start() srv.dbft.Start()
var txs []*transaction.Transaction var txs []*transaction.Transaction
for i := 0; i < 4; i++ { 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.Nonce = 123 + uint32(i)
tx.ValidUntilBlock = 1 tx.ValidUntilBlock = 1
txs = append(txs, tx) txs = append(txs, tx)

View file

@ -568,6 +568,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
if block.Index > 0 { if block.Index > 0 {
systemInterop := bc.newInteropContext(trigger.System, cache, block, nil) systemInterop := bc.newInteropContext(trigger.System, cache, block, nil)
v := SpawnVM(systemInterop) v := SpawnVM(systemInterop)
v.GasLimit = -1
v.LoadScriptWithFlags(bc.contracts.GetPersistScript(), smartcontract.AllowModifyStates|smartcontract.AllowCall) v.LoadScriptWithFlags(bc.contracts.GetPersistScript(), smartcontract.AllowModifyStates|smartcontract.AllowCall)
v.SetPriceGetter(getPrice) v.SetPriceGetter(getPrice)
if err := v.Run(); err != nil { 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()) }) 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) interopCtx := bc.newInteropContext(trigger.Verification, bc.dao, block, t)
for i := 0; i < len(hashes); i++ { 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 { if err != nil {
numStr := fmt.Sprintf("witness #%d", i) numStr := fmt.Sprintf("witness #%d", i)
return errors.Wrap(err, numStr) return errors.Wrap(err, numStr)

View file

@ -257,7 +257,7 @@ func TestCreateBasicChain(t *testing.T) {
script = io.NewBufBinWriter() script = io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(script.BinWriter, hash.Hash160(avm), "Put", "testkey", "testvalue") 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.Nonce = getNextNonce()
txInv.ValidUntilBlock = validUntilBlock txInv.ValidUntilBlock = validUntilBlock
txInv.Sender = priv0ScriptHash txInv.Sender = priv0ScriptHash
@ -288,7 +288,7 @@ func TestCreateBasicChain(t *testing.T) {
sh := hash.Hash160(avm) sh := hash.Hash160(avm)
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(w.BinWriter, sh, "init") 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.Nonce = getNextNonce()
initTx.ValidUntilBlock = validUntilBlock initTx.ValidUntilBlock = validUntilBlock
initTx.Sender = priv0ScriptHash initTx.Sender = priv0ScriptHash
@ -378,7 +378,7 @@ func newNEP5Transfer(sc, from, to util.Uint160, amount int64) *transaction.Trans
emit.Opcode(w.BinWriter, opcode.ASSERT) emit.Opcode(w.BinWriter, opcode.ASSERT)
script := w.Bytes() script := w.Bytes()
return transaction.New(testchain.Network(), script, 0) return transaction.New(testchain.Network(), script, 10000000)
} }
func addSender(txs ...*transaction.Transaction) error { 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) binary.LittleEndian.PutUint32(buf[1:], ecdsaCheckMultisigID)
v := vm.New() v := vm.New()
v.GasLimit = -1
ic := &interop.Context{Trigger: trigger.Verification} ic := &interop.Context{Trigger: trigger.Verification}
v.RegisterInteropGetter(GetInterop(ic)) v.RegisterInteropGetter(GetInterop(ic))
v.LoadScript(buf) v.LoadScript(buf)

View file

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

View file

@ -52,7 +52,7 @@ type rpcTestCase struct {
} }
const testContractHash = "10e262ef80c76bdecca287a2c047841fc02c3129" const testContractHash = "10e262ef80c76bdecca287a2c047841fc02c3129"
const deploymentTxHash = "ad8b149c799d4b2337162b0ad23e0ba8845cddb9cfca8a45587ee207015d2a7c" const deploymentTxHash = "4843700d16be3e6507a25909d3b2aa1472dcd0526be2520f2bfdd53d768bfebf"
var rpcTestCases = map[string][]rpcTestCase{ var rpcTestCases = map[string][]rpcTestCase{
"getapplicationlog": { "getapplicationlog": {
@ -148,7 +148,7 @@ var rpcTestCases = map[string][]rpcTestCase{
}, },
{ {
Asset: e.chain.UtilityTokenHash(), Asset: e.chain.UtilityTokenHash(),
Amount: "918.01738700", Amount: "915.79002700",
LastUpdated: 6, LastUpdated: 6,
}}, }},
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
@ -720,7 +720,7 @@ var rpcTestCases = map[string][]rpcTestCase{
"sendrawtransaction": { "sendrawtransaction": {
{ {
name: "positive", name: "positive",
params: `["000a000000316e851039019d39dfc2c37d6c3fee19fd5809870000000000000000f2ad050000000000b00400000001316e851039019d39dfc2c37d6c3fee19fd580987015d0300e87648170000000c1420728274afafc36f43a071d328cfa3e629d9cbb00c14316e851039019d39dfc2c37d6c3fee19fd58098713c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801420c409803db41e66a94e0bd6fdd3d7a7b1e106c1e2281c9782a231f32df036bb80c7f19155cdb9a52a45cf8d93ec9c1e8df69d6ee35625f352d1710c7cc6eee26003c290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"]`, params: `["000a000000316e851039019d39dfc2c37d6c3fee19fd58098780969800000000009269130000000000b00400000001316e851039019d39dfc2c37d6c3fee19fd580987015d0300e87648170000000c1420728274afafc36f43a071d328cfa3e629d9cbb00c14316e851039019d39dfc2c37d6c3fee19fd58098713c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801420c4037c8c002c3352329c5f2995567a355d57372447c156ea544844d5f8027babf4cd86142d986681992805e3f62f2625b551f18cf05897e6ea7c135f22537bb740e290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"]`,
result: func(e *executor) interface{} { result: func(e *executor) interface{} {
v := true v := true
return &v 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 { func newTestVM(w *transaction.Witness, tx *transaction.Transaction) *vm.VM {
v := vm.New() v := vm.New()
v.GasLimit = -1
v.RegisterInteropGetter(crypto.GetInterop(&interop.Context{Container: tx})) v.RegisterInteropGetter(crypto.GetInterop(&interop.Context{Container: tx}))
v.LoadScript(w.VerificationScript) v.LoadScript(w.VerificationScript)
v.LoadScript(w.InvocationScript) 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. // AddGas consumes specified amount of gas. It returns true iff gas limit wasn't exceeded.
func (v *VM) AddGas(gas int64) bool { func (v *VM) AddGas(gas int64) bool {
v.gasConsumed += gas 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. // 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) { if v.getPrice != nil && ctx.ip < len(ctx.prog) {
v.gasConsumed += v.getPrice(v, op, parameter) 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") panic("gas limit is exceeded")
} }
} }

View file

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