diff --git a/pkg/compiler/interop_test.go b/pkg/compiler/interop_test.go index 360c81f3a..2ba7a5f2f 100644 --- a/pkg/compiler/interop_test.go +++ b/pkg/compiler/interop_test.go @@ -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)) }) diff --git a/pkg/compiler/syscall_test.go b/pkg/compiler/syscall_test.go index 22cbb2756..d42610d07 100644 --- a/pkg/compiler/syscall_test.go +++ b/pkg/compiler/syscall_test.go @@ -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) } diff --git a/pkg/consensus/payload_test.go b/pkg/consensus/payload_test.go index 3101f6072..1eddab699 100644 --- a/pkg/consensus/payload_test.go +++ b/pkg/consensus/payload_test.go @@ -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), }, }, diff --git a/pkg/core/blockchain_neotest_test.go b/pkg/core/blockchain_neotest_test.go index ffe9f23ba..043939c5a 100644 --- a/pkg/core/blockchain_neotest_test.go +++ b/pkg/core/blockchain_neotest_test.go @@ -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{}, }, { diff --git a/pkg/core/interop/runtime/engine.go b/pkg/core/interop/runtime/engine.go index 73654db19..daadcda5a 100644 --- a/pkg/core/interop/runtime/engine.go +++ b/pkg/core/interop/runtime/engine.go @@ -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 } diff --git a/pkg/core/interop/runtime/engine_test.go b/pkg/core/interop/runtime/engine_test.go index 4ddf628ae..656a78dcb 100644 --- a/pkg/core/interop/runtime/engine_test.go +++ b/pkg/core/interop/runtime/engine_test.go @@ -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) - }) -} diff --git a/pkg/core/interop/runtime/ext_test.go b/pkg/core/interop/runtime/ext_test.go index 43e5fff56..d5a878036 100644 --- a/pkg/core/interop/runtime/ext_test.go +++ b/pkg/core/interop/runtime/ext_test.go @@ -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) + }) +} diff --git a/pkg/core/native/native_test/gas_test.go b/pkg/core/native/native_test/gas_test.go index a478b13a9..dd270ade2 100644 --- a/pkg/core/native/native_test/gas_test.go +++ b/pkg/core/native/native_test/gas_test.go @@ -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), diff --git a/pkg/core/native/native_test/management_test.go b/pkg/core/native/native_test/management_test.go index dd64aeed5..37699f59d 100644 --- a/pkg/core/native/native_test/management_test.go +++ b/pkg/core/native/native_test/management_test.go @@ -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) diff --git a/pkg/core/native/native_test/notary_test.go b/pkg/core/native/native_test/notary_test.go index 40339ebe5..a7f020f61 100644 --- a/pkg/core/native/native_test/notary_test.go +++ b/pkg/core/native/native_test/notary_test.go @@ -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), diff --git a/pkg/crypto/hash/hash.go b/pkg/crypto/hash/hash.go index 9df09c66f..a997a3514 100644 --- a/pkg/crypto/hash/hash.go +++ b/pkg/crypto/hash/hash.go @@ -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[:]) diff --git a/pkg/neotest/signer.go b/pkg/neotest/signer.go index df529e607..333144434 100644 --- a/pkg/neotest/signer.go +++ b/pkg/neotest/signer.go @@ -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 diff --git a/pkg/network/payload/notary_request.go b/pkg/network/payload/notary_request.go index 8c68efe8a..afc8f813b 100644 --- a/pkg/network/payload/notary_request.go +++ b/pkg/network/payload/notary_request.go @@ -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) { diff --git a/pkg/network/payload/notary_request_test.go b/pkg/network/payload/notary_request_test.go index 8e0bc6ea0..41eb073f8 100644 --- a/pkg/network/payload/notary_request_test.go +++ b/pkg/network/payload/notary_request_test.go @@ -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() diff --git a/pkg/network/server_test.go b/pkg/network/server_test.go index 750878e9f..578e82f7e 100644 --- a/pkg/network/server_test.go +++ b/pkg/network/server_test.go @@ -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() diff --git a/pkg/rpcclient/notary/actor.go b/pkg/rpcclient/notary/actor.go index 06c9ef958..c99a4e7af 100644 --- a/pkg/rpcclient/notary/actor.go +++ b/pkg/rpcclient/notary/actor.go @@ -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) diff --git a/pkg/rpcclient/rpc.go b/pkg/rpcclient/rpc.go index 9e2c86264..112689bea 100644 --- a/pkg/rpcclient/rpc.go +++ b/pkg/rpcclient/rpc.go @@ -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) diff --git a/pkg/services/notary/core_test.go b/pkg/services/notary/core_test.go index 844aeab92..fe1c9e156 100644 --- a/pkg/services/notary/core_test.go +++ b/pkg/services/notary/core_test.go @@ -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]) diff --git a/pkg/services/notary/notary.go b/pkg/services/notary/notary.go index c626fb854..dc3d93a85 100644 --- a/pkg/services/notary/notary.go +++ b/pkg/services/notary/notary.go @@ -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) } } diff --git a/pkg/services/notary/notary_test.go b/pkg/services/notary/notary_test.go index d8375e0d7..d3d173e99 100644 --- a/pkg/services/notary/notary_test.go +++ b/pkg/services/notary/notary_test.go @@ -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() diff --git a/pkg/services/rpcsrv/client_test.go b/pkg/services/rpcsrv/client_test.go index 247506d5b..aaf4b31f1 100644 --- a/pkg/services/rpcsrv/client_test.go +++ b/pkg/services/rpcsrv/client_test.go @@ -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) diff --git a/pkg/services/rpcsrv/server_test.go b/pkg/services/rpcsrv/server_test.go index e0e2ee86a..93f015121 100644 --- a/pkg/services/rpcsrv/server_test.go +++ b/pkg/services/rpcsrv/server_test.go @@ -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 diff --git a/pkg/smartcontract/context/item_test.go b/pkg/smartcontract/context/item_test.go index 21108aa4d..767e510d4 100644 --- a/pkg/smartcontract/context/item_test.go +++ b/pkg/smartcontract/context/item_test.go @@ -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), }, } diff --git a/pkg/smartcontract/manifest/event.go b/pkg/smartcontract/manifest/event.go index 00a87e988..04a1f348d 100644 --- a/pkg/smartcontract/manifest/event.go +++ b/pkg/smartcontract/manifest/event.go @@ -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 +} diff --git a/pkg/smartcontract/manifest/event_test.go b/pkg/smartcontract/manifest/event_test.go index 2e50bdd46..65b32183a 100644 --- a/pkg/smartcontract/manifest/event_test.go +++ b/pkg/smartcontract/manifest/event_test.go @@ -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)})) +} diff --git a/pkg/smartcontract/param_type.go b/pkg/smartcontract/param_type.go index 5b36de52f..59d44ab04 100644 --- a/pkg/smartcontract/param_type.go +++ b/pkg/smartcontract/param_type.go @@ -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 diff --git a/pkg/smartcontract/param_type_test.go b/pkg/smartcontract/param_type_test.go index a9f52c11d..24ffa69fe 100644 --- a/pkg/smartcontract/param_type_test.go +++ b/pkg/smartcontract/param_type_test.go @@ -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()) + } +} diff --git a/pkg/wallet/account.go b/pkg/wallet/account.go index e1a7c30e3..4658d63f0 100644 --- a/pkg/wallet/account.go +++ b/pkg/wallet/account.go @@ -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 {