Merge pull request #2710 from nspcc-dev/check-notification-abi-compiliance

runtime: check notifications against ABI
This commit is contained in:
Roman Khimov 2022-10-05 15:06:39 +07:00 committed by GitHub
commit 3609f26cc0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 273 additions and 120 deletions

View file

@ -45,7 +45,7 @@ func TestTypeConstantSize(t *testing.T) {
t.Run("Hash160", func(t *testing.T) {
t.Run("good", func(t *testing.T) {
a := make(cinterop.Hash160, 20)
a := make(cinterop.Hash160, smartcontract.Hash160Len)
src := fmt.Sprintf(src, a, a)
eval(t, src, []byte(a))
})
@ -58,7 +58,7 @@ func TestTypeConstantSize(t *testing.T) {
})
t.Run("Hash256", func(t *testing.T) {
t.Run("good", func(t *testing.T) {
a := make(cinterop.Hash256, 32)
a := make(cinterop.Hash256, smartcontract.Hash256Len)
src := fmt.Sprintf(src, a, a)
eval(t, src, []byte(a))
})

View file

@ -15,6 +15,7 @@ import (
istorage "github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
@ -488,6 +489,6 @@ func TestInteropTypesComparison(t *testing.T) {
}
typeCheck(t, "Hash160", util.Uint160Size)
typeCheck(t, "Hash256", util.Uint256Size)
typeCheck(t, "Signature", 64)
typeCheck(t, "PublicKey", 33)
typeCheck(t, "Signature", smartcontract.SignatureLen)
typeCheck(t, "PublicKey", smartcontract.PublicKeyLen)
}

View file

@ -228,13 +228,13 @@ func randomRecoveryMessage(t *testing.T) *recoveryMessage {
{
ViewNumber: 0,
ValidatorIndex: 1,
Signature: [64]byte{1, 2, 3},
Signature: [keys.SignatureLen]byte{1, 2, 3},
InvocationScript: random.Bytes(20),
},
{
ViewNumber: 0,
ValidatorIndex: 2,
Signature: [64]byte{11, 3, 4, 98},
Signature: [keys.SignatureLen]byte{11, 3, 4, 98},
InvocationScript: random.Bytes(10),
},
},

View file

@ -559,9 +559,9 @@ func TestBlockchain_VerifyHashAgainstScript(t *testing.T) {
require.True(t, errors.Is(err, core.ErrVerificationFailed))
})
t.Run("BadResult", func(t *testing.T) {
verif := make([]byte, 66)
verif := make([]byte, keys.SignatureLen+2)
verif[0] = byte(opcode.PUSHDATA1)
verif[1] = 64
verif[1] = keys.SignatureLen
w := &transaction.Witness{
InvocationScript: []byte{byte(opcode.NOP)},
VerificationScript: verif,
@ -1634,7 +1634,7 @@ func TestBlockchain_VerifyTx(t *testing.T) {
VerificationScript: rawScript,
},
{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, notary.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), tx)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, notary.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), tx)...),
},
}
return tx
@ -1679,7 +1679,7 @@ func TestBlockchain_VerifyTx(t *testing.T) {
VerificationScript: committee.Script(),
},
{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, notary.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), tx)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, notary.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), tx)...),
},
}
require.Error(t, bc.VerifyTx(tx))
@ -1698,7 +1698,7 @@ func TestBlockchain_VerifyTx(t *testing.T) {
}
tx.Scripts = []transaction.Witness{
{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, notary.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), tx)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, notary.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), tx)...),
},
{
InvocationScript: committee.SignHashable(uint32(netmode.UnitTestNet), tx),
@ -1725,7 +1725,7 @@ func TestBlockchain_VerifyTx(t *testing.T) {
VerificationScript: committee.Script(),
},
{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, notary.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), tx)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, notary.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), tx)...),
},
}
require.Error(t, bc.VerifyTx(tx))
@ -1766,7 +1766,7 @@ func TestBlockchain_VerifyTx(t *testing.T) {
VerificationScript: committee.Script(),
},
{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc.SignHashable(uint32(netmode.UnitTestNet), tx)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, acc.SignHashable(uint32(netmode.UnitTestNet), tx)...),
},
}
require.Error(t, bc.VerifyTx(tx))
@ -1781,7 +1781,7 @@ func TestBlockchain_VerifyTx(t *testing.T) {
}
tx.Scripts = []transaction.Witness{
{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, notary.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), tx)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, notary.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), tx)...),
},
}
require.Error(t, bc.VerifyTx(tx))
@ -1831,7 +1831,7 @@ func TestBlockchain_VerifyTx(t *testing.T) {
nativeprices.NotaryVerificationPrice*bc.GetBaseExecFee() // Notary witness verification price
tx.Scripts = []transaction.Witness{
{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...),
VerificationScript: []byte{},
},
{
@ -1858,7 +1858,7 @@ func TestBlockchain_VerifyTx(t *testing.T) {
tx.NetworkFee-- // to check that NetworkFee was set correctly in getPartiallyFilledTx
tx.Scripts = []transaction.Witness{
{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...),
VerificationScript: []byte{},
},
{

View file

@ -73,9 +73,20 @@ func Notify(ic *interop.Context) error {
if len(name) > MaxEventNameLen {
return fmt.Errorf("event name must be less than %d", MaxEventNameLen)
}
if !ic.VM.Context().IsDeployed() {
curHash := ic.VM.GetCurrentScriptHash()
ctr, err := ic.GetContract(curHash)
if err != nil {
return errors.New("notifications are not allowed in dynamic scripts")
}
ev := ctr.Manifest.ABI.GetEvent(name)
if ev == nil {
ic.Log.Info("bad notification", zap.String("contract", curHash.StringLE()), zap.String("event", name), zap.Error(fmt.Errorf("event %s does not exist", name)))
} else {
err = ev.CheckCompliance(args)
if err != nil {
ic.Log.Info("bad notification", zap.String("contract", curHash.StringLE()), zap.String("event", name), zap.Error(err))
}
}
// But it has to be serializable, otherwise we either have some broken
// (recursive) structure inside or an interop item that can't be used
@ -87,7 +98,7 @@ func Notify(ic *interop.Context) error {
if len(bytes) > MaxNotificationSize {
return fmt.Errorf("notification size shouldn't exceed %d", MaxNotificationSize)
}
ic.AddNotification(ic.VM.GetCurrentScriptHash(), name, stackitem.DeepCopy(stackitem.NewArray(args), true).(*stackitem.Array))
ic.AddNotification(curHash, name, stackitem.DeepCopy(stackitem.NewArray(args), true).(*stackitem.Array))
return nil
}

View file

@ -8,11 +8,9 @@ import (
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/dao"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"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"
@ -131,55 +129,3 @@ func TestLog(t *testing.T) {
require.Equal(t, h.StringLE(), logMsg["script"])
})
}
func TestNotify(t *testing.T) {
h := random.Uint160()
caller := random.Uint160()
exe, err := nef.NewFile([]byte{1})
require.NoError(t, err)
newIC := func(name string, args interface{}) *interop.Context {
ic := &interop.Context{VM: vm.New(), DAO: &dao.Simple{}}
ic.VM.LoadNEFMethod(exe, caller, h, callflag.NoneFlag, true, 0, -1, nil)
ic.VM.Estack().PushVal(args)
ic.VM.Estack().PushVal(name)
return ic
}
t.Run("big name", func(t *testing.T) {
ic := newIC(string(make([]byte, MaxEventNameLen+1)), stackitem.NewArray([]stackitem.Item{stackitem.Null{}}))
require.Error(t, Notify(ic))
})
t.Run("dynamic script", func(t *testing.T) {
ic := &interop.Context{VM: vm.New(), DAO: &dao.Simple{}}
ic.VM.LoadScriptWithHash([]byte{1}, h, callflag.NoneFlag)
ic.VM.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.Make(42)}))
ic.VM.Estack().PushVal("event")
require.Error(t, Notify(ic))
})
t.Run("recursive struct", func(t *testing.T) {
arr := stackitem.NewArray([]stackitem.Item{stackitem.Null{}})
arr.Append(arr)
ic := newIC("event", arr)
require.Error(t, Notify(ic))
})
t.Run("big notification", func(t *testing.T) {
bs := stackitem.NewByteArray(make([]byte, MaxNotificationSize+1))
arr := stackitem.NewArray([]stackitem.Item{bs})
ic := newIC("event", arr)
require.Error(t, Notify(ic))
})
t.Run("good", func(t *testing.T) {
arr := stackitem.NewArray([]stackitem.Item{stackitem.Make(42)})
ic := newIC("good event", arr)
require.NoError(t, Notify(ic))
require.Equal(t, 1, len(ic.Notifications))
arr.MarkAsReadOnly() // tiny hack for test to be able to compare object references.
ev := ic.Notifications[0]
require.Equal(t, "good event", ev.Name)
require.Equal(t, h, ev.ScriptHash)
require.Equal(t, arr, ev.Item)
// Check deep copy.
arr.Value().([]stackitem.Item)[0] = stackitem.Null{}
require.NotEqual(t, arr, ev.Item)
})
}

View file

@ -79,7 +79,7 @@ func loadScriptWithHashAndFlags(ic *interop.Context, script []byte, hash util.Ui
ic.VM.GasLimit = -1
}
func TestBurnGas(t *testing.T) {
func getDeployedInternal(t *testing.T) (*neotest.Executor, neotest.Signer, *core.Blockchain, *state.Contract) {
bc, acc := chain.NewSingle(t)
e := neotest.NewExecutor(t, bc, acc, acc)
managementInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
@ -92,6 +92,12 @@ func TestBurnGas(t *testing.T) {
tx := managementInvoker.PrepareInvoke(t, "deploy", rawNef, rawManifest)
e.AddNewBlock(t, tx)
e.CheckHalt(t, tx.Hash())
return e, acc, bc, cs
}
func TestBurnGas(t *testing.T) {
e, acc, _, cs := getDeployedInternal(t)
cInvoker := e.ValidatorInvoker(cs.Hash)
t.Run("good", func(t *testing.T) {
@ -539,3 +545,53 @@ func TestGetRandomCompatibility(t *testing.T) {
require.NoError(t, runtime.GetRandom(ic))
require.Equal(t, "247152297361212656635216876565962360375", ic.VM.Estack().Pop().BigInt().String())
}
func TestNotify(t *testing.T) {
caller := random.Uint160()
newIC := func(name string, args interface{}) *interop.Context {
_, _, bc, cs := getDeployedInternal(t)
ic := bc.GetTestVM(trigger.Application, nil, nil)
ic.VM.LoadNEFMethod(&cs.NEF, caller, cs.Hash, callflag.NoneFlag, true, 0, -1, nil)
ic.VM.Estack().PushVal(args)
ic.VM.Estack().PushVal(name)
return ic
}
t.Run("big name", func(t *testing.T) {
ic := newIC(string(make([]byte, runtime.MaxEventNameLen+1)), stackitem.NewArray([]stackitem.Item{stackitem.Null{}}))
require.Error(t, runtime.Notify(ic))
})
t.Run("dynamic script", func(t *testing.T) {
ic := newIC("some", stackitem.Null{})
ic.VM.LoadScriptWithHash([]byte{1}, random.Uint160(), callflag.NoneFlag)
ic.VM.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.Make(42)}))
ic.VM.Estack().PushVal("event")
require.Error(t, runtime.Notify(ic))
})
t.Run("recursive struct", func(t *testing.T) {
arr := stackitem.NewArray([]stackitem.Item{stackitem.Null{}})
arr.Append(arr)
ic := newIC("event", arr)
require.Error(t, runtime.Notify(ic))
})
t.Run("big notification", func(t *testing.T) {
bs := stackitem.NewByteArray(make([]byte, runtime.MaxNotificationSize+1))
arr := stackitem.NewArray([]stackitem.Item{bs})
ic := newIC("event", arr)
require.Error(t, runtime.Notify(ic))
})
t.Run("good", func(t *testing.T) {
arr := stackitem.NewArray([]stackitem.Item{stackitem.Make(42)})
ic := newIC("good event", arr)
require.NoError(t, runtime.Notify(ic))
require.Equal(t, 1, len(ic.Notifications))
arr.MarkAsReadOnly() // tiny hack for test to be able to compare object references.
ev := ic.Notifications[0]
require.Equal(t, "good event", ev.Name)
require.Equal(t, ic.VM.GetCurrentScriptHash(), ev.ScriptHash)
require.Equal(t, arr, ev.Item)
// Check deep copy.
arr.Value().([]stackitem.Item)[0] = stackitem.Null{}
require.NotEqual(t, arr, ev.Item)
})
}

View file

@ -120,7 +120,7 @@ func TestGAS_RewardWithP2PSigExtensionsEnabled(t *testing.T) {
}
tx.Scripts = []transaction.Witness{
{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, notaryNodes[0].SignHashable(uint32(e.Chain.GetConfig().Magic), tx)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, notaryNodes[0].SignHashable(uint32(e.Chain.GetConfig().Magic), tx)...),
},
{
InvocationScript: e.Committee.SignHashable(uint32(e.Chain.GetConfig().Magic), tx),

View file

@ -126,7 +126,7 @@ func TestManagement_ContractDeploy(t *testing.T) {
require.NoError(t, err)
badManifest := cs1.Manifest
badManifest.Groups = []manifest.Group{{PublicKey: pkey.PublicKey(), Signature: make([]byte, 64)}}
badManifest.Groups = []manifest.Group{{PublicKey: pkey.PublicKey(), Signature: make([]byte, keys.SignatureLen)}}
manifB, err := json.Marshal(&badManifest)
require.NoError(t, err)
@ -432,7 +432,7 @@ func TestManagement_ContractUpdate(t *testing.T) {
require.NoError(t, err)
var badManifest = cs1.Manifest
badManifest.Groups = []manifest.Group{{PublicKey: pkey.PublicKey(), Signature: make([]byte, 64)}}
badManifest.Groups = []manifest.Group{{PublicKey: pkey.PublicKey(), Signature: make([]byte, keys.SignatureLen)}}
manifB, err := json.Marshal(badManifest)
require.NoError(t, err)

View file

@ -221,7 +221,7 @@ func TestNotary_NotaryNodesReward(t *testing.T) {
}
tx.Scripts = []transaction.Witness{
{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, notaryNodes[0].SignHashable(uint32(e.Chain.GetConfig().Magic), tx)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, notaryNodes[0].SignHashable(uint32(e.Chain.GetConfig().Magic), tx)...),
},
{
InvocationScript: e.Committee.SignHashable(uint32(e.Chain.GetConfig().Magic), tx),

View file

@ -17,7 +17,7 @@ type Hashable interface {
}
func getSignedData(net uint32, hh Hashable) []byte {
var b = make([]byte, 4+32)
var b = make([]byte, 4+util.Uint256Size)
binary.LittleEndian.PutUint32(b, net)
h := hh.Hash()
copy(b[4:], h[:])

View file

@ -9,6 +9,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"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"
@ -73,7 +74,7 @@ func (s *signer) ScriptHash() util.Uint160 {
// SignHashable implements Signer interface.
func (s *signer) SignHashable(magic uint32, item hash.Hashable) []byte {
return append([]byte{byte(opcode.PUSHDATA1), 64},
return append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen},
(*wallet.Account)(s).SignHashable(netmode.Magic(magic), item)...)
}
@ -131,7 +132,7 @@ func (m multiSigner) SignHashable(magic uint32, item hash.Hashable) []byte {
var script []byte
for i := 0; i < m.m; i++ {
sign := m.accounts[i].SignHashable(netmode.Magic(magic), item)
script = append(script, byte(opcode.PUSHDATA1), 64)
script = append(script, byte(opcode.PUSHDATA1), keys.SignatureLen)
script = append(script, sign...)
}
return script

View file

@ -6,6 +6,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
@ -110,7 +111,7 @@ func (r *P2PNotaryRequest) isValid() error {
}
if len(r.FallbackTransaction.Scripts[0].InvocationScript) != 66 ||
len(r.FallbackTransaction.Scripts[0].VerificationScript) != 0 ||
!bytes.HasPrefix(r.FallbackTransaction.Scripts[0].InvocationScript, []byte{byte(opcode.PUSHDATA1), 64}) {
!bytes.HasPrefix(r.FallbackTransaction.Scripts[0].InvocationScript, []byte{byte(opcode.PUSHDATA1), keys.SignatureLen}) {
return errors.New("fallback transaction has invalid dummy Notary witness")
}
if !r.FallbackTransaction.HasAttribute(transaction.NotValidBeforeT) {

View file

@ -5,6 +5,7 @@ import (
"github.com/nspcc-dev/neo-go/internal/random"
"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/util"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/stretchr/testify/require"
@ -45,21 +46,21 @@ func TestNotaryRequestIsValid(t *testing.T) {
MainTransaction: mainTx,
FallbackTransaction: &transaction.Transaction{
Signers: []transaction.Signer{{Account: random.Uint160()}, {Account: random.Uint160()}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 65}, make([]byte, 64)...)}, {}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 65}, make([]byte, keys.SignatureLen)...)}, {}},
},
},
"fallback tx: invalid dummy Notary witness (non-empty verification script))": {
MainTransaction: mainTx,
FallbackTransaction: &transaction.Transaction{
Signers: []transaction.Signer{{Account: random.Uint160()}, {Account: random.Uint160()}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...), VerificationScript: make([]byte, 1)}, {}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: make([]byte, 1)}, {}},
},
},
"fallback tx: missing NotValidBefore attribute": {
MainTransaction: mainTx,
FallbackTransaction: &transaction.Transaction{
Signers: []transaction.Signer{{Account: random.Uint160()}, {Account: random.Uint160()}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...), VerificationScript: make([]byte, 0)}, {}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: make([]byte, 0)}, {}},
},
},
"fallback tx: invalid number of Conflicts attributes": {
@ -67,7 +68,7 @@ func TestNotaryRequestIsValid(t *testing.T) {
FallbackTransaction: &transaction.Transaction{
Attributes: []transaction.Attribute{{Type: transaction.NotValidBeforeT, Value: &transaction.NotValidBefore{Height: 123}}},
Signers: []transaction.Signer{{Account: random.Uint160()}, {Account: random.Uint160()}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...), VerificationScript: make([]byte, 0)}, {}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: make([]byte, 0)}, {}},
},
},
"fallback tx: does not conflicts with main tx": {
@ -78,7 +79,7 @@ func TestNotaryRequestIsValid(t *testing.T) {
{Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: util.Uint256{}}},
},
Signers: []transaction.Signer{{Account: random.Uint160()}, {Account: random.Uint160()}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...), VerificationScript: make([]byte, 0)}, {}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: make([]byte, 0)}, {}},
},
},
"fallback tx: missing NotaryAssisted attribute": {
@ -89,7 +90,7 @@ func TestNotaryRequestIsValid(t *testing.T) {
{Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: mainTx.Hash()}},
},
Signers: []transaction.Signer{{Account: random.Uint160()}, {Account: random.Uint160()}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...), VerificationScript: make([]byte, 0)}, {}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: make([]byte, 0)}, {}},
},
},
"fallback tx: non-zero NKeys": {
@ -101,7 +102,7 @@ func TestNotaryRequestIsValid(t *testing.T) {
{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 1}},
},
Signers: []transaction.Signer{{Account: random.Uint160()}, {Account: random.Uint160()}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...), VerificationScript: make([]byte, 0)}, {}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: make([]byte, 0)}, {}},
},
},
"fallback tx: ValidUntilBlock mismatch": {
@ -114,7 +115,7 @@ func TestNotaryRequestIsValid(t *testing.T) {
{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 0}},
},
Signers: []transaction.Signer{{Account: random.Uint160()}, {Account: random.Uint160()}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...), VerificationScript: make([]byte, 0)}, {}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: make([]byte, 0)}, {}},
},
},
}
@ -134,7 +135,7 @@ func TestNotaryRequestIsValid(t *testing.T) {
{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 0}},
},
Signers: []transaction.Signer{{Account: random.Uint160()}, {Account: random.Uint160()}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...), VerificationScript: make([]byte, 0)}, {}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: make([]byte, 0)}, {}},
},
}
require.NoError(t, p.isValid())
@ -164,7 +165,7 @@ func TestNotaryRequestBytesFromBytes(t *testing.T) {
},
Signers: []transaction.Signer{{Account: util.Uint160{1, 4, 7}}, {Account: util.Uint160{9, 8, 7}}},
Scripts: []transaction.Witness{
{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...), VerificationScript: make([]byte, 0)},
{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: make([]byte, 0)},
{InvocationScript: []byte{1, 2, 3}, VerificationScript: []byte{1, 2, 3}}},
}
_ = fallbackTx.Hash()

