Skip to content

Commit

Permalink
Merge pull request #1692 from mesg-foundation/feature/simple-hash
Browse files Browse the repository at this point in the history
Custom hash serializer
  • Loading branch information
Nicolas Mahé authored Mar 6, 2020
2 parents 6c563e0 + aef551a commit ca26c07
Show file tree
Hide file tree
Showing 30 changed files with 1,344 additions and 654 deletions.
3 changes: 2 additions & 1 deletion cosmos/module_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
executionpb "github.com/mesg-foundation/engine/execution"
"github.com/mesg-foundation/engine/ext/xos"
"github.com/mesg-foundation/engine/hash"
"github.com/mesg-foundation/engine/hash/hashserializer"
instancepb "github.com/mesg-foundation/engine/instance"
ownershippb "github.com/mesg-foundation/engine/ownership"
processpb "github.com/mesg-foundation/engine/process"
Expand Down Expand Up @@ -223,7 +224,7 @@ func (mc *ModuleClient) CreateRunner(req *api.CreateRunnerRequest) (*runnerpb.Ru
if err != nil {
return nil, err
}
envHash := hash.Dump(xos.EnvMergeSlices(s.Configuration.Env, req.Env))
envHash := hash.Dump(hashserializer.StringSlice(xos.EnvMergeSlices(s.Configuration.Env, req.Env)))
acc, err := mc.GetAccount()
if err != nil {
return nil, err
Expand Down
5 changes: 3 additions & 2 deletions e2e/instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"

"github.com/mesg-foundation/engine/hash"
"github.com/mesg-foundation/engine/hash/hashserializer"
"github.com/mesg-foundation/engine/instance"
pb "github.com/mesg-foundation/engine/protobuf/api"
"github.com/stretchr/testify/require"
Expand All @@ -19,14 +20,14 @@ func testInstance(t *testing.T) {
require.NoError(t, err)
require.Equal(t, testInstanceHash, resp.Hash)
require.Equal(t, testServiceHash, resp.ServiceHash)
require.Equal(t, hash.Dump([]string{"BAR=3", "FOO=1", "REQUIRED=4"}), resp.EnvHash)
require.Equal(t, hash.Dump(hashserializer.StringSlice([]string{"BAR=3", "FOO=1", "REQUIRED=4"})), resp.EnvHash)
})
t.Run("lcd", func(t *testing.T) {
var inst *instance.Instance
lcdGet(t, "instance/get/"+testInstanceHash.String(), &inst)
require.Equal(t, testInstanceHash, inst.Hash)
require.Equal(t, testServiceHash, inst.ServiceHash)
require.Equal(t, hash.Dump([]string{"BAR=3", "FOO=1", "REQUIRED=4"}), inst.EnvHash)
require.Equal(t, hash.Dump(hashserializer.StringSlice([]string{"BAR=3", "FOO=1", "REQUIRED=4"})), inst.EnvHash)
})
})

Expand Down
15 changes: 15 additions & 0 deletions event/serialize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package event

import "github.com/mesg-foundation/engine/hash/hashserializer"

// HashSerialize returns the hashserialized string of this type
func (data *Event) HashSerialize() string {
if data == nil {
return ""
}
return hashserializer.New().
AddString("2", data.InstanceHash.String()).
AddString("3", data.Key).
Add("4", data.Data).
HashSerialize()
}
32 changes: 32 additions & 0 deletions event/serialize_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package event

import (
"testing"

"github.com/mesg-foundation/engine/hash"
"github.com/mesg-foundation/engine/protobuf/types"
"github.com/stretchr/testify/require"
)

var data = &Event{
Key: "eventKey",
InstanceHash: hash.Int(10),
Data: &types.Struct{
Fields: map[string]*types.Value{
"foo": {
Kind: &types.Value_StringValue{StringValue: "bar"},
},
},
},
}

func TestHashSerialize(t *testing.T) {
require.Equal(t, "2:g35TxFqwMx95vCk63fTxGTHb6ei4W24qg5t2x6xD3cT;3:eventKey;4:1:foo:3:bar;;;;", data.HashSerialize())
require.Equal(t, "CGQ1DWeSsf13BDovLNb9zHXTVMUPNcTWY4DaSAFNN88T", hash.Dump(data).String())
}

func BenchmarkHashSerialize(b *testing.B) {
for i := 0; i < b.N; i++ {
data.HashSerialize()
}
}
19 changes: 19 additions & 0 deletions execution/serialize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package execution

import "github.com/mesg-foundation/engine/hash/hashserializer"

// HashSerialize returns the hashserialized string of this type
func (data *Execution) HashSerialize() string {
return hashserializer.New().
AddString("2", data.ParentHash.String()).
AddString("3", data.EventHash.String()).
AddString("5", data.InstanceHash.String()).
AddString("6", data.TaskKey).
Add("7", data.Inputs).
AddStringSlice("10", data.Tags).
AddString("11", data.ProcessHash.String()).
AddString("12", data.NodeKey).
AddString("13", data.ExecutorHash.String()).
AddString("14", data.Price).
HashSerialize()
}
77 changes: 77 additions & 0 deletions execution/serialize_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package execution

import (
"testing"

"github.com/mesg-foundation/engine/hash"
"github.com/mesg-foundation/engine/protobuf/types"
"github.com/stretchr/testify/require"
)

