emit: implement AppCallWithOperationAndArgs

It is nice to have a typical task of calling contract method
with specific arguments incapsulated inside some function.
This commit is contained in:
Evgenii Stratonikov 2020-03-27 11:00:06 +03:00
parent d4622768d1
commit db2dccf7cb
4 changed files with 64 additions and 24 deletions

View file

@ -318,12 +318,7 @@ func TestCreateBasicChain(t *testing.T) {
// Now invoke this contract.
script = io.NewBufBinWriter()
emit.String(script.BinWriter, "testvalue")
emit.String(script.BinWriter, "testkey")
emit.Int(script.BinWriter, 2)
emit.Opcode(script.BinWriter, opcode.PACK)
emit.String(script.BinWriter, "Put")
emit.AppCall(script.BinWriter, hash.Hash160(avm), false)
emit.AppCallWithOperationAndArgs(script.BinWriter, hash.Hash160(avm), "Put", "testkey", "testvalue")
txInv := transaction.NewInvocationTX(script.Bytes(), 0)
b = bc.newBlock(newMinerTX(), txInv)
@ -355,10 +350,7 @@ func TestCreateBasicChain(t *testing.T) {
sh := hash.Hash160(avm)
w := io.NewBufBinWriter()
emit.Int(w.BinWriter, 0)
emit.Opcode(w.BinWriter, opcode.NEWARRAY)
emit.String(w.BinWriter, "init")
emit.AppCall(w.BinWriter, sh, true)
emit.AppCallWithOperationAndArgs(w.BinWriter, sh, "init")
initTx := transaction.NewInvocationTX(w.Bytes(), 0)
transferTx := newNEP5Transfer(sh, sh, priv0.GetScriptHash(), 1000)
@ -406,13 +398,7 @@ func TestCreateBasicChain(t *testing.T) {
func newNEP5Transfer(sc, from, to util.Uint160, amount int64) *transaction.Transaction {
w := io.NewBufBinWriter()
emit.Int(w.BinWriter, amount)
emit.Bytes(w.BinWriter, to.BytesBE())
emit.Bytes(w.BinWriter, from.BytesBE())
emit.Int(w.BinWriter, 3)
emit.Opcode(w.BinWriter, opcode.PACK)
emit.String(w.BinWriter, "transfer")
emit.AppCall(w.BinWriter, sc, false)
emit.AppCallWithOperationAndArgs(w.BinWriter, sc, "transfer", from, to, amount)
emit.Opcode(w.BinWriter, opcode.THROWIFNOT)
script := w.Bytes()

View file

@ -104,13 +104,7 @@ func (c *Client) TransferNEP5(acc *wallet.Account, to util.Uint160, token *walle
// Note: we don't use invoke function here because it requires
// 2 round trips instead of one.
w := io.NewBufBinWriter()
emit.Int(w.BinWriter, amount)
emit.Bytes(w.BinWriter, to.BytesBE())
emit.Bytes(w.BinWriter, from.BytesBE())
emit.Int(w.BinWriter, 3)
emit.Opcode(w.BinWriter, opcode.PACK)
emit.String(w.BinWriter, "transfer")
emit.AppCall(w.BinWriter, token.Hash, false)
emit.AppCallWithOperationAndArgs(w.BinWriter, token.Hash, "transfer", from, to, amount)
emit.Opcode(w.BinWriter, opcode.THROWIFNOT)
tx := transaction.NewInvocationTX(w.Bytes(), gas)

View file

@ -48,6 +48,29 @@ func Int(w *io.BinWriter, i int64) {
}
}
// Array emits array of elements to the given buffer.
func Array(w *io.BinWriter, es ...interface{}) {
for i := len(es) - 1; i >= 0; i-- {
switch e := es[i].(type) {
case int64:
Int(w, e)
case string:
String(w, e)
case util.Uint160:
Bytes(w, e.BytesBE())
case []byte:
Bytes(w, e)
case bool:
Bool(w, e)
default:
w.Err = errors.New("unsupported type")
return
}
}
Int(w, int64(len(es)))
Opcode(w, opcode.PACK)
}
// String emits a string to the given buffer.
func String(w *io.BinWriter, s string) {
Bytes(w, []byte(s))
@ -118,6 +141,13 @@ func AppCall(w *io.BinWriter, scriptHash util.Uint160, tailCall bool) {
Instruction(w, op, scriptHash.BytesBE())
}
// AppCallWithOperationAndArgs emits an APPCALL with the given operation and arguments.
func AppCallWithOperationAndArgs(w *io.BinWriter, scriptHash util.Uint160, operation string, args ...interface{}) {
Array(w, args...)
String(w, operation)
AppCall(w, scriptHash, false)
}
// AppCallWithOperationAndData emits an appcall with the given operation and data.
func AppCallWithOperationAndData(w *io.BinWriter, scriptHash util.Uint160, operation string, data []byte) {
Bytes(w, data)

View file

@ -8,6 +8,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestEmitInt(t *testing.T) {
@ -107,6 +108,35 @@ func TestBytes(t *testing.T) {
})
}
func TestEmitArray(t *testing.T) {
t.Run("good", func(t *testing.T) {
buf := io.NewBufBinWriter()
Array(buf.BinWriter, int64(1), "str", true, []byte{0xCA, 0xFE})
require.NoError(t, buf.Err)
res := buf.Bytes()
assert.EqualValues(t, opcode.PUSHBYTES2, res[0])
assert.EqualValues(t, []byte{0xCA, 0xFE}, res[1:3])
assert.EqualValues(t, opcode.PUSHT, res[3])
assert.EqualValues(t, opcode.PUSHBYTES3, res[4])
assert.EqualValues(t, []byte("str"), res[5:8])
assert.EqualValues(t, opcode.PUSH1, res[8])
})
t.Run("empty", func(t *testing.T) {
buf := io.NewBufBinWriter()
Array(buf.BinWriter)
require.NoError(t, buf.Err)
assert.EqualValues(t, []byte{0, byte(opcode.PACK)}, buf.Bytes())
})
t.Run("invalid type", func(t *testing.T) {
buf := io.NewBufBinWriter()
Array(buf.BinWriter, struct{}{})
require.Error(t, buf.Err)
})
}
func TestEmitBool(t *testing.T) {
buf := io.NewBufBinWriter()
Bool(buf.BinWriter, true)