View file

@ -19,6 +19,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/mpt"
"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/network/capability"
"github.com/nspcc-dev/neo-go/pkg/network/payload"
"github.com/nspcc-dev/neo-go/pkg/util"
@ -545,7 +546,7 @@ func TestGetData(t *testing.T) {
{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 0}},
},
Signers: []transaction.Signer{{Account: random.Uint160()}, {Account: random.Uint160()}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...), VerificationScript: make([]byte, 0)}, {InvocationScript: []byte{}, VerificationScript: []byte{}}},
Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: make([]byte, 0)}, {InvocationScript: []byte{}, VerificationScript: []byte{}}},
}
fallbackTx.Size()
fallbackTx.Hash()

View file

@ -5,6 +5,7 @@ import (
"fmt"
"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/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/network/payload"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
@ -278,7 +279,7 @@ func (a *Actor) SendRequest(mainTx *transaction.Transaction, fbTx *transaction.T
if err != nil {
return mainHash, fbHash, vub, err
}
fbTx.Scripts[0].InvocationScript = append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...) // Must be present.
fbTx.Scripts[0].InvocationScript = append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...) // Must be present.
return a.SendRequestExactly(mainTx, fbTx)
}
@ -300,7 +301,7 @@ func (a *Actor) SendRequestExactly(mainTx *transaction.Transaction, fbTx *transa
FallbackTransaction: fbTx,
}
req.Witness = transaction.Witness{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, a.sender.SignHashable(a.GetNetwork(), req)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, a.sender.SignHashable(a.GetNetwork(), req)...),
VerificationScript: a.sender.GetVerificationScript(),
}
actualHash, err := a.rpc.SubmitP2PNotaryRequest(req)

