forked from TrueCloudLab/neoneo-go
native: implement CryptoLib contract
This commit is contained in:
parent
19a23a36e4
commit
100f2db3fb
22 changed files with 240 additions and 172 deletions
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
"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/native"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/native/crypto"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/gas"
|
"github.com/nspcc-dev/neo-go/pkg/interop/native/gas"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
|
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
|
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
|
||||||
|
@ -37,6 +38,7 @@ func TestContractHashes(t *testing.T) {
|
||||||
require.Equal(t, []byte(ledger.Hash), cs.Ledger.Hash.BytesBE())
|
require.Equal(t, []byte(ledger.Hash), cs.Ledger.Hash.BytesBE())
|
||||||
require.Equal(t, []byte(management.Hash), cs.Management.Hash.BytesBE())
|
require.Equal(t, []byte(management.Hash), cs.Management.Hash.BytesBE())
|
||||||
require.Equal(t, []byte(notary.Hash), cs.Notary.Hash.BytesBE())
|
require.Equal(t, []byte(notary.Hash), cs.Notary.Hash.BytesBE())
|
||||||
|
require.Equal(t, []byte(crypto.Hash), cs.Crypto.Hash.BytesBE())
|
||||||
}
|
}
|
||||||
|
|
||||||
// testPrintHash is a helper for updating contract hashes.
|
// testPrintHash is a helper for updating contract hashes.
|
||||||
|
@ -77,6 +79,11 @@ func TestNameServiceRecordType(t *testing.T) {
|
||||||
require.EqualValues(t, native.RecordTypeAAAA, nameservice.TypeAAAA)
|
require.EqualValues(t, native.RecordTypeAAAA, nameservice.TypeAAAA)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCryptoLibNamedCurve(t *testing.T) {
|
||||||
|
require.EqualValues(t, native.Secp256k1, crypto.Secp256k1)
|
||||||
|
require.EqualValues(t, native.Secp256r1, crypto.Secp256r1)
|
||||||
|
}
|
||||||
|
|
||||||
type nativeTestCase struct {
|
type nativeTestCase struct {
|
||||||
method string
|
method string
|
||||||
params []string
|
params []string
|
||||||
|
@ -88,6 +95,7 @@ func TestNativeHelpersCompile(t *testing.T) {
|
||||||
u160 := `interop.Hash160("aaaaaaaaaaaaaaaaaaaa")`
|
u160 := `interop.Hash160("aaaaaaaaaaaaaaaaaaaa")`
|
||||||
u256 := `interop.Hash256("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")`
|
u256 := `interop.Hash256("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")`
|
||||||
pub := `interop.PublicKey("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")`
|
pub := `interop.PublicKey("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")`
|
||||||
|
sig := `interop.Signature("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")`
|
||||||
nep17TestCases := []nativeTestCase{
|
nep17TestCases := []nativeTestCase{
|
||||||
{"balanceOf", []string{u160}},
|
{"balanceOf", []string{u160}},
|
||||||
{"decimals", nil},
|
{"decimals", nil},
|
||||||
|
@ -176,6 +184,11 @@ func TestNativeHelpersCompile(t *testing.T) {
|
||||||
{"update", []string{"nil", "nil"}},
|
{"update", []string{"nil", "nil"}},
|
||||||
{"updateWithData", []string{"nil", "nil", "123"}},
|
{"updateWithData", []string{"nil", "nil", "123"}},
|
||||||
})
|
})
|
||||||
|
runNativeTestCases(t, cs.Crypto.ContractMD, "crypto", []nativeTestCase{
|
||||||
|
{"sha256", []string{"[]byte{1, 2, 3}"}},
|
||||||
|
{"ripemd160", []string{"[]byte{1, 2, 3}"}},
|
||||||
|
{"verifyWithECDsa", []string{"[]byte{1, 2, 3}", pub, sig, "crypto.Secp256k1"}},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func runNativeTestCases(t *testing.T, ctr interop.ContractMD, name string, testCases []nativeTestCase) {
|
func runNativeTestCases(t *testing.T, ctr interop.ContractMD, name string, testCases []nativeTestCase) {
|
||||||
|
|
|
@ -103,8 +103,6 @@ func TestSyscallExecution(t *testing.T) {
|
||||||
"crypto.ECDsaSecp256k1Verify": {interopnames.NeoCryptoVerifyWithECDsaSecp256k1, []string{b, pub, sig}, false},
|
"crypto.ECDsaSecp256k1Verify": {interopnames.NeoCryptoVerifyWithECDsaSecp256k1, []string{b, pub, sig}, false},
|
||||||
"crypto.ECDSASecp256r1CheckMultisig": {interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1, []string{b, pubs, sigs}, false},
|
"crypto.ECDSASecp256r1CheckMultisig": {interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1, []string{b, pubs, sigs}, false},
|
||||||
"crypto.ECDSASecp256k1CheckMultisig": {interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1, []string{b, pubs, sigs}, false},
|
"crypto.ECDSASecp256k1CheckMultisig": {interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1, []string{b, pubs, sigs}, false},
|
||||||
"crypto.SHA256": {interopnames.NeoCryptoSHA256, []string{b}, false},
|
|
||||||
"crypto.RIPEMD160": {interopnames.NeoCryptoRIPEMD160, []string{b}, false},
|
|
||||||
}
|
}
|
||||||
ic := &interop.Context{}
|
ic := &interop.Context{}
|
||||||
core.SpawnVM(ic) // set Functions field
|
core.SpawnVM(ic) // set Functions field
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
package compiler_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/crypto"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSHA256(t *testing.T) {
|
|
||||||
src := `
|
|
||||||
package foo
|
|
||||||
import (
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
|
||||||
)
|
|
||||||
func Main() []byte {
|
|
||||||
src := []byte{0x97}
|
|
||||||
hash := crypto.SHA256(src)
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
`
|
|
||||||
v := vmAndCompile(t, src)
|
|
||||||
ic := &interop.Context{Trigger: trigger.Verification}
|
|
||||||
ic.VM = v
|
|
||||||
crypto.Register(ic)
|
|
||||||
v.SyscallHandler = ic.SyscallHandler
|
|
||||||
require.NoError(t, v.Run())
|
|
||||||
require.True(t, v.Estack().Len() >= 1)
|
|
||||||
|
|
||||||
h := []byte{0x2a, 0xa, 0xb7, 0x32, 0xb4, 0xe9, 0xd8, 0x5e, 0xf7, 0xdc, 0x25, 0x30, 0x3b, 0x64, 0xab, 0x52, 0x7c, 0x25, 0xa4, 0xd7, 0x78, 0x15, 0xeb, 0xb5, 0x79, 0xf3, 0x96, 0xec, 0x6c, 0xac, 0xca, 0xd3}
|
|
||||||
require.Equal(t, h, v.PopResult())
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
package crypto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Sha256 returns sha256 hash of the data.
|
|
||||||
func Sha256(ic *interop.Context) error {
|
|
||||||
h, err := getMessageHash(ic, ic.VM.Estack().Pop().Item())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ic.VM.Estack().PushVal(h.BytesBE())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RipeMD160 returns RipeMD160 hash of the data.
|
|
||||||
func RipeMD160(ic *interop.Context) error {
|
|
||||||
var msg []byte
|
|
||||||
|
|
||||||
item := ic.VM.Estack().Pop().Item()
|
|
||||||
switch val := item.(type) {
|
|
||||||
case *stackitem.Interop:
|
|
||||||
msg = val.Value().(crypto.Verifiable).GetSignedPart()
|
|
||||||
case stackitem.Null:
|
|
||||||
msg = ic.Container.GetSignedPart()
|
|
||||||
default:
|
|
||||||
var err error
|
|
||||||
if msg, err = val.TryBytes(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h := hash.RipeMD160(msg).BytesBE()
|
|
||||||
ic.VM.Estack().PushVal(h)
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
package crypto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
type testVerifiable []byte
|
|
||||||
|
|
||||||
var _ crypto.Verifiable = testVerifiable{}
|
|
||||||
|
|
||||||
func (v testVerifiable) GetSignedPart() []byte {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
func (v testVerifiable) GetSignedHash() util.Uint256 {
|
|
||||||
return hash.Sha256(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testHash0100(t *testing.T, result string, interopFunc func(*interop.Context) error) {
|
|
||||||
t.Run("good", func(t *testing.T) {
|
|
||||||
bs := []byte{1, 0}
|
|
||||||
|
|
||||||
checkGood := func(t *testing.T, ic *interop.Context) {
|
|
||||||
require.NoError(t, interopFunc(ic))
|
|
||||||
require.Equal(t, 1, ic.VM.Estack().Len())
|
|
||||||
require.Equal(t, result, hex.EncodeToString(ic.VM.Estack().Pop().Bytes()))
|
|
||||||
}
|
|
||||||
t.Run("raw bytes", func(t *testing.T) {
|
|
||||||
ic := &interop.Context{VM: vm.New()}
|
|
||||||
ic.VM.Estack().PushVal(bs)
|
|
||||||
checkGood(t, ic)
|
|
||||||
})
|
|
||||||
t.Run("interop", func(t *testing.T) {
|
|
||||||
ic := &interop.Context{VM: vm.New()}
|
|
||||||
ic.VM.Estack().PushVal(stackitem.NewInterop(testVerifiable(bs)))
|
|
||||||
checkGood(t, ic)
|
|
||||||
})
|
|
||||||
t.Run("container", func(t *testing.T) {
|
|
||||||
ic := &interop.Context{VM: vm.New(), Container: testVerifiable(bs)}
|
|
||||||
ic.VM.Estack().PushVal(stackitem.Null{})
|
|
||||||
checkGood(t, ic)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
t.Run("bad message", func(t *testing.T) {
|
|
||||||
ic := &interop.Context{VM: vm.New()}
|
|
||||||
ic.VM.Estack().PushVal(stackitem.NewArray(nil))
|
|
||||||
require.Error(t, interopFunc(ic))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSHA256(t *testing.T) {
|
|
||||||
// 0x0100 hashes to 47dc540c94ceb704a23875c11273e16bb0b8a87aed84de911f2133568115f254
|
|
||||||
res := "47dc540c94ceb704a23875c11273e16bb0b8a87aed84de911f2133568115f254"
|
|
||||||
testHash0100(t, res, Sha256)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRIPEMD160(t *testing.T) {
|
|
||||||
// 0x0100 hashes to 213492c0c6fc5d61497cf17249dd31cd9964b8a3
|
|
||||||
res := "213492c0c6fc5d61497cf17249dd31cd9964b8a3"
|
|
||||||
testHash0100(t, res, RipeMD160)
|
|
||||||
}
|
|
|
@ -10,8 +10,6 @@ var (
|
||||||
ecdsaSecp256k1VerifyID = interopnames.ToID([]byte(interopnames.NeoCryptoVerifyWithECDsaSecp256k1))
|
ecdsaSecp256k1VerifyID = interopnames.ToID([]byte(interopnames.NeoCryptoVerifyWithECDsaSecp256k1))
|
||||||
ecdsaSecp256r1CheckMultisigID = interopnames.ToID([]byte(interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1))
|
ecdsaSecp256r1CheckMultisigID = interopnames.ToID([]byte(interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1))
|
||||||
ecdsaSecp256k1CheckMultisigID = interopnames.ToID([]byte(interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1))
|
ecdsaSecp256k1CheckMultisigID = interopnames.ToID([]byte(interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1))
|
||||||
sha256ID = interopnames.ToID([]byte(interopnames.NeoCryptoSHA256))
|
|
||||||
ripemd160ID = interopnames.ToID([]byte(interopnames.NeoCryptoRIPEMD160))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var cryptoInterops = []interop.Function{
|
var cryptoInterops = []interop.Function{
|
||||||
|
@ -19,8 +17,6 @@ var cryptoInterops = []interop.Function{
|
||||||
{ID: ecdsaSecp256k1VerifyID, Func: ECDSASecp256k1Verify},
|
{ID: ecdsaSecp256k1VerifyID, Func: ECDSASecp256k1Verify},
|
||||||
{ID: ecdsaSecp256r1CheckMultisigID, Func: ECDSASecp256r1CheckMultisig},
|
{ID: ecdsaSecp256r1CheckMultisigID, Func: ECDSASecp256r1CheckMultisig},
|
||||||
{ID: ecdsaSecp256k1CheckMultisigID, Func: ECDSASecp256k1CheckMultisig},
|
{ID: ecdsaSecp256k1CheckMultisigID, Func: ECDSASecp256k1CheckMultisig},
|
||||||
{ID: sha256ID, Func: Sha256},
|
|
||||||
{ID: ripemd160ID, Func: RipeMD160},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -51,8 +51,6 @@ const (
|
||||||
NeoCryptoVerifyWithECDsaSecp256k1 = "Neo.Crypto.VerifyWithECDsaSecp256k1"
|
NeoCryptoVerifyWithECDsaSecp256k1 = "Neo.Crypto.VerifyWithECDsaSecp256k1"
|
||||||
NeoCryptoCheckMultisigWithECDsaSecp256r1 = "Neo.Crypto.CheckMultisigWithECDsaSecp256r1"
|
NeoCryptoCheckMultisigWithECDsaSecp256r1 = "Neo.Crypto.CheckMultisigWithECDsaSecp256r1"
|
||||||
NeoCryptoCheckMultisigWithECDsaSecp256k1 = "Neo.Crypto.CheckMultisigWithECDsaSecp256k1"
|
NeoCryptoCheckMultisigWithECDsaSecp256k1 = "Neo.Crypto.CheckMultisigWithECDsaSecp256k1"
|
||||||
NeoCryptoSHA256 = "Neo.Crypto.SHA256"
|
|
||||||
NeoCryptoRIPEMD160 = "Neo.Crypto.RIPEMD160"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var names = []string{
|
var names = []string{
|
||||||
|
@ -105,6 +103,4 @@ var names = []string{
|
||||||
NeoCryptoVerifyWithECDsaSecp256k1,
|
NeoCryptoVerifyWithECDsaSecp256k1,
|
||||||
NeoCryptoCheckMultisigWithECDsaSecp256r1,
|
NeoCryptoCheckMultisigWithECDsaSecp256r1,
|
||||||
NeoCryptoCheckMultisigWithECDsaSecp256k1,
|
NeoCryptoCheckMultisigWithECDsaSecp256k1,
|
||||||
NeoCryptoSHA256,
|
|
||||||
NeoCryptoRIPEMD160,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,8 +93,6 @@ var neoInterops = []interop.Function{
|
||||||
Price: fee.ECDSAVerifyPrice, ParamCount: 3},
|
Price: fee.ECDSAVerifyPrice, ParamCount: 3},
|
||||||
{Name: interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1, Func: crypto.ECDSASecp256r1CheckMultisig, Price: 0, ParamCount: 3},
|
{Name: interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1, Func: crypto.ECDSASecp256r1CheckMultisig, Price: 0, ParamCount: 3},
|
||||||
{Name: interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1, Func: crypto.ECDSASecp256k1CheckMultisig, Price: 0, ParamCount: 3},
|
{Name: interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1, Func: crypto.ECDSASecp256k1CheckMultisig, Price: 0, ParamCount: 3},
|
||||||
{Name: interopnames.NeoCryptoSHA256, Func: crypto.Sha256, Price: 1 << 15, ParamCount: 1},
|
|
||||||
{Name: interopnames.NeoCryptoRIPEMD160, Func: crypto.RipeMD160, Price: 1 << 15, ParamCount: 1},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initIDinInteropsSlice initializes IDs from names in one given
|
// initIDinInteropsSlice initializes IDs from names in one given
|
||||||
|
|
|
@ -24,6 +24,7 @@ type Contracts struct {
|
||||||
Designate *Designate
|
Designate *Designate
|
||||||
NameService *NameService
|
NameService *NameService
|
||||||
Notary *Notary
|
Notary *Notary
|
||||||
|
Crypto *Crypto
|
||||||
Contracts []interop.Contract
|
Contracts []interop.Contract
|
||||||
// persistScript is vm script which executes "onPersist" method of every native contract.
|
// persistScript is vm script which executes "onPersist" method of every native contract.
|
||||||
persistScript []byte
|
persistScript []byte
|
||||||
|
@ -61,6 +62,10 @@ func NewContracts(p2pSigExtensionsEnabled bool) *Contracts {
|
||||||
cs.Management = mgmt
|
cs.Management = mgmt
|
||||||
cs.Contracts = append(cs.Contracts, mgmt)
|
cs.Contracts = append(cs.Contracts, mgmt)
|
||||||
|
|
||||||
|
c := newCrypto()
|
||||||
|
cs.Crypto = c
|
||||||
|
cs.Contracts = append(cs.Contracts, c)
|
||||||
|
|
||||||
ledger := newLedger()
|
ledger := newLedger()
|
||||||
cs.Ledger = ledger
|
cs.Ledger = ledger
|
||||||
cs.Contracts = append(cs.Contracts, ledger)
|
cs.Contracts = append(cs.Contracts, ledger)
|
||||||
|
|
138
pkg/core/native/crypto.go
Normal file
138
pkg/core/native/crypto.go
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
package native
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/elliptic"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcec"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
|
"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/smartcontract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Crypto represents CryptoLib contract.
|
||||||
|
type Crypto struct {
|
||||||
|
interop.ContractMD
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamedCurve identifies named elliptic curves.
|
||||||
|
type NamedCurve byte
|
||||||
|
|
||||||
|
// Various named elliptic curves.
|
||||||
|
const (
|
||||||
|
Secp256k1 NamedCurve = 22
|
||||||
|
Secp256r1 NamedCurve = 23
|
||||||
|
)
|
||||||
|
|
||||||
|
const cryptoContractID = -3
|
||||||
|
|
||||||
|
func newCrypto() *Crypto {
|
||||||
|
c := &Crypto{ContractMD: *interop.NewContractMD(nativenames.CryptoLib, cryptoContractID)}
|
||||||
|
defer c.UpdateHash()
|
||||||
|
|
||||||
|
desc := newDescriptor("sha256", smartcontract.ByteArrayType,
|
||||||
|
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
||||||
|
md := newMethodAndPrice(c.sha256, 1<<15, callflag.NoneFlag)
|
||||||
|
c.AddMethod(md, desc)
|
||||||
|
|
||||||
|
desc = newDescriptor("ripemd160", smartcontract.ByteArrayType,
|
||||||
|
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
||||||
|
md = newMethodAndPrice(c.ripemd160, 1<<15, callflag.NoneFlag)
|
||||||
|
c.AddMethod(md, desc)
|
||||||
|
|
||||||
|
desc = newDescriptor("verifyWithECDsa", smartcontract.BoolType,
|
||||||
|
manifest.NewParameter("message", smartcontract.ByteArrayType),
|
||||||
|
manifest.NewParameter("pubkey", smartcontract.ByteArrayType),
|
||||||
|
manifest.NewParameter("signature", smartcontract.ByteArrayType),
|
||||||
|
manifest.NewParameter("curve", smartcontract.IntegerType))
|
||||||
|
md = newMethodAndPrice(c.verifyWithECDsa, 1<<15, callflag.NoneFlag)
|
||||||
|
c.AddMethod(md, desc)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Crypto) sha256(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
|
bs, err := args[0].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return stackitem.NewByteArray(hash.Sha256(bs).BytesBE())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Crypto) ripemd160(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
|
bs, err := args[0].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return stackitem.NewByteArray(hash.RipeMD160(bs).BytesBE())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Crypto) verifyWithECDsa(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
|
msg, err := args[0].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("invalid message stackitem: %w", err))
|
||||||
|
}
|
||||||
|
hashToCheck := hash.Sha256(msg)
|
||||||
|
pubkey, err := args[1].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("invalid pubkey stackitem: %w", err))
|
||||||
|
}
|
||||||
|
signature, err := args[2].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("invalid signature stackitem: %w", err))
|
||||||
|
}
|
||||||
|
curve, err := curveFromStackitem(args[3])
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("invalid curve stackitem: %w", err))
|
||||||
|
}
|
||||||
|
pkey, err := keys.NewPublicKeyFromBytes(pubkey, curve)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("failed to decode pubkey: %w", err))
|
||||||
|
}
|
||||||
|
res := pkey.Verify(signature, hashToCheck.BytesBE())
|
||||||
|
return stackitem.NewBool(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func curveFromStackitem(si stackitem.Item) (elliptic.Curve, error) {
|
||||||
|
curve, err := si.TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !curve.IsInt64() {
|
||||||
|
return nil, errors.New("not an int64")
|
||||||
|
}
|
||||||
|
c := curve.Int64()
|
||||||
|
switch c {
|
||||||
|
case int64(Secp256k1):
|
||||||
|
return btcec.S256(), nil
|
||||||
|
case int64(Secp256r1):
|
||||||
|
return elliptic.P256(), nil
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unsupported curve type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata implements Contract interface.
|
||||||
|
func (c *Crypto) Metadata() *interop.ContractMD {
|
||||||
|
return &c.ContractMD
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize implements Contract interface.
|
||||||
|
func (c *Crypto) Initialize(ic *interop.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnPersist implements Contract interface.
|
||||||
|
func (c *Crypto) OnPersist(ic *interop.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostPersist implements Contract interface.
|
||||||
|
func (c *Crypto) PostPersist(ic *interop.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
41
pkg/core/native/crypto_test.go
Normal file
41
pkg/core/native/crypto_test.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package native
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSha256(t *testing.T) {
|
||||||
|
c := newCrypto()
|
||||||
|
ic := &interop.Context{VM: vm.New()}
|
||||||
|
|
||||||
|
t.Run("bad arg type", func(t *testing.T) {
|
||||||
|
require.Panics(t, func() {
|
||||||
|
c.sha256(ic, []stackitem.Item{stackitem.NewInterop(nil)})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("good", func(t *testing.T) {
|
||||||
|
// 0x0100 hashes to 47dc540c94ceb704a23875c11273e16bb0b8a87aed84de911f2133568115f254
|
||||||
|
require.Equal(t, "47dc540c94ceb704a23875c11273e16bb0b8a87aed84de911f2133568115f254", hex.EncodeToString(c.sha256(ic, []stackitem.Item{stackitem.NewByteArray([]byte{1, 0})}).Value().([]byte)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRIPEMD160(t *testing.T) {
|
||||||
|
c := newCrypto()
|
||||||
|
ic := &interop.Context{VM: vm.New()}
|
||||||
|
|
||||||
|
t.Run("bad arg type", func(t *testing.T) {
|
||||||
|
require.Panics(t, func() {
|
||||||
|
c.ripemd160(ic, []stackitem.Item{stackitem.NewInterop(nil)})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("good", func(t *testing.T) {
|
||||||
|
// 0x0100 hashes to 213492c0c6fc5d61497cf17249dd31cd9964b8a3
|
||||||
|
require.Equal(t, "213492c0c6fc5d61497cf17249dd31cd9964b8a3", hex.EncodeToString(c.ripemd160(ic, []stackitem.Item{stackitem.NewByteArray([]byte{1, 0})}).Value().([]byte)))
|
||||||
|
})
|
||||||
|
}
|
|
@ -52,7 +52,7 @@ type roleData struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
designateContractID = -6
|
designateContractID = -8
|
||||||
|
|
||||||
// maxNodeCount is the maximum number of nodes to set the role for.
|
// maxNodeCount is the maximum number of nodes to set the role for.
|
||||||
maxNodeCount = 32
|
maxNodeCount = 32
|
||||||
|
|
|
@ -26,7 +26,7 @@ type Ledger struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ledgerContractID = -2
|
ledgerContractID = -4
|
||||||
|
|
||||||
prefixBlockHash = 9
|
prefixBlockHash = 9
|
||||||
prefixCurrentBlock = 12
|
prefixCurrentBlock = 12
|
||||||
|
|
|
@ -54,7 +54,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
nameServiceID = -8
|
nameServiceID = -10
|
||||||
|
|
||||||
prefixRoots = 10
|
prefixRoots = 10
|
||||||
prefixDomainPrice = 22
|
prefixDomainPrice = 22
|
||||||
|
|
|
@ -18,7 +18,7 @@ type GAS struct {
|
||||||
NEO *NEO
|
NEO *NEO
|
||||||
}
|
}
|
||||||
|
|
||||||
const gasContractID = -4
|
const gasContractID = -6
|
||||||
|
|
||||||
// GASFactor is a divisor for finding GAS integral value.
|
// GASFactor is a divisor for finding GAS integral value.
|
||||||
const GASFactor = NEOTotalSupply
|
const GASFactor = NEOTotalSupply
|
||||||
|
|
|
@ -50,7 +50,7 @@ type NEO struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
neoContractID = -3
|
neoContractID = -5
|
||||||
// NEOTotalSupply is the total amount of NEO in the system.
|
// NEOTotalSupply is the total amount of NEO in the system.
|
||||||
NEOTotalSupply = 100000000
|
NEOTotalSupply = 100000000
|
||||||
// prefixCandidate is a prefix used to store validator's data.
|
// prefixCandidate is a prefix used to store validator's data.
|
||||||
|
|
|
@ -11,4 +11,5 @@ const (
|
||||||
Designation = "RoleManagement"
|
Designation = "RoleManagement"
|
||||||
Notary = "Notary"
|
Notary = "Notary"
|
||||||
NameService = "NameService"
|
NameService = "NameService"
|
||||||
|
CryptoLib = "CryptoLib"
|
||||||
)
|
)
|
||||||
|
|
|
@ -47,7 +47,7 @@ type Oracle struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
oracleContractID = -7
|
oracleContractID = -9
|
||||||
maxURLLength = 256
|
maxURLLength = 256
|
||||||
maxFilterLength = 128
|
maxFilterLength = 128
|
||||||
maxCallbackLength = 32
|
maxCallbackLength = 32
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
policyContractID = -5
|
policyContractID = -7
|
||||||
|
|
||||||
defaultExecFeeFactor = interop.DefaultBaseExecFee
|
defaultExecFeeFactor = interop.DefaultBaseExecFee
|
||||||
defaultFeePerByte = 1000
|
defaultFeePerByte = 1000
|
||||||
|
|
|
@ -8,16 +8,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/neogointernal"
|
"github.com/nspcc-dev/neo-go/pkg/interop/neogointernal"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SHA256 computes SHA256 hash of b. It uses `Neo.Crypto.SHA256` syscall.
|
|
||||||
func SHA256(b []byte) interop.Hash256 {
|
|
||||||
return neogointernal.Syscall1("Neo.Crypto.SHA256", b).(interop.Hash256)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RIPEMD160 computes RIPEMD160 hash of b. It uses `Neo.Crypto.RIPEMD160` syscall.
|
|
||||||
func RIPEMD160(b []byte) interop.Hash160 {
|
|
||||||
return neogointernal.Syscall1("Neo.Crypto.RIPEMD160", b).(interop.Hash160)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ECDsaSecp256r1Verify checks that sig is correct msg's signature for a given pub
|
// ECDsaSecp256r1Verify checks that sig is correct msg's signature for a given pub
|
||||||
// (serialized public key). It uses `Neo.Crypto.VerifyWithECDsaSecp256r1` syscall.
|
// (serialized public key). It uses `Neo.Crypto.VerifyWithECDsaSecp256r1` syscall.
|
||||||
func ECDsaSecp256r1Verify(msg []byte, pub interop.PublicKey, sig interop.Signature) bool {
|
func ECDsaSecp256r1Verify(msg []byte, pub interop.PublicKey, sig interop.Signature) bool {
|
||||||
|
|
34
pkg/interop/native/crypto/crypto.go
Normal file
34
pkg/interop/native/crypto/crypto.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hash represents CryptoLib contract hash.
|
||||||
|
const Hash = "\x1b\xf5\x75\xab\x11\x89\x68\x84\x13\x61\x0a\x35\xa1\x28\x86\xcd\xe0\xb6\x6c\x72"
|
||||||
|
|
||||||
|
// NamedCurve represents named elliptic curve.
|
||||||
|
type NamedCurve byte
|
||||||
|
|
||||||
|
// Various named elliptic curves.
|
||||||
|
const (
|
||||||
|
Secp256k1 NamedCurve = 22
|
||||||
|
Secp256r1 NamedCurve = 23
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sha256 calls `sha256` method of native CryptoLib contract and computes SHA256 hash of b.
|
||||||
|
func Sha256(b []byte) interop.Hash256 {
|
||||||
|
return contract.Call(interop.Hash160(Hash), "sha256", contract.NoneFlag, b).(interop.Hash256)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ripemd160 calls `ripemd160` method of native CryptoLib contract and computes RIPEMD160 hash of b.
|
||||||
|
func Ripemd160(b []byte) interop.Hash160 {
|
||||||
|
return contract.Call(interop.Hash160(Hash), "ripemd160", contract.NoneFlag, b).(interop.Hash160)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyWithECDsa calls `verifyWithECDsa` method of native CryptoLib contract and checks that sig is
|
||||||
|
// correct msg's signature for a given pub (serialized public key on a given curve).
|
||||||
|
func VerifyWithECDsa(msg []byte, pub interop.PublicKey, sig interop.Signature, curve NamedCurve) bool {
|
||||||
|
return contract.Call(interop.Hash160(Hash), "verifyWithECDsa", contract.NoneFlag, msg, pub, sig, curve).(bool)
|
||||||
|
}
|
|
@ -183,7 +183,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
check: func(t *testing.T, e *executor, cs interface{}) {
|
check: func(t *testing.T, e *executor, cs interface{}) {
|
||||||
res, ok := cs.(*state.Contract)
|
res, ok := cs.(*state.Contract)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, int32(-5), res.ID)
|
assert.Equal(t, int32(-7), res.ID)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue