neoneo-go/pkg/core/interop_neo_test.go
Roman Khimov a9abd3d841 core: fix wrong endian used in interop functions
C# uses ToArray() or UintXXX(bytes) here which interprets hashes as they
should be interpreted (BE, although they always convert to LE when converting
to String just for the fun of it). It leads to state difference for us at
block 2025204 where even though we have the same value for the key, the key
itself differs, ours:

dd2b538e2a0c1db1ae5061c15be14f916bd1e678e512ffcda6d9499d8e7fe97ee71fd6b8004583d9afe09cc4dadbd5deb63d01e061009b7cffdaa674beae0f930ebe6085af900093e5fe56b34a5c220ccdcf6efc336fc5000000000000000000000000000000000010

theirs:

dd2b538e2a0c1db1ae5061c15be14f916bd1e67861e0013db6ded5dbdac49ce0afd9834500b8d61fe77ee97f8e9d49d9a6cdff12e5009b7cffdaa674beae0f930ebe6085af900093e5fe56b34a5c220ccdcf6efc336fc5000000000000000000000000000000000010

In this key there is a tx hash encoded
(e512ffcda6d9499d8e7fe97ee71fd6b84583d9afe09cc4dadbd5deb63d01e061 in LE used
by all the tools like neoscan).

I love Neo.
2020-03-05 19:44:09 +03:00

562 lines
16 KiB
Go