View file

@ -893,7 +893,7 @@ func (c *Client) SignAndPushP2PNotaryRequest(mainTx *transaction.Transaction, fa
fallbackTx.Scripts = []transaction.Witness{
{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...),
VerificationScript: []byte{},
},
{
@ -919,7 +919,7 @@ func (c *Client) SignAndPushP2PNotaryRequest(mainTx *transaction.Transaction, fa
FallbackTransaction: fallbackTx,
}
req.Witness = transaction.Witness{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc.SignHashable(m, req)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, acc.SignHashable(m, req)...),
VerificationScript: acc.GetVerificationScript(),
}
actualHash, err := c.SubmitP2PNotaryRequest(req)

View file

@ -199,7 +199,7 @@ func TestNotary(t *testing.T) {
}
fallback.Scripts = []transaction.Witness{
{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...),
VerificationScript: []byte{},
},
}
@ -261,7 +261,7 @@ func TestNotary(t *testing.T) {
for j := range main.Scripts {
main.Scripts[j].VerificationScript = verificationScripts[j]
if i == j {
main.Scripts[j].InvocationScript = append([]byte{byte(opcode.PUSHDATA1), 64}, acc.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), main)...)
main.Scripts[j].InvocationScript = append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, acc.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), main)...)
}
}
main.Scripts = append(main.Scripts, transaction.Witness{}) // empty Notary witness
@ -310,7 +310,7 @@ func TestNotary(t *testing.T) {
require.NoError(t, err)
}
require.Equal(t, transaction.Witness{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc1.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), requests[0].MainTransaction)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, acc1.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), requests[0].MainTransaction)...),
VerificationScript: []byte{},
}, completedTx.Scripts[len(completedTx.Scripts)-1])
} else {
@ -325,7 +325,7 @@ func TestNotary(t *testing.T) {
require.Equal(t, 2, len(completedTx.Signers))
require.Equal(t, 2, len(completedTx.Scripts))
require.Equal(t, transaction.Witness{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc1.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), req.FallbackTransaction)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, acc1.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), req.FallbackTransaction)...),
VerificationScript: []byte{},
}, completedTx.Scripts[0])

View file

@ -392,7 +392,7 @@ func (n *Notary) PostPersist() {
// finalize adds missing Notary witnesses to the transaction (main or fallback) and pushes it to the network.
func (n *Notary) finalize(acc *wallet.Account, tx *transaction.Transaction, h util.Uint256) error {
notaryWitness := transaction.Witness{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc.SignHashable(n.Network, tx)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, acc.SignHashable(n.Network, tx)...),
VerificationScript: []byte{},
}
for i, signer := range tx.Signers {
@ -515,7 +515,7 @@ func (n *Notary) verifyIncompleteWitnesses(tx *transaction.Transaction, nKeysExp
}
// Each verification script is allowed to have either one signature or zero signatures. If signature is provided, then need to verify it.
if len(w.InvocationScript) != 0 {
if len(w.InvocationScript) != 66 || !bytes.HasPrefix(w.InvocationScript, []byte{byte(opcode.PUSHDATA1), 64}) {
if len(w.InvocationScript) != 66 || !bytes.HasPrefix(w.InvocationScript, []byte{byte(opcode.PUSHDATA1), keys.SignatureLen}) {
return nil, fmt.Errorf("witness #%d: invocation script should have length = 66 and be of the form [PUSHDATA1, 64, signatureBytes...]", i)
}
}

View file

@ -51,7 +51,7 @@ func TestVerifyIncompleteRequest(t *testing.T) {
notaryContractHash := util.Uint160{1, 2, 3}
bc.NotaryContractScriptHash = notaryContractHash
_, ntr, _ := getTestNotary(t, bc, "./testdata/notary1.json", "one")
sig := append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...) // we're not interested in signature correctness
sig := append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...) // we're not interested in signature correctness
acc1, _ := keys.NewPrivateKey()
acc2, _ := keys.NewPrivateKey()
acc3, _ := keys.NewPrivateKey()

View file

@ -1089,7 +1089,7 @@ func TestSignAndPushP2PNotaryRequest(t *testing.T) {
err = ntr.Decrypt(notaryPass, w.Scrypt)
require.NoError(t, err)
req.FallbackTransaction.Scripts[0] = transaction.Witness{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, ntr.PrivateKey().SignHashable(uint32(testchain.Network()), req.FallbackTransaction)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, ntr.PrivateKey().SignHashable(uint32(testchain.Network()), req.FallbackTransaction)...),
VerificationScript: []byte{},
}
b := testchain.NewBlock(t, chain, 1, 0, req.FallbackTransaction)

View file

@ -1782,7 +1782,7 @@ func TestSubmitNotaryRequest(t *testing.T) {
},
Signers: []transaction.Signer{{Account: util.Uint160{1, 4, 7}}, {Account: util.Uint160{9, 8, 7}}},
Scripts: []transaction.Witness{
{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...), VerificationScript: make([]byte, 0)},
{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: make([]byte, 0)},
{InvocationScript: []byte{1, 2, 3}, VerificationScript: []byte{1, 2, 3}}},
}
p := &payload.P2PNotaryRequest{
@ -1833,12 +1833,12 @@ func createValidNotaryRequest(chain *core.Blockchain, sender *keys.PrivateKey, n
},
Signers: []transaction.Signer{{Account: chain.GetNotaryContractScriptHash()}, {Account: sender.GetScriptHash()}},
Scripts: []transaction.Witness{
{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...), VerificationScript: []byte{}},
{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: []byte{}},
},
NetworkFee: 2_0000_0000,
}
fallbackTx.Scripts = append(fallbackTx.Scripts, transaction.Witness{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, sender.SignHashable(uint32(testchain.Network()), fallbackTx)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, sender.SignHashable(uint32(testchain.Network()), fallbackTx)...),
VerificationScript: sender.PublicKey().GetVerificationScript(),
})
p := &payload.P2PNotaryRequest{
@ -1846,7 +1846,7 @@ func createValidNotaryRequest(chain *core.Blockchain, sender *keys.PrivateKey, n
FallbackTransaction: fallbackTx,
}
p.Witness = transaction.Witness{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, sender.SignHashable(uint32(testchain.Network()), p)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, sender.SignHashable(uint32(testchain.Network()), p)...),
VerificationScript: sender.PublicKey().GetVerificationScript(),
}
return p