var instanceHash, _ = hash.Decode("5M8pQvBCPYwzwxe2bZUbV2g8bSgpsotp441xYvVBNMhd")
var exec = New(nil, instanceHash, nil, nil, "", "taskKey", "", &types.Struct{
Fields: map[string]*types.Value{
"a": {
Kind: &types.Value_StringValue{
StringValue: "b",
},
},
"b": {
Kind: &types.Value_NumberValue{
NumberValue: 3.14159265359,
},
},
"c": {
Kind: &types.Value_BoolValue{
BoolValue: true,
},
},
"d": {
Kind: &types.Value_ListValue{
ListValue: &types.ListValue{
Values: []*types.Value{
{
Kind: &types.Value_NullValue{
NullValue: types.NullValue_NULL_VALUE,
},
},
{
Kind: &types.Value_StringValue{
StringValue: "hello",
},
},
},
},
},
},
"e": {
Kind: &types.Value_NullValue{
NullValue: types.NullValue_NULL_VALUE,
},
},
"f": {
Kind: &types.Value_StructValue{
StructValue: &types.Struct{
Fields: map[string]*types.Value{
"a": {
Kind: &types.Value_StringValue{
StringValue: "hello",
},
},
},
},
},
},
},
}, nil, nil)

func TestHashSerialize(t *testing.T) {
require.Equal(t, "5:5M8pQvBCPYwzwxe2bZUbV2g8bSgpsotp441xYvVBNMhd;6:taskKey;7:1:a:3:b;;b:2:3.14159265359;;c:4:true;;d:6:1:1:3:hello;;;;;f:5:1:a:3:hello;;;;;;;", exec.HashSerialize())
require.Equal(t, "CNT7drUzuRuv59bbTXhoD2AUzs8vrQgi7XqfAxeHvxGf", hash.Dump(exec).String())
}

func BenchmarkHashSerialize(b *testing.B) {
for i := 0; i < b.N; i++ {
exec.HashSerialize()
}
}
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ require (
github.com/tendermint/go-amino v0.15.1
github.com/tendermint/tendermint v0.33.0
github.com/tendermint/tm-db v0.4.0
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413
google.golang.org/grpc v1.27.1
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/go-playground/validator.v9 v9.31.0
Expand Down
49 changes: 15 additions & 34 deletions hash/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,32 @@ import (
"encoding/json"
"errors"
"fmt"
"hash"

"github.com/mesg-foundation/engine/hash/structhash"
"github.com/mesg-foundation/engine/hash/hashserializer"
"github.com/mr-tron/base58"
)

// DefaultHash is a default hashing algorithm - "sha256".
var DefaultHash = sha256.New
// size is the default size for the hashing algorithm.
const size = sha256.Size

// size is a default size for hashing algorithm.
var size = DefaultHash().Size()
// sumFunc is the default function to hash.
var sumFunc = sha256.Sum256

var errInvalidLen = errors.New("hash: invalid length")

// Digest represents the partial evaluation of a checksum.
type Digest struct {
hash.Hash
}

// Sum appends the current checksum to b and returns the Hash.
func (d *Digest) Sum(b []byte) Hash {
return Hash(d.Hash.Sum(b))
}

// WriteObject adds an interface data to the running hash.
// It never retruns an error.
func (d *Digest) WriteObject(v interface{}) (int, error) {
return d.Write(structhash.Dump(v))
}

// A Hash is a type for representing common hash.
type Hash []byte

// New returns new hash from a given integer.
func New() *Digest {
return &Digest{
Hash: DefaultHash(),
}
// Dump takes a structure that implement HashSerializable and returns its hash.
func Dump(v hashserializer.HashSerializable) Hash {
h := sumFunc([]byte(v.HashSerialize()))
return Hash(h[:])
}

// Dump takes an interface and returns its hash representation.
func Dump(v interface{}) Hash {
d := New()
d.WriteObject(v)
return d.Sum(nil)
// Sum takes a slice of byte and returns its hash.
func Sum(v []byte) Hash {
h := sumFunc(v)
return Hash(h[:])
}

// Int returns a new hash from a given integer.
Expand Down Expand Up @@ -150,12 +131,12 @@ func (h Hash) Valid() bool {
return len(h) == 0 || len(h) == size
}

// MarshalJSON mashals hash into encoded json string.
// MarshalJSON marshals hash into encoded json string.
func (h Hash) MarshalJSON() ([]byte, error) {
return json.Marshal(base58.Encode(h))
}

// UnmarshalJSON unmashals hex encoded json string into hash.
// UnmarshalJSON unmarshals hex encoded json string into hash.
func (h *Hash) UnmarshalJSON(data []byte) error {
var str string
if err := json.Unmarshal(data, &str); err != nil {
Expand Down
19 changes: 13 additions & 6 deletions hash/hash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"
"testing/quick"

"github.com/mesg-foundation/engine/hash/hashserializer"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand All @@ -14,16 +15,22 @@ var (
one = Int(1)
)

func TestDigest(t *testing.T) {
d := New()
d.Write([]byte{0})
type testDump struct {
a int
b string
c []string
}

hash := d.Sum(nil)
assert.Equal(t, hash.String(), "8RBsoeyoRwajj86MZfZE6gMDJQVYGYcdSfx1zxqxNHbr")
func (data testDump) HashSerialize() string {
return hashserializer.New().
AddInt("1", data.a).
AddString("2", data.b).
AddStringSlice("3", data.c).
HashSerialize()
}

func TestDump(t *testing.T) {
assert.Equal(t, Dump(struct{}{}).String(), "5ajuwjHoLj33yG5t5UFsJtUb3vnRaJQEMPqSLz6VyoHK")
assert.Equal(t, "2MREjs2XD2Lcyea4XhsAXBU6zrJE39JRHKrpHU4Q2dgj", Dump(testDump{42, "hello", []string{"c", "b", "a"}}).String())
}

func TestInt(t *testing.T) {
Expand Down
Loading

0 comments on commit ca26c07

Please sign in to comment.