Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 101 additions & 57 deletions merklize/merklize.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import (
)

var (
defaultHasher Hasher = PoseidonHasher{}
numRE = regexp.MustCompile(`^\d+$`)
defaultHasher Hasher = PoseidonHasher{}
defaultDocumentLoader = NewDocumentLoader(nil, "")
numRE = regexp.MustCompile(`^\d+$`)
)

var (
Expand All @@ -43,10 +44,16 @@ func SetHasher(h Hasher) {
defaultHasher = h
}

// SetDocumentLoader changes default DocumentLoader
func SetDocumentLoader(docLoader ld.DocumentLoader) {
defaultDocumentLoader = docLoader
}

// Options type allows to change hashing algorithm and create Path and RDFEntry
// instances with hasher different from default one.
type Options struct {
Hasher Hasher
Hasher Hasher
DocumentLoader ld.DocumentLoader
}

func (o Options) getHasher() Hasher {
Expand All @@ -56,6 +63,23 @@ func (o Options) getHasher() Hasher {
return defaultHasher
}

func (o Options) getDocumentLoader() ld.DocumentLoader {
if o.DocumentLoader != nil {
return o.DocumentLoader
}
return defaultDocumentLoader
}

func (o Options) getJSONLdOptions() *ld.JsonLdOptions {
docLoader := o.getDocumentLoader()
if docLoader == nil {
return nil
}
return &ld.JsonLdOptions{
DocumentLoader: docLoader,
}
}

func (o Options) NewPath(parts ...interface{}) (Path, error) {
p := Path{hasher: o.getHasher()}
err := p.Append(parts)
Expand All @@ -64,7 +88,7 @@ func (o Options) NewPath(parts ...interface{}) (Path, error) {

func (o Options) PathFromContext(ctxBytes []byte, path string) (Path, error) {
out := Path{hasher: o.getHasher()}
err := out.pathFromContext(ctxBytes, path)
err := out.pathFromContext(ctxBytes, path, o.getJSONLdOptions())
return out, err
}

Expand All @@ -89,6 +113,28 @@ func (o Options) NewRDFEntry(key Path, value interface{}) (RDFEntry, error) {
return e, nil
}

func (o Options) NewPathFromDocument(docBytes []byte,
path string) (Path, error) {

var docObj map[string]interface{}
err := json.Unmarshal(docBytes, &docObj)
if err != nil {
return Path{}, err
}

pathParts := strings.Split(path, ".")
if len(pathParts) == 0 {
return Path{}, errors.New("path is empty")
}

pathPartsI, err := o.pathFromDocument(nil, docObj, pathParts, false)
if err != nil {
return Path{}, err
}

return Path{parts: pathPartsI, hasher: o.getHasher()}, nil
}

type Path struct {
parts []interface{} // string or int types
hasher Hasher
Expand All @@ -113,29 +159,14 @@ func NewPath(parts ...interface{}) (Path, error) {
// NewPathFromContext parses context and do its best to generate full Path
// from shortcut line field1.field2.field3...
func NewPathFromContext(ctxBytes []byte, path string) (Path, error) {
var out = Path{hasher: defaultHasher}
err := out.pathFromContext(ctxBytes, path)
defaultOpts := Options{}
var out = Path{hasher: defaultOpts.getHasher()}
err := out.pathFromContext(ctxBytes, path, defaultOpts.getJSONLdOptions())
return out, err
}

func NewPathFromDocument(docBytes []byte, path string) (Path, error) {
var docObj map[string]interface{}
err := json.Unmarshal(docBytes, &docObj)
if err != nil {
return Path{}, err
}

pathParts := strings.Split(path, ".")
if len(pathParts) == 0 {
return Path{}, errors.New("path is empty")
}

pathPartsI, err := pathFromDocument(nil, docObj, pathParts, false)
if err != nil {
return Path{}, err
}

return Path{parts: pathPartsI, hasher: defaultHasher}, nil
return Options{}.NewPathFromDocument(docBytes, path)
}

// NewFieldPathFromContext resolves field path without type path prefix
Expand Down Expand Up @@ -164,14 +195,17 @@ func NewFieldPathFromContext(ctxBytes []byte, ctxType, fieldPath string) (Path,
}

// TypeIDFromContext returns @id attribute for type from JSON-LD context
func TypeIDFromContext(ctxBytes []byte, typeName string) (string, error) {
func (o Options) TypeIDFromContext(ctxBytes []byte,
typeName string) (string, error) {

var ctxObj map[string]interface{}
err := json.Unmarshal(ctxBytes, &ctxObj)
if err != nil {
return "", err
}

ldCtx, err := ld.NewContext(nil, nil).Parse(ctxObj["@context"])
ldCtx, err := ld.NewContext(nil, o.getJSONLdOptions()).
Parse(ctxObj["@context"])
if err != nil {
return "", err
}
Expand All @@ -198,15 +232,21 @@ func TypeIDFromContext(ctxBytes []byte, typeName string) (string, error) {
return typeIDStr, nil
}

// TypeIDFromContext returns @id attribute for type from JSON-LD context
func TypeIDFromContext(ctxBytes []byte, typeName string) (string, error) {
return Options{}.TypeIDFromContext(ctxBytes, typeName)
}

// TypeFromContext returns type of field from context by path.
func TypeFromContext(ctxBytes []byte, path string) (string, error) {
func (o Options) TypeFromContext(ctxBytes []byte, path string) (string, error) {
var ctxObj map[string]interface{}
err := json.Unmarshal(ctxBytes, &ctxObj)
if err != nil {
return "", err
}

ldCtx, err := ld.NewContext(nil, nil).Parse(ctxObj["@context"])
ldCtx, err := ld.NewContext(nil, o.getJSONLdOptions()).
Parse(ctxObj["@context"])
if err != nil {
return "", err
}
Expand Down Expand Up @@ -236,15 +276,21 @@ func TypeFromContext(ctxBytes []byte, path string) (string, error) {
return ldCtx.GetTypeMapping(parts[len(parts)-1]), nil
}

func (p *Path) pathFromContext(ctxBytes []byte, path string) error {
// TypeFromContext returns type of field from context by path.
func TypeFromContext(ctxBytes []byte, path string) (string, error) {
return Options{}.TypeFromContext(ctxBytes, path)
}

func (p *Path) pathFromContext(ctxBytes []byte, path string,
jsonLdOptions *ld.JsonLdOptions) error {

var ctxObj map[string]interface{}
err := json.Unmarshal(ctxBytes, &ctxObj)
if err != nil {
return err
}

ldCtx, err := ld.NewContext(nil, nil).Parse(ctxObj["@context"])
ldCtx, err := ld.NewContext(nil, jsonLdOptions).Parse(ctxObj["@context"])
if err != nil {
return err
}
Expand Down Expand Up @@ -289,7 +335,7 @@ func (p *Path) pathFromContext(ctxBytes []byte, path string) error {
// Create path JSON-LD document.
// If acceptArray is true, the previous element was index, and we accept an
// array
func pathFromDocument(ldCtx *ld.Context, docObj interface{},
func (o Options) pathFromDocument(ldCtx *ld.Context, docObj interface{},
pathParts []string, acceptArray bool) ([]interface{}, error) {

if len(pathParts) == 0 {
Expand All @@ -305,7 +351,7 @@ func pathFromDocument(ldCtx *ld.Context, docObj interface{},
return nil, err
}

moreParts, err := pathFromDocument(ldCtx, docObj, newPathParts, true)
moreParts, err := o.pathFromDocument(ldCtx, docObj, newPathParts, true)
if err != nil {
return nil, err
}
Expand All @@ -325,7 +371,7 @@ func pathFromDocument(ldCtx *ld.Context, docObj interface{},
return nil, errors.New("unexpected array element")
}

return pathFromDocument(ldCtx, docObjT[0], pathParts, false)
return o.pathFromDocument(ldCtx, docObjT[0], pathParts, false)
case map[string]interface{}:
// pass
docObjMap = docObjT
Expand All @@ -334,7 +380,7 @@ func pathFromDocument(ldCtx *ld.Context, docObj interface{},
}

if ldCtx == nil {
ldCtx = ld.NewContext(nil, nil)
ldCtx = ld.NewContext(nil, o.getJSONLdOptions())
}

var err error
Expand Down Expand Up @@ -408,7 +454,7 @@ func pathFromDocument(ldCtx *ld.Context, docObj interface{},
}
}

moreParts, err := pathFromDocument(ldCtx, docObjMap[term], newPathParts,
moreParts, err := o.pathFromDocument(ldCtx, docObjMap[term], newPathParts,
true)
if err != nil {
return nil, err
Expand Down Expand Up @@ -589,21 +635,7 @@ func (v *value) AsBool() (bool, error) {
}

func NewRDFEntry(key Path, value any) (RDFEntry, error) {
e := RDFEntry{key: key}
if len(key.parts) == 0 {
return e, errors.New("key length is zero")
}

switch v := value.(type) {
case int:
e.value = int64(v)
case int64, string, bool, time.Time:
e.value = value
default:
return e, fmt.Errorf("incorrect value type: %T", value)
}

return e, nil
return Options{}.NewRDFEntry(key, value)
}

func (e RDFEntry) KeyMtEntry() (*big.Int, error) {
Expand Down Expand Up @@ -1525,12 +1557,7 @@ func MerklizeJSONLD(ctx context.Context, in io.Reader,
options := ld.NewJsonLdOptions("")
options.Algorithm = ld.AlgorithmURDNA2015
options.SafeMode = mz.safeMode

if mz.documentLoader == nil {
options.DocumentLoader = NewDocumentLoader(mz.ipfsCli, mz.ipfsGW)
} else {
options.DocumentLoader = mz.documentLoader
}
options.DocumentLoader = mz.getDocumentLoader()

normDoc, err := proc.Normalize(obj, options)
if err != nil {
Expand Down Expand Up @@ -1579,6 +1606,16 @@ func (m *Merklizer) entry(path Path) (RDFEntry, error) {
return e, nil
}

func (m *Merklizer) getDocumentLoader() ld.DocumentLoader {
if m.documentLoader != nil {
return m.documentLoader
}
if m.ipfsCli == nil && m.ipfsGW == "" {
return defaultDocumentLoader
}
return NewDocumentLoader(m.ipfsCli, m.ipfsGW)
}

func rvExtractObjField(obj any, field string) (any, error) {
jsObj, isJSONObj := obj.(map[string]any)
if !isJSONObj {
Expand Down Expand Up @@ -1657,11 +1694,18 @@ func (m *Merklizer) JSONLDType(path Path) (string, error) {
}

func (m *Merklizer) ResolveDocPath(path string) (Path, error) {
realPath, err := NewPathFromDocument(m.srcDoc, path)
opts := Options{
Hasher: m.hasher,
DocumentLoader: m.getDocumentLoader(),
}
if opts.Hasher == nil {
opts.Hasher = defaultHasher
}

realPath, err := opts.NewPathFromDocument(m.srcDoc, path)
if err != nil {
return Path{}, err
}
realPath.hasher = m.hasher
return realPath, nil
}

Expand Down
Loading