View file

@ -43,11 +43,11 @@ func TestContextItem_MarshalJSON(t *testing.T) {
Script: []byte{1, 2, 3},
Parameters: []smartcontract.Parameter{{
Type: smartcontract.SignatureType,
Value: random.Bytes(64),
Value: random.Bytes(keys.SignatureLen),
}},
Signatures: map[string][]byte{
hex.EncodeToString(priv1.PublicKey().Bytes()): random.Bytes(64),
hex.EncodeToString(priv2.PublicKey().Bytes()): random.Bytes(64),
hex.EncodeToString(priv1.PublicKey().Bytes()): random.Bytes(keys.SignatureLen),
hex.EncodeToString(priv2.PublicKey().Bytes()): random.Bytes(keys.SignatureLen),
},
}

View file

@ -2,6 +2,7 @@ package manifest
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
@ -60,3 +61,17 @@ func (e *Event) FromStackItem(item stackitem.Item) error {
}
return nil
}
// CheckCompliance checks compliance of the given array of items with the
// current event.
func (e *Event) CheckCompliance(items []stackitem.Item) error {
if len(items) != len(e.Parameters) {
return errors.New("mismatch between the number of parameters and items")
}
for i := range items {
if !e.Parameters[i].Type.Match(items[i]) {
return fmt.Errorf("parameter %d type mismatch: %s vs %s", i, e.Parameters[i].Type.String(), items[i].Type().String())
}
}
return nil
}

