diff --git a/cli/wallet/multisig.go b/cli/wallet/multisig.go index 41d5b72e2..165f3079e 100644 --- a/cli/wallet/multisig.go +++ b/cli/wallet/multisig.go @@ -59,14 +59,16 @@ func signMultisig(ctx *cli.Context) error { if !ok { return cli.NewExitError("verifiable item is not a transaction", 1) } - fmt.Fprintln(ctx.App.Writer, tx.Hash().StringLE()) priv := acc.PrivateKey() sign := priv.Sign(tx.GetSignedPart()) if err := c.AddSignature(acc.Contract, priv.PublicKey(), sign); err != nil { return cli.NewExitError(fmt.Errorf("can't add signature: %w", err), 1) - } else if err := writeParameterContext(c, ctx.String("out")); err != nil { - return cli.NewExitError(err, 1) + } + if out := ctx.String("out"); out != "" { + if err := writeParameterContext(c, out); err != nil { + return cli.NewExitError(err, 1) + } } if len(ctx.String(options.RPCEndpointFlag)) != 0 { w, err := c.GetWitness(acc.Contract) diff --git a/pkg/consensus/consensus_test.go b/pkg/consensus/consensus_test.go index 6a34a1269..1d31b9089 100644 --- a/pkg/consensus/consensus_test.go +++ b/pkg/consensus/consensus_test.go @@ -9,6 +9,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core" + "github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/transaction" @@ -344,7 +345,7 @@ func signTx(t *testing.T, feePerByte int64, txs ...*transaction.Transaction) { require.NoError(t, err) for _, tx := range txs { size := io.GetVarSize(tx) - netFee, sizeDelta := core.CalculateNetworkFee(rawScript) + netFee, sizeDelta := fee.Calculate(rawScript) tx.NetworkFee += +netFee size += sizeDelta tx.NetworkFee += int64(size) * feePerByte diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index d3058776b..cfe5f0f11 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -9,6 +9,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core/block" + "github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/core/mempool" "github.com/nspcc-dev/neo-go/pkg/core/native" @@ -300,7 +301,7 @@ func TestVerifyTx(t *testing.T) { }) t.Run("AlmostEnoughNetworkFee", func(t *testing.T) { tx := bc.newTestTx(h, testScript) - verificationNetFee, calcultedScriptSize := CalculateNetworkFee(accs[0].Contract.Script) + verificationNetFee, calcultedScriptSize := fee.Calculate(accs[0].Contract.Script) expectedSize := io.GetVarSize(tx) + calcultedScriptSize calculatedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte() tx.NetworkFee = calculatedNetFee - 1 @@ -310,7 +311,7 @@ func TestVerifyTx(t *testing.T) { }) t.Run("EnoughNetworkFee", func(t *testing.T) { tx := bc.newTestTx(h, testScript) - verificationNetFee, calcultedScriptSize := CalculateNetworkFee(accs[0].Contract.Script) + verificationNetFee, calcultedScriptSize := fee.Calculate(accs[0].Contract.Script) expectedSize := io.GetVarSize(tx) + calcultedScriptSize calculatedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte() tx.NetworkFee = calculatedNetFee @@ -321,7 +322,7 @@ func TestVerifyTx(t *testing.T) { t.Run("CalculateNetworkFee, signature script", func(t *testing.T) { tx := bc.newTestTx(h, testScript) expectedSize := io.GetVarSize(tx) - verificationNetFee, calculatedScriptSize := CalculateNetworkFee(accs[0].Contract.Script) + verificationNetFee, calculatedScriptSize := fee.Calculate(accs[0].Contract.Script) expectedSize += calculatedScriptSize expectedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte() tx.NetworkFee = expectedNetFee @@ -340,7 +341,7 @@ func TestVerifyTx(t *testing.T) { require.NoError(t, multisigAcc.ConvertMultisig(1, pKeys)) multisigHash := hash.Hash160(multisigAcc.Contract.Script) tx := bc.newTestTx(multisigHash, testScript) - verificationNetFee, calculatedScriptSize := CalculateNetworkFee(multisigAcc.Contract.Script) + verificationNetFee, calculatedScriptSize := fee.Calculate(multisigAcc.Contract.Script) expectedSize := io.GetVarSize(tx) + calculatedScriptSize expectedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte() tx.NetworkFee = expectedNetFee @@ -440,7 +441,7 @@ func TestVerifyTx(t *testing.T) { rawScript := testchain.CommitteeVerificationScript() require.NoError(t, err) size := io.GetVarSize(tx) - netFee, sizeDelta := CalculateNetworkFee(rawScript) + netFee, sizeDelta := fee.Calculate(rawScript) tx.NetworkFee += netFee tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte() data := tx.GetSignedPart() @@ -479,7 +480,7 @@ func TestVerifyTx(t *testing.T) { Scopes: transaction.None, }} size := io.GetVarSize(tx) - netFee, sizeDelta := CalculateNetworkFee(oracleScript) + netFee, sizeDelta := fee.Calculate(oracleScript) tx.NetworkFee += netFee tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte() return tx diff --git a/pkg/core/fee/calculate.go b/pkg/core/fee/calculate.go new file mode 100644 index 000000000..38ad41a22 --- /dev/null +++ b/pkg/core/fee/calculate.go @@ -0,0 +1,39 @@ +package fee + +import ( + "github.com/nspcc-dev/neo-go/pkg/core/interop/crypto" + "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/vm/emit" + "github.com/nspcc-dev/neo-go/pkg/vm/opcode" +) + +// Calculate returns network fee for transaction +func Calculate(script []byte) (int64, int) { + var ( + netFee int64 + size int + ) + if vm.IsSignatureContract(script) { + size += 67 + io.GetVarSize(script) + netFee += Opcode(opcode.PUSHDATA1, opcode.PUSHNULL, opcode.PUSHDATA1) + crypto.ECDSAVerifyPrice + } else if m, pubs, ok := vm.ParseMultiSigContract(script); ok { + n := len(pubs) + sizeInv := 66 * m + size += io.GetVarSize(sizeInv) + sizeInv + io.GetVarSize(script) + netFee += calculateMultisig(m) + calculateMultisig(n) + netFee += Opcode(opcode.PUSHNULL) + crypto.ECDSAVerifyPrice*int64(n) + } else { + // We can support more contract types in the future. + } + return netFee, size +} + +func calculateMultisig(n int) int64 { + result := Opcode(opcode.PUSHDATA1) * int64(n) + bw := io.NewBufBinWriter() + emit.Int(bw.BinWriter, int64(n)) + // it's a hack because prices of small PUSH* opcodes are equal + result += Opcode(opcode.Opcode(bw.Bytes()[0])) + return result +} diff --git a/pkg/core/opcode_price.go b/pkg/core/fee/opcode.go similarity index 96% rename from pkg/core/opcode_price.go rename to pkg/core/fee/opcode.go index 848826f07..27fa8b745 100644 --- a/pkg/core/opcode_price.go +++ b/pkg/core/fee/opcode.go @@ -1,11 +1,9 @@ -package core +package fee -import ( - "github.com/nspcc-dev/neo-go/pkg/vm/opcode" -) +import "github.com/nspcc-dev/neo-go/pkg/vm/opcode" -// opcodePrice returns the deployment prices of specified opcodes -func opcodePrice(opcodes ...opcode.Opcode) int64 { +// Opcode returns the deployment prices of specified opcodes. +func Opcode(opcodes ...opcode.Opcode) int64 { var result int64 for _, op := range opcodes { result += prices[op] diff --git a/pkg/core/gas_price.go b/pkg/core/gas_price.go index 61657d379..e8641bc1a 100644 --- a/pkg/core/gas_price.go +++ b/pkg/core/gas_price.go @@ -1,6 +1,7 @@ package core import ( + "github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" ) @@ -10,5 +11,5 @@ const StoragePrice = 100000 // getPrice returns a price for executing op with the provided parameter. func getPrice(v *vm.VM, op opcode.Opcode, parameter []byte) int64 { - return opcodePrice(op) + return fee.Opcode(op) } diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 5dfe24c90..d0bdf4477 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -14,6 +14,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/core/block" + "github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/storage" @@ -407,7 +408,7 @@ func signTx(bc *Blockchain, txs ...*transaction.Transaction) error { } for _, tx := range txs { size := io.GetVarSize(tx) - netFee, sizeDelta := CalculateNetworkFee(rawScript) + netFee, sizeDelta := fee.Calculate(rawScript) tx.NetworkFee += netFee size += sizeDelta tx.NetworkFee += int64(size) * bc.FeePerByte() @@ -422,13 +423,13 @@ func signTx(bc *Blockchain, txs ...*transaction.Transaction) error { func addNetworkFee(bc *Blockchain, tx *transaction.Transaction, sender *wallet.Account) error { size := io.GetVarSize(tx) - netFee, sizeDelta := CalculateNetworkFee(sender.Contract.Script) + netFee, sizeDelta := fee.Calculate(sender.Contract.Script) tx.NetworkFee += netFee size += sizeDelta for _, cosigner := range tx.Signers { contract := bc.GetContractState(cosigner.Account) if contract != nil { - netFee, sizeDelta = CalculateNetworkFee(contract.Script) + netFee, sizeDelta = fee.Calculate(contract.Script) tx.NetworkFee += netFee size += sizeDelta } diff --git a/pkg/core/native/oracle.go b/pkg/core/native/oracle.go index 2a695bb9d..7ba340384 100644 --- a/pkg/core/native/oracle.go +++ b/pkg/core/native/oracle.go @@ -46,15 +46,19 @@ const ( oracleRequestPrice = 5000_0000 ) -var oracleScript []byte +var ( + oracleInvokeScript []byte + oracleScript []byte +) func init() { w := io.NewBufBinWriter() emit.String(w.BinWriter, oracleName) emit.Syscall(w.BinWriter, interopnames.NeoNativeCall) - h := hash.Hash160(w.Bytes()) + oracleInvokeScript = w.Bytes() + h := hash.Hash160(oracleInvokeScript) - w.Reset() + w = io.NewBufBinWriter() emit.Int(w.BinWriter, 0) emit.Opcodes(w.BinWriter, opcode.NEWARRAY) emit.String(w.BinWriter, "finish") @@ -79,6 +83,13 @@ var ( ErrResponseNotFound = errors.New("oracle response not found") ) +// GetOracleInvokeScript returns oracle contract script. +func GetOracleInvokeScript() []byte { + b := make([]byte, len(oracleInvokeScript)) + copy(b, oracleInvokeScript) + return b +} + // GetOracleResponseScript returns script for transaction with oracle response. func GetOracleResponseScript() []byte { b := make([]byte, len(oracleScript)) diff --git a/pkg/core/util.go b/pkg/core/util.go index 7cfc479de..05c1ce495 100644 --- a/pkg/core/util.go +++ b/pkg/core/util.go @@ -7,7 +7,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core/block" - "github.com/nspcc-dev/neo-go/pkg/core/interop/crypto" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" @@ -15,7 +14,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" ) @@ -129,33 +127,3 @@ func headerSliceReverse(dest []*block.Header) { dest[i], dest[j] = dest[j], dest[i] } } - -// CalculateNetworkFee returns network fee for transaction -func CalculateNetworkFee(script []byte) (int64, int) { - var ( - netFee int64 - size int - ) - if vm.IsSignatureContract(script) { - size += 67 + io.GetVarSize(script) - netFee += opcodePrice(opcode.PUSHDATA1, opcode.PUSHNULL, opcode.PUSHDATA1) + crypto.ECDSAVerifyPrice - } else if m, pubs, ok := vm.ParseMultiSigContract(script); ok { - n := len(pubs) - sizeInv := 66 * m - size += io.GetVarSize(sizeInv) + sizeInv + io.GetVarSize(script) - netFee += calculateMultisigFee(m) + calculateMultisigFee(n) - netFee += opcodePrice(opcode.PUSHNULL) + crypto.ECDSAVerifyPrice*int64(n) - } else { - // We can support more contract types in the future. - } - return netFee, size -} - -func calculateMultisigFee(n int) int64 { - result := opcodePrice(opcode.PUSHDATA1) * int64(n) - bw := io.NewBufBinWriter() - emit.Int(bw.BinWriter, int64(n)) - // it's a hack because prices of small PUSH* opcodes are equal - result += opcodePrice(opcode.Opcode(bw.Bytes()[0])) - return result -} diff --git a/pkg/crypto/keys/publickey.go b/pkg/crypto/keys/publickey.go index 0daa0431c..7eac8931f 100644 --- a/pkg/crypto/keys/publickey.go +++ b/pkg/crypto/keys/publickey.go @@ -333,7 +333,7 @@ func (p *PublicKey) Address() string { // Verify returns true if the signature is valid and corresponds // to the hash and public key. func (p *PublicKey) Verify(signature []byte, hash []byte) bool { - if p.X == nil || p.Y == nil { + if p.X == nil || p.Y == nil || len(signature) != 64 { return false } rBytes := new(big.Int).SetBytes(signature[0:32]) diff --git a/pkg/crypto/keys/sign_verify_test.go b/pkg/crypto/keys/sign_verify_test.go index 405a99c9d..7e0b8b217 100644 --- a/pkg/crypto/keys/sign_verify_test.go +++ b/pkg/crypto/keys/sign_verify_test.go @@ -52,6 +52,9 @@ func TestPubKeyVerify(t *testing.T) { expected := true assert.Equal(t, expected, result) + // Small signature, no panic. + assert.False(t, pubKey.Verify([]byte{1, 2, 3}, hashedData.BytesBE())) + pubKey = &PublicKey{} assert.False(t, pubKey.Verify(signedData, hashedData.BytesBE())) }) diff --git a/pkg/rpc/client/rpc.go b/pkg/rpc/client/rpc.go index fb6175c54..61b734cb2 100644 --- a/pkg/rpc/client/rpc.go +++ b/pkg/rpc/client/rpc.go @@ -7,6 +7,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/block" + "github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" @@ -552,7 +553,7 @@ func (c *Client) AddNetworkFee(tx *transaction.Transaction, extraFee int64, accs size += io.GetVarSize([]byte{}) * 2 // both scripts are empty continue } - netFee, sizeDelta := core.CalculateNetworkFee(accs[i].Contract.Script) + netFee, sizeDelta := fee.Calculate(accs[i].Contract.Script) tx.NetworkFee += netFee size += sizeDelta } diff --git a/pkg/rpc/server/client_test.go b/pkg/rpc/server/client_test.go index 9ad40480d..11f845ead 100644 --- a/pkg/rpc/server/client_test.go +++ b/pkg/rpc/server/client_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/nspcc-dev/neo-go/pkg/config/netmode" - "github.com/nspcc-dev/neo-go/pkg/core" + "github.com/nspcc-dev/neo-go/pkg/core/fee" "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" @@ -105,7 +105,7 @@ func TestAddNetworkFee(t *testing.T) { }} require.NoError(t, c.AddNetworkFee(tx, 10, accs[0])) require.NoError(t, accs[0].SignTx(tx)) - cFee, _ := core.CalculateNetworkFee(accs[0].Contract.Script) + cFee, _ := fee.Calculate(accs[0].Contract.Script) require.Equal(t, int64(io.GetVarSize(tx))*feePerByte+cFee+10, tx.NetworkFee) }) @@ -129,8 +129,8 @@ func TestAddNetworkFee(t *testing.T) { require.NoError(t, accs[0].SignTx(tx)) require.NoError(t, accs[1].SignTx(tx)) require.NoError(t, accs[2].SignTx(tx)) - cFee, _ := core.CalculateNetworkFee(accs[0].Contract.Script) - cFeeM, _ := core.CalculateNetworkFee(accs[1].Contract.Script) + cFee, _ := fee.Calculate(accs[0].Contract.Script) + cFeeM, _ := fee.Calculate(accs[1].Contract.Script) require.Equal(t, int64(io.GetVarSize(tx))*feePerByte+cFee+cFeeM+10, tx.NetworkFee) }) t.Run("Contract", func(t *testing.T) { diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 32d2ac9b3..e28863d85 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -19,6 +19,7 @@ import ( "github.com/gorilla/websocket" "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/block" + "github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/encoding/address" @@ -787,7 +788,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] addNetworkFee := func(tx *transaction.Transaction) { size := io.GetVarSize(tx) - netFee, sizeDelta := core.CalculateNetworkFee(acc0.Contract.Script) + netFee, sizeDelta := fee.Calculate(acc0.Contract.Script) tx.NetworkFee += netFee size += sizeDelta tx.NetworkFee += int64(size) * chain.FeePerByte()