Skip to content

Commit 0742083

Browse files
interop: add keccak256 implementation
Port neo-project/neo#2925. Close #3295 Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
1 parent ef99a7a commit 0742083

File tree

4 files changed

+89
-0
lines changed

4 files changed

+89
-0
lines changed

pkg/compiler/native_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ func TestNativeHelpersCompile(t *testing.T) {
239239
{"bls12381Add", []string{"crypto.Bls12381Point{}", "crypto.Bls12381Point{}"}},
240240
{"bls12381Mul", []string{"crypto.Bls12381Point{}", "[]byte{1, 2, 3}", "true"}},
241241
{"bls12381Pairing", []string{"crypto.Bls12381Point{}", "crypto.Bls12381Point{}"}},
242+
{"keccak256", []string{"[]byte{1, 2, 3}"}},
242243
})
243244
runNativeTestCases(t, cs.Std.ContractMD, "std", []nativeTestCase{
244245
{"serialize", []string{"[]byte{1, 2, 3}"}},

pkg/core/native/crypto.go

+19
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
2121
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
2222
"github.com/twmb/murmur3"
23+
"golang.org/x/crypto/sha3"
2324
)
2425

2526
// Crypto represents CryptoLib contract.
@@ -101,6 +102,10 @@ func newCrypto() *Crypto {
101102
md = newMethodAndPrice(c.bls12381Pairing, 1<<23, callflag.NoneFlag)
102103
c.AddMethod(md, desc)
103104

105+
desc = newDescriptor("keccak256", smartcontract.ByteArrayType,
106+
manifest.NewParameter("data", smartcontract.ByteArrayType))
107+
md = newMethodAndPrice(c.sha256, 1<<15, callflag.NoneFlag)
108+
c.AddMethod(md, desc)
104109
return c
105110
}
106111

@@ -285,6 +290,20 @@ func (c *Crypto) bls12381Pairing(_ *interop.Context, args []stackitem.Item) stac
285290
return stackitem.NewInterop(p)
286291
}
287292

293+
func (c *Crypto) keccak256(_ *interop.Context, args []stackitem.Item) stackitem.Item {
294+
bs, err := args[0].TryBytes()
295+
if err != nil {
296+
panic(err)
297+
}
298+
299+
digest := sha3.NewLegacyKeccak256()
300+
_, err = digest.Write(bs)
301+
if err != nil {
302+
panic(err)
303+
}
304+
return stackitem.NewByteArray(digest.Sum(nil))
305+
}
306+
288307
// Metadata implements the Contract interface.
289308
func (c *Crypto) Metadata() *interop.ContractMD {
290309
return &c.ContractMD

pkg/core/native/crypto_test.go

+64
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,70 @@ func TestSha256(t *testing.T) {
2929
require.Equal(t, "47dc540c94ceb704a23875c11273e16bb0b8a87aed84de911f2133568115f254", hex.EncodeToString(c.sha256(ic, []stackitem.Item{stackitem.NewByteArray([]byte{1, 0})}).Value().([]byte)))
3030
})
3131
}
32+
func TestKeccak256(t *testing.T) {
33+
c := newCrypto()
34+
ic := &interop.Context{VM: vm.New()}
35+
36+
t.Run("bad arg type", func(t *testing.T) {
37+
require.Panics(t, func() {
38+
c.keccak256(ic, []stackitem.Item{stackitem.NewInterop(nil)})
39+
})
40+
})
41+
t.Run("good", func(t *testing.T) {
42+
// 0x0100 hashes to 628bf3596747d233f1e6533345700066bf458fa48daedaf04a7be6c392902476
43+
require.Equal(t, "628bf3596747d233f1e6533345700066bf458fa48daedaf04a7be6c392902476", hex.EncodeToString(c.keccak256(ic, []stackitem.Item{stackitem.NewByteArray([]byte{1, 0})}).Value().([]byte)))
44+
})
45+
46+
t.Run("hello world", func(t *testing.T) {
47+
inputData := []byte("Hello, World!")
48+
expectedHashHex := "acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f"
49+
50+
result := c.keccak256(ic, []stackitem.Item{stackitem.NewByteArray(inputData)}).Value().([]byte)
51+
outputHashHex := hex.EncodeToString(result)
52+
53+
require.Equal(t, expectedHashHex, outputHashHex)
54+
})
55+
56+
t.Run("cryptography", func(t *testing.T) {
57+
inputData := []byte("Cryptography")
58+
expectedHashHex := "53d49d225dd2cfe77d8c5e2112bcc9efe77bea1c7aa5e5ede5798a36e99e2d29"
59+
60+
result := c.keccak256(ic, []stackitem.Item{stackitem.NewByteArray(inputData)}).Value().([]byte)
61+
outputHashHex := hex.EncodeToString(result)
62+
63+
require.Equal(t, expectedHashHex, outputHashHex)
64+
})
65+
66+
t.Run("testing123", func(t *testing.T) {
67+
inputData := []byte("Testing123")
68+
expectedHashHex := "3f82db7b16b0818a1c6b2c6152e265f682d5ebcf497c9aad776ad38bc39cb6ca"
69+
70+
result := c.keccak256(ic, []stackitem.Item{stackitem.NewByteArray(inputData)}).Value().([]byte)
71+
outputHashHex := hex.EncodeToString(result)
72+
73+
require.Equal(t, expectedHashHex, outputHashHex)
74+
})
75+
76+
t.Run("long string", func(t *testing.T) {
77+
inputData := []byte("This is a longer string for Keccak256 testing purposes.")
78+
expectedHashHex := "24115e5c2359f85f6840b42acd2f7ea47bc239583e576d766fa173bf711bdd2f"
79+
80+
result := c.keccak256(ic, []stackitem.Item{stackitem.NewByteArray(inputData)}).Value().([]byte)
81+
outputHashHex := hex.EncodeToString(result)
82+
83+
require.Equal(t, expectedHashHex, outputHashHex)
84+
})
85+
86+
t.Run("blanc string", func(t *testing.T) {
87+
inputData := []byte("")
88+
expectedHashHex := "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
89+
90+
result := c.keccak256(ic, []stackitem.Item{stackitem.NewByteArray(inputData)}).Value().([]byte)
91+
outputHashHex := hex.EncodeToString(result)
92+
93+
require.Equal(t, expectedHashHex, outputHashHex)
94+
})
95+
}
3296

3397
func TestRIPEMD160(t *testing.T) {
3498
c := newCrypto()

pkg/interop/native/crypto/crypto.go

+5
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,8 @@ func Bls12381Mul(x Bls12381Point, mul []byte, neg bool) Bls12381Point {
9292
func Bls12381Pairing(g1, g2 Bls12381Point) Bls12381Point {
9393
return neogointernal.CallWithToken(Hash, "bls12381Pairing", int(contract.NoneFlag), g1, g2).(Bls12381Point)
9494
}
95+
96+
// Keccak256 calls `keccak256` method of native CryptoLib contract and computes Keccak256 hash of b.
97+
func Keccak256(b []byte) interop.Hash256 {
98+
return neogointernal.CallWithToken(Hash, "keccak256", int(contract.NoneFlag), b).(interop.Hash256)
99+
}

0 commit comments

Comments
 (0)