View file

@ -64,3 +64,13 @@ func TestEvent_FromStackItemErrors(t *testing.T) {
})
}
}
func TestEventCheckCompliance(t *testing.T) {
m := &Event{
Name: "mur",
Parameters: []Parameter{{Name: "p1", Type: smartcontract.BoolType}},
}
require.Error(t, m.CheckCompliance([]stackitem.Item{}))
require.Error(t, m.CheckCompliance([]stackitem.Item{stackitem.Make("something")}))
require.NoError(t, m.CheckCompliance([]stackitem.Item{stackitem.Make(true)}))
}

View file

@ -38,6 +38,14 @@ const (
VoidType ParamType = 0xff
)
// Lengths (in bytes) of fixed-size types.
const (
Hash160Len = util.Uint160Size
Hash256Len = util.Uint256Size
PublicKeyLen = 33
SignatureLen = keys.SignatureLen
)
// fileBytesParamType is a string representation of `filebytes` parameter type used in cli.
const fileBytesParamType string = "filebytes"
@ -151,7 +159,7 @@ func (pt *ParamType) DecodeBinary(r *io.BinReader) {
// public key is represented by 33-byte value while 32 bytes are used for integer
// and a simple push+convert is used for boolean. Other types produce no code at all.
func (pt ParamType) EncodeDefaultValue(w *io.BinWriter) {
var b [64]byte
var b [SignatureLen]byte
switch pt {
case AnyType, SignatureType, StringType, ByteArrayType:
@ -161,15 +169,61 @@ func (pt ParamType) EncodeDefaultValue(w *io.BinWriter) {
case IntegerType:
emit.Instruction(w, opcode.PUSHINT256, b[:32])
case Hash160Type:
emit.Bytes(w, b[:20])
emit.Bytes(w, b[:Hash160Len])
case Hash256Type:
emit.Bytes(w, b[:32])
emit.Bytes(w, b[:Hash256Len])
case PublicKeyType:
emit.Bytes(w, b[:33])
emit.Bytes(w, b[:PublicKeyLen])
case ArrayType, MapType, InteropInterfaceType, VoidType:
}
}
func checkBytesWithLen(vt stackitem.Type, v stackitem.Item, l int) bool {
if vt == stackitem.AnyT {
return true
}
if vt != stackitem.ByteArrayT && vt != stackitem.BufferT {
return false
}
b, _ := v.TryBytes() // Can't fail, we know the type exactly.
return len(b) == l
}
func (pt ParamType) Match(v stackitem.Item) bool {
vt := v.Type()
// Pointer can't be matched at all.
if vt == stackitem.PointerT {
return false
}
switch pt {
case AnyType:
return true
case BoolType:
return vt == stackitem.BooleanT
case IntegerType:
return vt == stackitem.IntegerT
case ByteArrayType, StringType:
return vt == stackitem.ByteArrayT || vt == stackitem.BufferT || vt == stackitem.AnyT
case Hash160Type:
return checkBytesWithLen(vt, v, Hash160Len)
case Hash256Type:
return checkBytesWithLen(vt, v, Hash256Len)
case PublicKeyType:
return checkBytesWithLen(vt, v, PublicKeyLen)
case SignatureType:
return checkBytesWithLen(vt, v, SignatureLen)
case ArrayType:
return vt == stackitem.AnyT || vt == stackitem.ArrayT || vt == stackitem.StructT
case MapType:
return vt == stackitem.AnyT || vt == stackitem.MapT
case InteropInterfaceType:
return vt == stackitem.AnyT || vt == stackitem.InteropT
default:
return false
}
}
// ParseParamType is a user-friendly string to ParamType converter, it's
// case-insensitive and makes the following conversions:
//
@ -228,7 +282,7 @@ func adjustValToType(typ ParamType, val string) (interface{}, error) {
if err != nil {
return nil, err
}
if len(b) != 64 {
if len(b) != SignatureLen {
return nil, errors.New("not a signature")
}
return b, nil
@ -310,11 +364,11 @@ func inferParamType(val string) ParamType {
unhexed, err := hex.DecodeString(val)
if err == nil {
switch len(unhexed) {
case 20:
case Hash160Len:
return Hash160Type
case 32:
case Hash256Len:
return Hash256Type
case 64:
case SignatureLen:
return SignatureType
default:
return ByteArrayType

View file

@ -418,3 +418,58 @@ func TestConvertToStackitemType(t *testing.T) {
UnknownType.ConvertToStackitemType()
})
}
func TestParamTypeMatch(t *testing.T) {
for itm, pt := range map[stackitem.Item]ParamType{
&stackitem.Pointer{}: BoolType,
&stackitem.Pointer{}: MapType,
stackitem.Make(0): BoolType,
stackitem.Make(0): ByteArrayType,
stackitem.Make(0): StringType,
stackitem.Make(false): ByteArrayType,
stackitem.Make(true): StringType,
stackitem.Make([]byte{1}): Hash160Type,
stackitem.Make([]byte{1}): Hash256Type,
stackitem.Make([]byte{1}): PublicKeyType,
stackitem.Make([]byte{1}): SignatureType,
stackitem.Make(0): Hash160Type,
stackitem.Make(0): Hash256Type,
stackitem.Make(0): PublicKeyType,
stackitem.Make(0): SignatureType,
stackitem.Make(0): ArrayType,
stackitem.Make(0): MapType,
stackitem.Make(0): InteropInterfaceType,
stackitem.Make(0): VoidType,
} {
require.Falsef(t, pt.Match(itm), "%s - %s", pt.String(), itm.String())
}
for itm, pt := range map[stackitem.Item]ParamType{
stackitem.Make(false): BoolType,
stackitem.Make(true): BoolType,
stackitem.Make(0): IntegerType,
stackitem.Make(100500): IntegerType,
stackitem.Make([]byte{1}): ByteArrayType,
stackitem.Make([]byte{1}): StringType,
stackitem.NewBuffer([]byte{1}): ByteArrayType,
stackitem.NewBuffer([]byte{1}): StringType,
stackitem.Null{}: ByteArrayType,
stackitem.Null{}: StringType,
stackitem.Make(util.Uint160{}.BytesBE()): Hash160Type,
stackitem.Make(util.Uint256{}.BytesBE()): Hash256Type,
stackitem.Null{}: Hash160Type,
stackitem.Null{}: Hash256Type,
stackitem.Make(make([]byte, PublicKeyLen)): PublicKeyType,
stackitem.Null{}: PublicKeyType,
stackitem.Make(make([]byte, SignatureLen)): SignatureType,
stackitem.Null{}: SignatureType,
stackitem.Make([]stackitem.Item{}): ArrayType,
stackitem.NewStruct([]stackitem.Item{}): ArrayType,
stackitem.Null{}: ArrayType,
stackitem.NewMap(): MapType,
stackitem.Null{}: MapType,
stackitem.NewInterop(true): InteropInterfaceType,
stackitem.Null{}: InteropInterfaceType,
} {
require.Truef(t, pt.Match(itm), "%s - %s", pt.String(), itm.String())
}
}

View file

@ -116,7 +116,7 @@ func (a *Account) SignTx(net netmode.Magic, t *transaction.Transaction) error {
}
sign := a.privateKey.SignHashable(uint32(net), t)
invoc := append([]byte{byte(opcode.PUSHDATA1), 64}, sign...)
invoc := append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, sign...)
if len(a.Contract.Parameters) == 1 {
t.Scripts[pos].InvocationScript = invoc
} else {