Skip to content

Commit ac60160

Browse files
committed
native: add refuel method to GAS contract
1 parent 0114f9a commit ac60160

File tree

5 files changed

+128
-2
lines changed

5 files changed

+128
-2
lines changed

pkg/compiler/native_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@ func TestNativeHelpersCompile(t *testing.T) {
136136
{"unclaimedGas", []string{u160, "123"}},
137137
{"unregisterCandidate", []string{pub}},
138138
}, nep17TestCases...))
139-
runNativeTestCases(t, cs.GAS.ContractMD, "gas", nep17TestCases)
139+
runNativeTestCases(t, cs.GAS.ContractMD, "gas", append([]nativeTestCase{
140+
{"refuel", []string{u160, "123"}},
141+
}, nep17TestCases...))
140142
runNativeTestCases(t, cs.Oracle.ContractMD, "oracle", []nativeTestCase{
141143
{"getPrice", nil},
142144
{"request", []string{`"url"`, "nil", `"callback"`, "nil", "123"}},

pkg/core/interop_system_test.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,10 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
347347
emit.Opcodes(w.BinWriter, opcode.CALLT, 1, 0, opcode.RET)
348348
callT2Off := w.Len()
349349
emit.Opcodes(w.BinWriter, opcode.CALLT, 0, 0, opcode.RET)
350+
refuelOff := w.Len()
351+
emit.Opcodes(w.BinWriter, opcode.PUSH2, opcode.PACK)
352+
emit.AppCallNoArgs(w.BinWriter, bc.contracts.GAS.Hash, "refuel", callflag.States|callflag.AllowNotify)
353+
emit.Opcodes(w.BinWriter, opcode.DROP)
350354
burnGasOff := w.Len()
351355
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeBurnGas)
352356
emit.Opcodes(w.BinWriter, opcode.RET)
@@ -518,8 +522,18 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
518522
},
519523
ReturnType: smartcontract.VoidType,
520524
},
525+
{
526+
Name: "refuelGas",
527+
Offset: refuelOff,
528+
Parameters: []manifest.Parameter{
529+
manifest.NewParameter("account", smartcontract.Hash160Type),
530+
manifest.NewParameter("gasRefuel", smartcontract.IntegerType),
531+
manifest.NewParameter("gasBurn", smartcontract.IntegerType),
532+
},
533+
ReturnType: smartcontract.VoidType,
534+
},
521535
}
522-
m.Permissions = make([]manifest.Permission, 2)
536+
m.Permissions = make([]manifest.Permission, 3)
523537
m.Permissions[0].Contract.Type = manifest.PermissionHash
524538
m.Permissions[0].Contract.Value = bc.contracts.NEO.Hash
525539
m.Permissions[0].Methods.Add("balanceOf")
@@ -528,6 +542,10 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
528542
m.Permissions[1].Contract.Value = util.Uint160{}
529543
m.Permissions[1].Methods.Add("method")
530544

545+
m.Permissions[2].Contract.Type = manifest.PermissionHash
546+
m.Permissions[2].Contract.Value = bc.contracts.GAS.Hash
547+
m.Permissions[2].Methods.Add("refuel")
548+
531549
cs := &state.Contract{
532550
ContractBase: state.ContractBase{
533551
Hash: h,

pkg/core/native/native_gas.go

+29
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@ package native
22

33
import (
44
"errors"
5+
"fmt"
56
"math/big"
67

78
"github.com/nspcc-dev/neo-go/pkg/core/interop"
9+
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
810
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
911
"github.com/nspcc-dev/neo-go/pkg/core/state"
1012
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
1113
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
14+
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
15+
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
1216
"github.com/nspcc-dev/neo-go/pkg/util"
17+
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
1318
)
1419

1520
// GAS represents GAS native contract.
@@ -38,6 +43,12 @@ func newGAS() *GAS {
3843

3944
g.nep17TokenNative = *nep17
4045

46+
desc := newDescriptor("refuel", smartcontract.VoidType,
47+
manifest.NewParameter("account", smartcontract.Hash160Type),
48+
manifest.NewParameter("amount", smartcontract.IntegerType))
49+
md := newMethodAndPrice(g.refuel, 1<<15, callflag.States|callflag.AllowNotify)
50+
g.AddMethod(md, desc)
51+
4152
return g
4253
}
4354

@@ -68,6 +79,24 @@ func (g *GAS) balanceFromBytes(si *state.StorageItem) (*big.Int, error) {
6879
return &acc.Balance, err
6980
}
7081

82+
func (g *GAS) refuel(ic *interop.Context, args []stackitem.Item) stackitem.Item {
83+
acc := toUint160(args[0])
84+
gas := toBigInt(args[1])
85+
86+
if !gas.IsInt64() || gas.Sign() == -1 {
87+
panic("invalid GAS value")
88+
}
89+
90+
ok, err := runtime.CheckHashedWitness(ic, acc)
91+
if !ok || err != nil {
92+
panic(fmt.Errorf("%w: %v", ErrInvalidWitness, err))
93+
}
94+
95+
g.burn(ic, acc, gas)
96+
ic.VM.GasLimit += gas.Int64()
97+
return stackitem.Null{}
98+
}
99+
71100
// Initialize initializes GAS contract.
72101
func (g *GAS) Initialize(ic *interop.Context) error {
73102
if err := g.nep17TokenNative.Initialize(ic); err != nil {

pkg/core/native_gas_test.go

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package core
2+
3+
import (
4+
"math/big"
5+
"testing"
6+
7+
"github.com/nspcc-dev/neo-go/internal/random"
8+
"github.com/nspcc-dev/neo-go/pkg/vm"
9+
"github.com/nspcc-dev/neo-go/pkg/wallet"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
func TestGAS_Refuel(t *testing.T) {
14+
bc := newTestChain(t)
15+
16+
cs, _ := getTestContractState(bc)
17+
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
18+
19+
const (
20+
sysFee = 10_000000
21+
burnFee = sysFee + 12345678
22+
)
23+
24+
accs := []*wallet.Account{
25+
newAccountWithGAS(t, bc),
26+
newAccountWithGAS(t, bc),
27+
}
28+
29+
t.Run("good, refuel from self", func(t *testing.T) {
30+
before0 := bc.GetUtilityTokenBalance(accs[0].Contract.ScriptHash())
31+
aer, err := invokeContractMethodGeneric(bc, sysFee, bc.contracts.GAS.Hash, "refuel",
32+
accs[0], accs[0].Contract.ScriptHash(), int64(burnFee))
33+
require.NoError(t, err)
34+
require.Equal(t, vm.HaltState, aer.VMState)
35+
36+
after0 := bc.GetUtilityTokenBalance(accs[0].Contract.ScriptHash())
37+
tx, _, _ := bc.GetTransaction(aer.Container)
38+
require.Equal(t, before0, new(big.Int).Add(after0, big.NewInt(tx.SystemFee+tx.NetworkFee+burnFee)))
39+
})
40+
41+
t.Run("good, refuel from other", func(t *testing.T) {
42+
before0 := bc.GetUtilityTokenBalance(accs[0].Contract.ScriptHash())
43+
before1 := bc.GetUtilityTokenBalance(accs[1].Contract.ScriptHash())
44+
aer, err := invokeContractMethodGeneric(bc, sysFee, cs.Hash, "refuelGas",
45+
accs, accs[1].Contract.ScriptHash(), int64(burnFee), int64(burnFee))
46+
require.NoError(t, err)
47+
require.Equal(t, vm.HaltState, aer.VMState)
48+
49+
after0 := bc.GetUtilityTokenBalance(accs[0].Contract.ScriptHash())
50+
after1 := bc.GetUtilityTokenBalance(accs[1].Contract.ScriptHash())
51+
52+
tx, _, _ := bc.GetTransaction(aer.Container)
53+
require.Equal(t, before0, new(big.Int).Add(after0, big.NewInt(tx.SystemFee+tx.NetworkFee)))
54+
require.Equal(t, before1, new(big.Int).Add(after1, big.NewInt(burnFee)))
55+
})
56+
57+
t.Run("bad, invalid witness", func(t *testing.T) {
58+
aer, err := invokeContractMethodGeneric(bc, sysFee, cs.Hash, "refuelGas",
59+
accs, random.Uint160(), int64(1), int64(1))
60+
require.NoError(t, err)
61+
require.Equal(t, vm.FaultState, aer.VMState)
62+
})
63+
64+
t.Run("bad, invalid GAS amount", func(t *testing.T) {
65+
aer, err := invokeContractMethodGeneric(bc, sysFee, cs.Hash, "refuelGas",
66+
accs, accs[0].Contract.ScriptHash(), int64(0), int64(1))
67+
require.NoError(t, err)
68+
require.Equal(t, vm.FaultState, aer.VMState)
69+
})
70+
}

pkg/interop/native/gas/gas.go

+7
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,10 @@ func Transfer(from, to interop.Hash160, amount int, data interface{}) bool {
3737
return contract.Call(interop.Hash160(Hash), "transfer",
3838
contract.All, from, to, amount, data).(bool)
3939
}
40+
41+
// Refuel makes some GAS from the provided account available
42+
// for the current execution. It represents `refuel` method of GAS native contract.
43+
func Refuel(from interop.Hash160, amount int) {
44+
contract.Call(interop.Hash160(Hash), "refuel",
45+
contract.States|contract.AllowNotify, from, amount)
46+
}

0 commit comments

Comments
 (0)