package core
import (
"math/big"
"testing"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/internal/random"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/stretchr/testify/require"
)
/* Missing tests:
* TestTxGetReferences
* TestTxGetUnspentCoins
* TestTxGetWitnesses
* TestBcGetAccount
* TestBcGetAsset
* TestAccountGetBalance
* TestAccountIsStandard
* TestCreateContractStateFromVM
* TestContractCreate
* TestContractMigrate
* TestAssetCreate
* TestAssetRenew
* TestRuntimeSerialize
* TestRuntimeDeserialize
*/
func TestGetTrigger(t *testing.T) {
v, _, context, chain := createVMAndPushBlock(t)
defer chain.Close()
require.NoError(t, context.runtimeGetTrigger(v))
}
func TestStorageFind(t *testing.T) {
v, contractState, context, chain := createVMAndContractState(t)
defer chain.Close()
skeys := [][]byte{{0x01, 0x02}, {0x02, 0x01}}
items := []*state.StorageItem{
{
Value: []byte{0x01, 0x02, 0x03, 0x04},
},
{
Value: []byte{0x04, 0x03, 0x02, 0x01},
},
}
require.NoError(t, context.dao.PutContractState(contractState))
scriptHash := contractState.ScriptHash()
for i := range skeys {
err := context.dao.PutStorageItem(scriptHash, skeys[i], items[i])
require.NoError(t, err)
}
t.Run("normal invocation", func(t *testing.T) {
v.Estack().PushVal([]byte{0x01})
v.Estack().PushVal(vm.NewInteropItem(&StorageContext{ScriptHash: scriptHash}))
err := context.storageFind(v)
require.NoError(t, err)
var iter *vm.InteropItem
require.NotPanics(t, func() { iter = v.Estack().Top().Interop() })
require.NoError(t, context.enumeratorNext(v))
require.True(t, v.Estack().Pop().Bool())
v.Estack().PushVal(iter)
require.NoError(t, context.iteratorKey(v))
require.Equal(t, []byte{0x01, 0x02}, v.Estack().Pop().Bytes())
v.Estack().PushVal(iter)
require.NoError(t, context.enumeratorValue(v))
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, v.Estack().Pop().Bytes())
v.Estack().PushVal(iter)
require.NoError(t, context.enumeratorNext(v))
require.False(t, v.Estack().Pop().Bool())
})
t.Run("invalid type for StorageContext", func(t *testing.T) {
v.Estack().PushVal([]byte{0x01})
v.Estack().PushVal(vm.NewInteropItem(nil))
require.Error(t, context.storageFind(v))
})
t.Run("invalid script hash", func(t *testing.T) {
invalidHash := scriptHash
invalidHash[0] = ^invalidHash[0]
v.Estack().PushVal([]byte{0x01})
v.Estack().PushVal(vm.NewInteropItem(&StorageContext{ScriptHash: invalidHash}))
require.Error(t, context.storageFind(v))
})
}
func TestHeaderGetVersion(t *testing.T) {
v, block, context, chain := createVMAndPushBlock(t)
defer chain.Close()
err := context.headerGetVersion(v)
require.NoError(t, err)
value := v.Estack().Pop().Value().(*big.Int)
require.Equal(t, uint64(block.Version), value.Uint64())
}
func TestHeaderGetVersion_Negative(t *testing.T) {
v := vm.New()
block := newDumbBlock()
chain := newTestChain(t)
defer chain.Close()
context := chain.newInteropContext(trigger.Application, storage.NewMemoryStore(), block, nil)
v.Estack().PushVal(vm.NewBoolItem(false))
err := context.headerGetVersion(v)
require.Errorf(t, err, "value is not a header or block")
}
func TestHeaderGetConsensusData(t *testing.T) {
v, block, context, chain := createVMAndPushBlock(t)
defer chain.Close()
err := context.headerGetConsensusData(v)
require.NoError(t, err)
value := v.Estack().Pop().Value().(*big.Int)
require.Equal(t, block.ConsensusData, value.Uint64())
}
func TestHeaderGetMerkleRoot(t *testing.T) {
v, block, context, chain := createVMAndPushBlock(t)
defer chain.Close()
err := context.headerGetMerkleRoot(v)
require.NoError(t, err)
value := v.Estack().Pop().Value()
require.Equal(t, block.MerkleRoot.BytesBE(), value)
}
func TestHeaderGetNextConsensus(t *testing.T) {
v, block, context, chain := createVMAndPushBlock(t)
defer chain.Close()
err := context.headerGetNextConsensus(v)
require.NoError(t, err)
value := v.Estack().Pop().Value()
require.Equal(t, block.NextConsensus.BytesBE(), value)
}
func TestTxGetAttributes(t *testing.T) {
v, tx, context, chain := createVMAndPushTX(t)
defer chain.Close()
err := context.txGetAttributes(v)
require.NoError(t, err)
value := v.Estack().Pop().Value().([]vm.StackItem)
require.Equal(t, tx.Attributes[0].Usage, value[0].Value().(*transaction.Attribute).Usage)
}
func TestTxGetInputs(t *testing.T) {
v, tx, context, chain := createVMAndPushTX(t)
defer chain.Close()
err := context.txGetInputs(v)
require.NoError(t, err)
value := v.Estack().Pop().Value().([]vm.StackItem)
require.Equal(t, tx.Inputs[0], *value[0].Value().(*transaction.Input))
}
func TestTxGetOutputs(t *testing.T) {
v, tx, context, chain := createVMAndPushTX(t)
defer chain.Close()
err := context.txGetOutputs(v)
require.NoError(t, err)
value := v.Estack().Pop().Value().([]vm.StackItem)
require.Equal(t, tx.Outputs[0], *value[0].Value().(*transaction.Output))
}
func TestTxGetType(t *testing.T) {
v, tx, context, chain := createVMAndPushTX(t)
defer chain.Close()
err := context.txGetType(v)
require.NoError(t, err)
value := v.Estack().Pop().Value().(*big.Int)
require.Equal(t, big.NewInt(int64(tx.Type)), value)
}
func TestInvocationTxGetScript(t *testing.T) {
v, tx, context, chain := createVMAndPushTX(t)
defer chain.Close()
err := context.invocationTxGetScript(v)
require.NoError(t, err)
value := v.Estack().Pop().Value().([]byte)
inv := tx.Data.(*transaction.InvocationTX)
require.Equal(t, inv.Script, value)
}
func TestWitnessGetVerificationScript(t *testing.T) {
v := vm.New()
script := []byte{byte(opcode.PUSHM1), byte(opcode.RET)}
witness := transaction.Witness{InvocationScript: nil, VerificationScript: script}
chain := newTestChain(t)
defer chain.Close()
context := chain.newInteropContext(trigger.Application, storage.NewMemoryStore(), nil, nil)
v.Estack().PushVal(vm.NewInteropItem(&witness))
err := context.witnessGetVerificationScript(v)
require.NoError(t, err)
value := v.Estack().Pop().Value().([]byte)
require.Equal(t, witness.VerificationScript, value)
}
func TestPopInputFromVM(t *testing.T) {
v, tx, _, chain := createVMAndTX(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(&tx.Inputs[0]))
input, err := popInputFromVM(v)
require.NoError(t, err)
require.Equal(t, tx.Inputs[0], *input)
}
func TestInputGetHash(t *testing.T) {
v, tx, context, chain := createVMAndTX(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(&tx.Inputs[0]))
err := context.inputGetHash(v)
require.NoError(t, err)
hash := v.Estack().Pop().Value()
require.Equal(t, tx.Inputs[0].PrevHash.BytesBE(), hash)
}
func TestInputGetIndex(t *testing.T) {
v, tx, context, chain := createVMAndTX(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(&tx.Inputs[0]))
err := context.inputGetIndex(v)
require.NoError(t, err)
index := v.Estack().Pop().Value()
require.Equal(t, big.NewInt(int64(tx.Inputs[0].PrevIndex)), index)
}
func TestPopOutputFromVM(t *testing.T) {
v, tx, _, chain := createVMAndTX(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
output, err := popOutputFromVM(v)
require.NoError(t, err)
require.Equal(t, tx.Outputs[0], *output)
}
func TestOutputGetAssetID(t *testing.T) {
v, tx, context, chain := createVMAndTX(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
err := context.outputGetAssetID(v)
require.NoError(t, err)
assetID := v.Estack().Pop().Value()
require.Equal(t, tx.Outputs[0].AssetID.BytesBE(), assetID)
}
func TestOutputGetScriptHash(t *testing.T) {
v, tx, context, chain := createVMAndTX(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
err := context.outputGetScriptHash(v)
require.NoError(t, err)
scriptHash := v.Estack().Pop().Value()
require.Equal(t, tx.Outputs[0].ScriptHash.BytesBE(), scriptHash)
}
func TestOutputGetValue(t *testing.T) {
v, tx, context, chain := createVMAndTX(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
err := context.outputGetValue(v)
require.NoError(t, err)
amount := v.Estack().Pop().Value()
require.Equal(t, big.NewInt(int64(tx.Outputs[0].Amount)), amount)
}
func TestAttrGetData(t *testing.T) {
v, tx, context, chain := createVMAndTX(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(&tx.Attributes[0]))
err := context.attrGetData(v)
require.NoError(t, err)
data := v.Estack().Pop().Value()
require.Equal(t, tx.Attributes[0].Data, data)
}
func TestAttrGetUsage(t *testing.T) {
v, tx, context, chain := createVMAndTX(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(&tx.Attributes[0]))
err := context.attrGetUsage(v)
require.NoError(t, err)
usage := v.Estack().Pop().Value()
require.Equal(t, big.NewInt(int64(tx.Attributes[0].Usage)), usage)
}
func TestAccountGetScriptHash(t *testing.T) {
v, accState, context, chain := createVMAndAccState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(accState))
err := context.accountGetScriptHash(v)
require.NoError(t, err)
hash := v.Estack().Pop().Value()
require.Equal(t, accState.ScriptHash.BytesBE(), hash)
}
func TestAccountGetVotes(t *testing.T) {
v, accState, context, chain := createVMAndAccState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(accState))
err := context.accountGetVotes(v)
require.NoError(t, err)
votes := v.Estack().Pop().Value().([]vm.StackItem)
require.Equal(t, vm.NewByteArrayItem(accState.Votes[0].Bytes()), votes[0])
}
func TestContractGetScript(t *testing.T) {
v, contractState, context, chain := createVMAndContractState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(contractState))
err := context.contractGetScript(v)
require.NoError(t, err)
script := v.Estack().Pop().Value()
require.Equal(t, contractState.Script, script)
}
func TestContractIsPayable(t *testing.T) {
v, contractState, context, chain := createVMAndContractState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(contractState))
err := context.contractIsPayable(v)
require.NoError(t, err)
isPayable := v.Estack().Pop().Value()
require.Equal(t, contractState.IsPayable(), isPayable)
}
func TestAssetGetAdmin(t *testing.T) {
v, assetState, context, chain := createVMAndAssetState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(assetState))
err := context.assetGetAdmin(v)
require.NoError(t, err)
admin := v.Estack().Pop().Value()
require.Equal(t, assetState.Admin.BytesBE(), admin)
}
func TestAssetGetAmount(t *testing.T) {
v, assetState, context, chain := createVMAndAssetState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(assetState))
err := context.assetGetAmount(v)
require.NoError(t, err)
amount := v.Estack().Pop().Value()
require.Equal(t, big.NewInt(int64(assetState.Amount)), amount)
}
func TestAssetGetAssetID(t *testing.T) {
v, assetState, context, chain := createVMAndAssetState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(assetState))
err := context.assetGetAssetID(v)
require.NoError(t, err)
assetID := v.Estack().Pop().Value()
require.Equal(t, assetState.ID.BytesBE(), assetID)
}
func TestAssetGetAssetType(t *testing.T) {
v, assetState, context, chain := createVMAndAssetState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(assetState))
err := context.assetGetAssetType(v)
require.NoError(t, err)
assetType := v.Estack().Pop().Value()
require.Equal(t, big.NewInt(int64(assetState.AssetType)), assetType)
}
func TestAssetGetAvailable(t *testing.T) {
v, assetState, context, chain := createVMAndAssetState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(assetState))
err := context.assetGetAvailable(v)
require.NoError(t, err)
available := v.Estack().Pop().Value()
require.Equal(t, big.NewInt(int64(assetState.Available)), available)
}
func TestAssetGetIssuer(t *testing.T) {
v, assetState, context, chain := createVMAndAssetState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(assetState))
err := context.assetGetIssuer(v)
require.NoError(t, err)
issuer := v.Estack().Pop().Value()
require.Equal(t, assetState.Issuer.BytesBE(), issuer)
}
func TestAssetGetOwner(t *testing.T) {
v, assetState, context, chain := createVMAndAssetState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(assetState))
err := context.assetGetOwner(v)
require.NoError(t, err)
owner := v.Estack().Pop().Value()
require.Equal(t, assetState.Owner.Bytes(), owner)
}
func TestAssetGetPrecision(t *testing.T) {
v, assetState, context, chain := createVMAndAssetState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(assetState))
err := context.assetGetPrecision(v)
require.NoError(t, err)
precision := v.Estack().Pop().Value()
require.Equal(t, big.NewInt(int64(assetState.Precision)), precision)
}
// Helper functions to create VM, InteropContext, TX, Account, Contract, Asset.
func createVMAndPushBlock(t *testing.T) (*vm.VM, *block.Block, *interopContext, *Blockchain) {
v := vm.New()
block := newDumbBlock()
chain := newTestChain(t)
context := chain.newInteropContext(trigger.Application, storage.NewMemoryStore(), block, nil)
v.Estack().PushVal(vm.NewInteropItem(block))
return v, block, context, chain
}
func createVMAndPushTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interopContext, *Blockchain) {
v, tx, context, chain := createVMAndTX(t)
v.Estack().PushVal(vm.NewInteropItem(tx))
return v, tx, context, chain
}
func createVMAndAssetState(t *testing.T) (*vm.VM, *state.Asset, *interopContext, *Blockchain) {
v := vm.New()
assetState := &state.Asset{
ID: util.Uint256{},
AssetType: transaction.GoverningToken,
Name: "TestAsset",
Amount: 1,
Available: 2,
Precision: 1,
FeeMode: 1,
FeeAddress: random.Uint160(),
Owner: keys.PublicKey{X: big.NewInt(1), Y: big.NewInt(1)},
Admin: random.Uint160(),
Issuer: random.Uint160(),
Expiration: 10,
IsFrozen: false,
}
chain := newTestChain(t)
context := chain.newInteropContext(trigger.Application, storage.NewMemoryStore(), nil, nil)
return v, assetState, context, chain
}
func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interopContext, *Blockchain) {
v := vm.New()
contractState := &state.Contract{
Script: []byte("testscript"),
ParamList: []smartcontract.ParamType{smartcontract.StringType, smartcontract.IntegerType, smartcontract.Hash160Type},
ReturnType: smartcontract.ArrayType,
Properties: smartcontract.HasStorage,
Name: random.String(10),
CodeVersion: random.String(10),
Author: random.String(10),
Email: random.String(10),
Description: random.String(10),
}
chain := newTestChain(t)
context := chain.newInteropContext(trigger.Application, storage.NewMemoryStore(), nil, nil)
return v, contractState, context, chain
}
func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interopContext, *Blockchain) {
v := vm.New()
rawHash := "4d3b96ae1bcc5a585e075e3b81920210dec16302"
hash, err := util.Uint160DecodeStringBE(rawHash)
accountState := state.NewAccount(hash)
key := &keys.PublicKey{X: big.NewInt(1), Y: big.NewInt(1)}
accountState.Votes = []*keys.PublicKey{key}
require.NoError(t, err)
chain := newTestChain(t)
context := chain.newInteropContext(trigger.Application, storage.NewMemoryStore(), nil, nil)
return v, accountState, context, chain
}
func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interopContext, *Blockchain) {
v := vm.New()
script := []byte{byte(opcode.PUSH1), byte(opcode.RET)}
tx := transaction.NewInvocationTX(script, 0)
bytes := make([]byte, 1)
attributes := append(tx.Attributes, transaction.Attribute{
Usage: transaction.Description,
Data: bytes,
})
inputs := append(tx.Inputs, transaction.Input{
PrevHash: random.Uint256(),
PrevIndex: 1,
})
outputs := append(tx.Outputs, transaction.Output{
AssetID: random.Uint256(),
Amount: 10,
ScriptHash: random.Uint160(),
Position: 1,
})
tx.Attributes = attributes
tx.Inputs = inputs
tx.Outputs = outputs
chain := newTestChain(t)
context := chain.newInteropContext(trigger.Application, storage.NewMemoryStore(), nil, tx)
return v, tx, context, chain
}