diff --git a/context.go b/context.go index b521929..b625da8 100644 --- a/context.go +++ b/context.go @@ -7,6 +7,7 @@ import ( "fmt" "reflect" "runtime" + "slices" "strconv" "strings" "sync" @@ -31,7 +32,8 @@ type DeserializeFn func(ctx *Context, data []byte) (reflect.Value, []byte, error type FieldInfo struct { Index []int - Tag FieldTag + Tag string + NodeTag string Type reflect.Type } @@ -49,22 +51,20 @@ type TypeInfo struct { type ExtensionInfo struct { ExtType - Interface *graphql.Interface - Fields map[string][]int + Fields map[string]FieldInfo Data interface{} } type FieldIndex struct { - FieldTag + FieldTag FieldTag Extension ExtType - Field string } type NodeInfo struct { NodeType Type *graphql.Object + Interface *graphql.Interface Extensions []ExtType - Fields map[string]FieldIndex } // A Context stores all the data to run a graphvent process @@ -92,52 +92,65 @@ type Context struct { nodeMap map[NodeID]*Node } -func (ctx *Context) GQLType(t reflect.Type) (graphql.Type, error) { - info, mapped := ctx.TypeTypes[t] - if mapped { - return info.Type, nil +func (ctx *Context) GQLType(t reflect.Type, node_type string) (graphql.Type, error) { + if t == reflect.TypeFor[NodeID]() { + node_info, mapped := ctx.NodeTypes[node_type] + if mapped == false { + return nil, fmt.Errorf("Cannot get GQL type for unregistered Node Type \"%s\"", node_type) + } else { + return node_info.Interface, nil + } } else { - switch t.Kind() { - case reflect.Array: - info, mapped := ctx.TypeTypes[t.Elem()] - if mapped { - return graphql.NewList(info.Type), nil - } - case reflect.Slice: - info, mapped := ctx.TypeTypes[t.Elem()] - if mapped { - return graphql.NewList(info.Type), nil - } - case reflect.Map: - info, exists := ctx.TypeTypes[t] - if exists { - return info.Type, nil - } else { - err := RegisterMap(ctx, t) - if err != nil { - return nil, err + info, mapped := ctx.TypeTypes[t] + if mapped { + return info.Type, nil + } else { + switch t.Kind() { + case reflect.Array: + info, mapped := ctx.TypeTypes[t.Elem()] + if mapped { + return graphql.NewList(info.Type), nil } - map_type := ctx.TypeTypes[t].Type - ctx.Log.Logf("gql", "Getting type for %s: %s", t, map_type) - return map_type, nil - } - case reflect.Pointer: - info, mapped := ctx.TypeTypes[t.Elem()] - if mapped { - return info.Type, nil + case reflect.Slice: + info, mapped := ctx.TypeTypes[t.Elem()] + if mapped { + return graphql.NewList(info.Type), nil + } + case reflect.Map: + info, exists := ctx.TypeTypes[t] + if exists { + return info.Type, nil + } else { + err := RegisterMap(ctx, t, node_type) + if err != nil { + return nil, err + } + map_type := ctx.TypeTypes[t].Type + ctx.Log.Logf("gql", "Getting type for %s: %s", t, map_type) + return map_type, nil + } + case reflect.Pointer: + return ctx.GQLType(t.Elem(), node_type) } + return nil, fmt.Errorf("Can't convert %s to GQL type", t) } - return nil, fmt.Errorf("Can't convert %s to GQL type", t) } } -func RegisterMap(ctx *Context, reflect_type reflect.Type) error { - key_type, err := ctx.GQLType(reflect_type.Key()) +func RegisterMap(ctx *Context, reflect_type reflect.Type, node_type string) error { + ctx.Log.Logf("gql", "Registering map %s with node_type %s", reflect_type, node_type) + node_types := strings.Split(node_type, ":") + + if len(node_types) != 2 { + return fmt.Errorf("Invalid node tag for map type %s: \"%s\"", reflect_type, node_type) + } + + key_type, err := ctx.GQLType(reflect_type.Key(), node_types[0]) if err != nil { return err } - val_type, err := ctx.GQLType(reflect_type.Elem()) + val_type, err := ctx.GQLType(reflect_type.Elem(), node_types[1]) if err != nil { return err } @@ -181,17 +194,18 @@ func RegisterMap(ctx *Context, reflect_type reflect.Type) error { func BuildSchema(ctx *Context, query, mutation *graphql.Object) (graphql.Schema, error) { types := []graphql.Type{} + ctx.Log.Logf("gql", "Building Schema") for _, info := range(ctx.TypeMap) { + ctx.Log.Logf("gql", "Adding type %+v", info.Type) types = append(types, info.Type) } - for _, info := range(ctx.Extensions) { - types = append(types, info.Interface) - } - for _, info := range(ctx.Nodes) { + ctx.Log.Logf("gql", "Adding node type object %+v", info.Type) types = append(types, info.Type) + ctx.Log.Logf("gql", "Adding node type interface %+v", info.Interface) + types = append(types, info.Interface) } subscription := graphql.NewObject(graphql.ObjectConfig{ @@ -214,19 +228,6 @@ func BuildSchema(ctx *Context, query, mutation *graphql.Object) (graphql.Schema, }) } -func RegisterSignal[S Signal](ctx *Context) error { - reflect_type := reflect.TypeFor[S]() - signal_type := SignalTypeFor[S]() - - err := RegisterObject[S](ctx) - if err != nil { - return err - } - - ctx.Log.Logf("serialize_types", "Registered SignalType: %+v - %+v", reflect_type, signal_type) - return nil -} - func RegisterExtension[E any, T interface { *E; Extension}](ctx *Context, data interface{}) error { reflect_type := reflect.TypeFor[E]() ext_type := ExtType(SerializedTypeFor[E]()) @@ -235,62 +236,24 @@ func RegisterExtension[E any, T interface { *E; Extension}](ctx *Context, data i return fmt.Errorf("Cannot register extension %+v of type %+v, type already exists in context", reflect_type, ext_type) } - gql_name := "interface_" + strings.ReplaceAll(reflect_type.String(), ".", "_") - ctx.Log.Logf("gql", "Registering %s with gql name %s", reflect_type, gql_name) - gql_interface := graphql.NewInterface(graphql.InterfaceConfig{ - Name: gql_name, - ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object { - ctx, ok := p.Context.Value("resolve").(*ResolveContext) - if ok == false { - return nil - } - - node, ok := p.Value.(NodeResult) - if ok == false { - return nil - } + fields := map[string]FieldInfo{} - type_info, type_exists := ctx.Context.Nodes[node.NodeType] - if type_exists == false { - return ctx.Context.NodeTypes["Base"].Type - } - - return type_info.Type - }, - Fields: graphql.Fields{ - "ID": &graphql.Field{ - Type: graphql.String, - }, - }, - }) - - fields := map[string][]int{} - for _, field := range reflect.VisibleFields(reflect.TypeFor[E]()) { + for _, field := range(reflect.VisibleFields(reflect_type)) { gv_tag, tagged_gv := field.Tag.Lookup("gv") if tagged_gv { - fields[gv_tag] = field.Index - - gql_type, err := ctx.GQLType(field.Type) - if err != nil { - return err + fields[gv_tag] = FieldInfo{ + Index: field.Index, + Tag: gv_tag, + NodeTag: field.Tag.Get("node"), + Type: field.Type, } - - gql_interface.AddFieldConfig(gv_tag, &graphql.Field{ - Type: gql_type, - }) } } - err := RegisterObject[E](ctx) - if err != nil { - return err - } - ctx.Log.Logf("serialize_types", "Registered ExtType: %+v - %+v", reflect_type, ext_type) ctx.Extensions[ext_type] = &ExtensionInfo{ ExtType: ext_type, - Interface: gql_interface, Data: data, Fields: fields, } @@ -299,8 +262,9 @@ func RegisterExtension[E any, T interface { *E; Extension}](ctx *Context, data i return nil } -func RegisterNodeType(ctx *Context, name string, extensions []ExtType, mappings map[string]FieldIndex) error { - node_type := NodeTypeFor(name, extensions, mappings) +func RegisterNodeType(ctx *Context, name string, extensions []ExtType) error { + ctx.Log.Logf("gql", "Registering NodeType %s with extensions %+v", name, extensions) + node_type := NodeTypeFor(extensions) _, exists := ctx.Nodes[node_type] if exists == true { return fmt.Errorf("Cannot register node type %+v, type already exists in context", node_type) @@ -310,7 +274,7 @@ func RegisterNodeType(ctx *Context, name string, extensions []ExtType, mappings for _, extension := range(extensions) { _, in_ctx := ctx.Extensions[extension] if in_ctx == false { - return fmt.Errorf("Cannot register node type %+v, required extension %+v not in context", node_type, extension) + return fmt.Errorf("Cannot register node type %+v, required extension %+v not in context", name, extension) } _, duplicate := ext_found[extension] @@ -321,30 +285,112 @@ func RegisterNodeType(ctx *Context, name string, extensions []ExtType, mappings ext_found[extension] = true } - gql := graphql.NewObject(graphql.ObjectConfig{ + gql_interface := graphql.NewInterface(graphql.InterfaceConfig{ Name: name, - Interfaces: []*graphql.Interface{}, Fields: graphql.Fields{ "ID": &graphql.Field{ - Type: graphql.String, + Type: ctx.TypeTypes[reflect.TypeFor[NodeID]()].Type, }, "Type": &graphql.Field{ - Type: graphql.String, + Type: ctx.TypeTypes[reflect.TypeFor[NodeType]()].Type, + }, + }, + ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object { + ctx_val := p.Context.Value("resolve") + ctx, ok := ctx_val.(*ResolveContext) + if ok == false { + return nil + } + + val, ok := p.Value.(NodeResult) + if ok == false { + return nil + } + + node_info, exists := ctx.Context.Nodes[val.NodeType] + if exists == false { + return nil + } + + for _, ext_type := range(extensions) { + if slices.Contains(node_info.Extensions, ext_type) == false { + // node_info does not contain required extension, so this cannot be a type of this interface + return nil + } + } + + return node_info.Type + }, + }) + + gql := graphql.NewObject(graphql.ObjectConfig{ + Name: name + "Node", + Fields: graphql.Fields{ + "ID": &graphql.Field{ + Type: ctx.TypeTypes[reflect.TypeFor[NodeID]()].Type, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + source, ok := p.Source.(NodeResult) + if ok == false { + return nil, fmt.Errorf("GQL Node value is not NodeResult") + } + return source.NodeID, nil + }, + }, + "Type": &graphql.Field{ + Type: ctx.TypeTypes[reflect.TypeFor[NodeType]()].Type, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + source, ok := p.Source.(NodeResult) + if ok == false { + return nil, fmt.Errorf("GQL Node value is not NodeResult") + } + return source.NodeType, nil + }, }, }, IsTypeOf: func(p graphql.IsTypeOfParams) bool { + source, ok := p.Value.(NodeResult) + if ok == false { return false + } + return source.NodeType == node_type }, }) ctx.Nodes[node_type] = &NodeInfo{ NodeType: node_type, + Interface: gql_interface, Type: gql, Extensions: extensions, - Fields: mappings, } ctx.NodeTypes[name] = ctx.Nodes[node_type] + for _, ext_type := range(extensions) { + ext_info, ext_found := ctx.Extensions[ext_type] + if ext_found == false { + return fmt.Errorf("Extension %s not found", ext_type) + } + + for field_name, field_info := range(ext_info.Fields) { + gql_type, err := ctx.GQLType(field_info.Type, field_info.NodeTag) + if err != nil { + return err + } + ctx.Log.Logf("gql", "Adding field %s[%+v] to %s with gql type %+v", field_name, field_info, name, gql_type) + + gql_interface.AddFieldConfig(field_name, &graphql.Field{ + Type: gql_type, + }) + + gql.AddFieldConfig(field_name, &graphql.Field{ + Type: gql_type, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + return nil, fmt.Errorf("NOT_IMPLEMENTED: TODO") + }, + }) + } + + } + return nil } @@ -378,16 +424,19 @@ func RegisterObject[T any](ctx *Context) error { for _, field := range(reflect.VisibleFields(reflect_type)) { gv_tag, tagged_gv := field.Tag.Lookup("gv") if tagged_gv { + node_tag := field.Tag.Get("node") field_infos[GetFieldTag(gv_tag)] = FieldInfo{ Type: field.Type, - Tag: GetFieldTag(gv_tag), Index: field.Index, + NodeTag: node_tag, + Tag: gv_tag, } - gql_type, err := ctx.GQLType(field.Type) + gql_type, err := ctx.GQLType(field.Type, node_tag) if err != nil { return err } + gql.AddFieldConfig(gv_tag, &graphql.Field{ Type: gql_type, Resolve: func(p graphql.ResolveParams) (interface{}, error) { @@ -563,7 +612,6 @@ func RegisterScalar[S any](ctx *Context, to_json func(interface{})interface{}, f return nil } - func (ctx *Context) AddNode(id NodeID, node *Node) { ctx.nodeMapLock.Lock() ctx.nodeMap[id] = node @@ -670,6 +718,21 @@ func NewContext(db * badger.DB, log Logger) (*Context, error) { var err error + err = RegisterScalar[NodeID](ctx, stringify, unstringify[NodeID], unstringifyAST[NodeID]) + if err != nil { + return nil, err + } + + err = RegisterScalar[NodeType](ctx, identity, coerce[NodeType], astInt[NodeType]) + if err != nil { + return nil, err + } + + err = RegisterNodeType(ctx, "Base", []ExtType{}) + if err != nil { + return nil, err + } + err = RegisterScalar[bool](ctx, identity, coerce[bool], astBool[bool]) if err != nil { return nil, err @@ -715,63 +778,77 @@ func NewContext(db * badger.DB, log Logger) (*Context, error) { return nil, err } - err = RegisterScalar[NodeID](ctx, stringify, unstringify[NodeID], unstringifyAST[NodeID]) + err = RegisterScalar[WaitReason](ctx, identity, coerce[WaitReason], astString[WaitReason]) if err != nil { return nil, err } - err = RegisterScalar[WaitReason](ctx, identity, coerce[WaitReason], astString[WaitReason]) + err = RegisterObject[Node](ctx) if err != nil { return nil, err } - err = RegisterScalar[NodeType](ctx, identity, coerce[NodeType], astInt[NodeType]) + err = RegisterObject[WaitInfo](ctx) if err != nil { return nil, err } - err = RegisterObject[Node](ctx) + err = RegisterExtension[LockableExt](ctx, nil) if err != nil { return nil, err } - err = RegisterObject[WaitInfo](ctx) + err = RegisterExtension[EventExt](ctx, nil) if err != nil { return nil, err } - err = RegisterExtension[LockableExt](ctx, nil) + err = RegisterExtension[ListenerExt](ctx, nil) if err != nil { return nil, err } - err = RegisterExtension[EventExt](ctx, nil) + err = RegisterExtension[GQLExt](ctx, nil) if err != nil { return nil, err } - err = RegisterExtension[ListenerExt](ctx, nil) + err = RegisterNodeType(ctx, "Lockable", []ExtType{ExtTypeFor[LockableExt]()}) if err != nil { return nil, err } - err = RegisterExtension[GQLExt](ctx, nil) + err = RegisterObject[LockableExt](ctx) + if err != nil { + return nil, err + } + + err = RegisterObject[EventExt](ctx) if err != nil { return nil, err } - err = RegisterNodeType(ctx, "Base", []ExtType{}, map[string]FieldIndex{}) + err = RegisterObject[ListenerExt](ctx) if err != nil { return nil, err } + err = RegisterObject[GQLExt](ctx) + if err != nil { + return nil, err + } + schema, err := BuildSchema(ctx, graphql.NewObject(graphql.ObjectConfig{ Name: "Query", Fields: graphql.Fields{ - "Test": &graphql.Field{ - Type: graphql.String, + "Self": &graphql.Field{ + Type: ctx.NodeTypes["Base"].Type, Resolve: func(p graphql.ResolveParams) (interface{}, error) { - return "TEST", nil + // TODO: Send read request and get response instead of hard-coding + return NodeResult{ + NodeID: RandID(), + NodeType: NodeTypeFor([]ExtType{}), + }, nil }, }, }, diff --git a/event.go b/event.go index 6028658..5f05903 100644 --- a/event.go +++ b/event.go @@ -12,7 +12,7 @@ type EventExt struct { Name string `gv:"name"` State EventState `gv:"state"` StateStart time.Time `gv:"state_start"` - Parent NodeID `gv:"parent"` + Parent NodeID `gv:"parent" node:"Base"` } func (ext *EventExt) Load(ctx *Context, node *Node) error { diff --git a/gql.go b/gql.go index 423bced..616338c 100644 --- a/gql.go +++ b/gql.go @@ -1,18 +1,12 @@ package graphvent import ( - "bytes" "context" - "crypto" - "crypto/aes" - "crypto/cipher" "crypto/ecdh" "crypto/ecdsa" - "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/x509" - "encoding/base64" "encoding/json" "fmt" "io" @@ -20,12 +14,9 @@ import ( "net" "net/http" "reflect" - "strings" "sync" "time" - "filippo.io/edwards25519" - "crypto/sha512" "github.com/gobwas/ws" "github.com/gobwas/ws/wsutil" "github.com/graphql-go/graphql" @@ -40,39 +31,6 @@ import ( "github.com/google/uuid" ) -func NodeInterfaceDefaultIsType(required_extensions []ExtType) func(graphql.IsTypeOfParams) bool { - return func(p graphql.IsTypeOfParams) bool { - ctx, ok := p.Context.Value("resolve").(*ResolveContext) - if ok == false { - return false - } - node, ok := p.Value.(NodeResult) - if ok == false { - return false - } - - node_type_def, exists := ctx.Context.Nodes[node.NodeType] - if exists == false { - return false - } else { - for _, ext := range(required_extensions) { - found := false - for _, e := range(node_type_def.Extensions) { - if e == ext { - found = true - break - } - } - if found == false { - return false - } - } - } - - return true - } -} - func PrepResolve(p graphql.ResolveParams) (*ResolveContext, error) { resolve_context, ok := p.Context.Value("resolve").(*ResolveContext) if ok == false { @@ -82,7 +40,7 @@ func PrepResolve(p graphql.ResolveParams) (*ResolveContext, error) { return resolve_context, nil } -// TODO: Make composabe by checkinf if K is a slice, then recursing in the same way that ExtractList does +// TODO: Make composabe by checking if K is a slice, then recursing in the same way that ExtractList does func ExtractParam[K interface{}](p graphql.ResolveParams, name string) (K, error) { var zero K arg_if, ok := p.Args[name] @@ -286,84 +244,6 @@ type ResolveContext struct { NodeCache map[NodeID]NodeResult } -func AuthB64(client_key ed25519.PrivateKey, server_pubkey ed25519.PublicKey) (string, error) { - token_start := time.Now() - token_start_bytes, err := token_start.MarshalBinary() - if err != nil { - return "", err - } - - session_key_public, session_key_private, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - return "", err - } - - session_h := sha512.Sum512(session_key_private.Seed()) - ecdh_client, err := ECDH.NewPrivateKey(session_h[:32]) - if err != nil { - return "", err - } - - server_point, err := (&edwards25519.Point{}).SetBytes(server_pubkey) - if err != nil { - return "", err - } - - ecdh_server, err := ECDH.NewPublicKey(server_point.BytesMontgomery()) - if err != nil { - return "", err - } - - - secret, err := ecdh_client.ECDH(ecdh_server) - if err != nil { - return "", err - } - - if len(secret) != 32 { - return "", fmt.Errorf("ECDH secret not 32 bytes(for AES-256): %d bytes long", len(secret)) - } - - block, err := aes.NewCipher(secret) - if err != nil { - return "", err - } - - iv := make([]byte, block.BlockSize()) - iv_len, err := rand.Reader.Read(iv) - if err != nil { - return "", err - } else if iv_len != block.BlockSize() { - return "", fmt.Errorf("Not enough iv bytes read: %d", iv_len) - } - - var key_encrypted bytes.Buffer - stream := cipher.NewOFB(block, iv) - writer := &cipher.StreamWriter{S: stream, W: &key_encrypted} - - bytes_written, err := writer.Write(session_key_private.Seed()) - if err != nil { - return "", err - } else if bytes_written != len(ecdh_client.Bytes()) { - return "", fmt.Errorf("wrong number of bytes encrypted %d/%d", bytes_written, len(ecdh_client.Bytes())) - } - - digest := append(session_key_public, token_start_bytes...) - signature, err := client_key.Sign(rand.Reader, digest, crypto.Hash(0)) - if err != nil { - return "", err - } - - start_b64 := base64.StdEncoding.EncodeToString(token_start_bytes) - iv_b64 := base64.StdEncoding.EncodeToString(iv) - encrypted_b64 := base64.StdEncoding.EncodeToString(key_encrypted.Bytes()) - key_b64 := base64.StdEncoding.EncodeToString(session_key_public) - sig_b64 := base64.StdEncoding.EncodeToString(signature) - id_b64 := base64.StdEncoding.EncodeToString(client_key.Public().(ed25519.PublicKey)) - - return base64.StdEncoding.EncodeToString([]byte(strings.Join([]string{id_b64, iv_b64, key_b64, encrypted_b64, start_b64, sig_b64}, ":"))), nil -} - func NewResolveContext(ctx *Context, server *Node, gql_ext *GQLExt) (*ResolveContext, error) { return &ResolveContext{ ID: uuid.New(), diff --git a/lockable.go b/lockable.go index a7c05f1..b7e8a60 100644 --- a/lockable.go +++ b/lockable.go @@ -25,11 +25,11 @@ var ReqStateStrings = map[ReqState]string { type LockableExt struct{ State ReqState `gv:"state"` ReqID *uuid.UUID `gv:"req_id"` - Owner *NodeID `gv:"owner"` - PendingOwner *NodeID `gv:"pending_owner"` + Owner *NodeID `gv:"owner" node:"Base"` + PendingOwner *NodeID `gv:"pending_owner" node:"Base"` PendingID uuid.UUID `gv:"pending_id"` - Requirements map[NodeID]ReqState `gv:"requirements"` - WaitInfos WaitMap `gv:"wait_infos"` + Requirements map[NodeID]ReqState `gv:"requirements" node:"Lockable:"` + WaitInfos WaitMap `gv:"wait_infos" node:":Base"` } func NewLockableExt(requirements []NodeID) *LockableExt { diff --git a/node.go b/node.go index eae4100..c55531d 100644 --- a/node.go +++ b/node.go @@ -104,7 +104,7 @@ func (node *Node) PostDeserialize(ctx *Context) error { type WaitReason string type WaitInfo struct { - Destination NodeID `gv:"destination"` + Destination NodeID `gv:"destination" node:"Base"` Timeout uuid.UUID `gv:"timeout"` Reason WaitReason `gv:"reason"` } @@ -226,7 +226,7 @@ func (node *Node) ReadFields(ctx *Context, reqs map[ExtType][]string)map[ExtType if exists == false { fields[req] = fmt.Errorf("%+v does not have %+v extension", node.ID, ext_type) } else { - fields[req] = reflect.ValueOf(ext).Elem().FieldByIndex(ext_info.Fields[req]).Interface() + fields[req] = reflect.ValueOf(ext).Elem().FieldByIndex(ext_info.Fields[req].Index).Interface() } } exts[ext_type] = fields diff --git a/serialize.go b/serialize.go index 4663247..e5a1feb 100644 --- a/serialize.go +++ b/serialize.go @@ -39,28 +39,13 @@ func (t FieldTag) String() string { return fmt.Sprintf("0x%x", uint64(t)) } -func NodeTypeFor(name string, extensions []ExtType, mappings map[string]FieldIndex) NodeType { - digest := []byte("GRAPHVENT_NODE[" + name + "] - ") - for _, ext := range(extensions) { - digest = binary.BigEndian.AppendUint64(digest, uint64(ext)) - } +func NodeTypeFor(extensions []ExtType) NodeType { + digest := []byte("GRAPHVENT_NODE - ") - digest = binary.BigEndian.AppendUint64(digest, 0) - - sorted_keys := make([]string, len(mappings)) - i := 0 - for key := range(mappings) { - sorted_keys[i] = key - i += 1 - } - slices.Sort(sorted_keys) + slices.Sort(extensions) - - - for _, key := range(sorted_keys) { - digest = append(digest, []byte(key + ":")...) - digest = binary.BigEndian.AppendUint64(digest, uint64(mappings[key].Extension)) - digest = append(digest, []byte(mappings[key].Field + "|")...) + for _, ext := range(extensions) { + digest = binary.BigEndian.AppendUint64(digest, uint64(ext)) } hash := sha512.Sum512(digest) diff --git a/signal.go b/signal.go index 4c33984..9f8aafb 100644 --- a/signal.go +++ b/signal.go @@ -242,7 +242,7 @@ type ReadResultSignal struct { } func (signal ReadResultSignal) String() string { - return fmt.Sprintf("ReadResultSignal(%s, %s, %+v, %+v)", signal.ResponseHeader, signal.NodeID, signal.NodeType, signal.Extensions) + return fmt.Sprintf("ReadResultSignal(%s, %s, %+v)", signal.ResponseHeader, signal.NodeID, signal.Extensions) } func NewReadResultSignal(req_id uuid.UUID, node_id NodeID, node_type NodeType, exts map[ExtType]map[string]any) *ReadResultSignal {