github.com/metacurrency/holochain@v0.1.0-alpha-26.0.20200915073418-5c83169c9b5b/zygosome.go (about)

     1  // Copyright (C) 2013-2018, The MetaCurrency Project (Eric Harris-Braun, Arthur Brock, et. al.)
     2  // Use of this source code is governed by GPLv3 found in the LICENSE file
     3  //----------------------------------------------------------------------------------------
     4  // ZygoRibosome implements a zygomys use of the Ribosome interface
     5  
     6  package holochain
     7  
     8  import (
     9  	"encoding/json"
    10  	"errors"
    11  	"fmt"
    12  	zygo "github.com/glycerine/zygomys/zygo"
    13  	. "github.com/holochain/holochain-proto/hash"
    14  	peer "github.com/libp2p/go-libp2p-peer"
    15  	"math"
    16  	"regexp"
    17  	"strconv"
    18  	"strings"
    19  	"time"
    20  )
    21  
    22  const (
    23  	ZygoRibosomeType = "zygo"
    24  )
    25  
    26  // ZygoRibosome holds data needed for the Zygo VM
    27  type ZygoRibosome struct {
    28  	h          *Holochain
    29  	zome       *Zome
    30  	env        *zygo.Zlisp
    31  	lastResult zygo.Sexp
    32  	library    string
    33  }
    34  
    35  // Type returns the string value under which this ribosome is registered
    36  func (z *ZygoRibosome) Type() string { return ZygoRibosomeType }
    37  
    38  // ChainGenesis runs the application genesis function
    39  // this function gets called after the genesis entries are added to the chain
    40  func (z *ZygoRibosome) ChainGenesis() (err error) {
    41  	err = z.boolFn("genesis", "")
    42  	return
    43  }
    44  
    45  // BridgeGenesis runs the bridging genesis function
    46  // this function gets called on both sides of the bridging
    47  func (z *ZygoRibosome) BridgeGenesis(side int, dnaHash Hash, data string) (err error) {
    48  	err = z.boolFn("bridgeGenesis", fmt.Sprintf(`%d "%s" "%s"`, side, dnaHash.String(), sanitizeZyString(data)))
    49  	return
    50  }
    51  
    52  func (z *ZygoRibosome) boolFn(fnName string, args string) (err error) {
    53  	err = z.env.LoadString("(" + fnName + " " + args + ")")
    54  	if err != nil {
    55  		return
    56  	}
    57  	result, err := z.env.Run()
    58  	if err != nil {
    59  		err = fmt.Errorf("Error executing %s: %v", fnName, err)
    60  		return
    61  	}
    62  	switch result.(type) {
    63  	case *zygo.SexpBool:
    64  		r := result.(*zygo.SexpBool).Val
    65  		if !r {
    66  			err = fmt.Errorf("%s failed", fnName)
    67  		}
    68  	case *zygo.SexpSentinel:
    69  		err = fmt.Errorf("%s should return boolean, got nil", fnName)
    70  
    71  	default:
    72  		err = fmt.Errorf("%s should return boolean, got: %v", fnName, result)
    73  	}
    74  	return
    75  
    76  }
    77  
    78  // Receive calls the app receive function for node-to-node messages
    79  func (z *ZygoRibosome) Receive(from string, msg string) (response string, err error) {
    80  	var code string
    81  	fnName := "receive"
    82  
    83  	code = fmt.Sprintf(`(json (%s "%s" (unjson (raw "%s"))))`, fnName, from, sanitizeZyString(msg))
    84  	z.h.Debug(code)
    85  	err = z.env.LoadString(code)
    86  	if err != nil {
    87  		return
    88  	}
    89  	var result interface{}
    90  	result, err = z.env.Run()
    91  	if err == nil {
    92  		switch t := result.(type) {
    93  		case *zygo.SexpStr:
    94  			response = t.S
    95  		case *zygo.SexpInt:
    96  			response = fmt.Sprintf("%d", t.Val)
    97  		case *zygo.SexpRaw:
    98  			response = cleanZygoJson(string(t.Val))
    99  		default:
   100  			result = fmt.Sprintf("%v", result)
   101  		}
   102  	}
   103  	return
   104  }
   105  
   106  // BundleCancel calls the app bundleCanceled function
   107  func (z *ZygoRibosome) BundleCanceled(reason string) (response string, err error) {
   108  	return
   109  }
   110  
   111  // ValidatePackagingRequest calls the app for a validation packaging request for an action
   112  func (z *ZygoRibosome) ValidatePackagingRequest(action ValidatingAction, def *EntryDef) (req PackagingReq, err error) {
   113  	var code string
   114  	fnName := "validate" + strings.Title(action.Name()) + "Pkg"
   115  	code = fmt.Sprintf(`(%s "%s")`, fnName, def.Name)
   116  	z.h.Debug(code)
   117  	err = z.env.LoadString(code)
   118  	if err != nil {
   119  		return
   120  	}
   121  	result, err := z.env.Run()
   122  	if err != nil {
   123  		err = fmt.Errorf("Error executing %s: %v", fnName, err)
   124  		return
   125  	}
   126  	switch v := result.(type) {
   127  	case *zygo.SexpHash:
   128  		j := cleanZygoJson(zygo.SexpToJson(v))
   129  		m := make(PackagingReq)
   130  		err = json.Unmarshal([]byte(j), &m)
   131  		if err != nil {
   132  			return
   133  		}
   134  		delete(m, "zKeyOrder")
   135  		delete(m, "Atype")
   136  		req = m
   137  	case *zygo.SexpSentinel:
   138  	default:
   139  		err = fmt.Errorf("%s should return nil or hash, got: %v", fnName, v)
   140  	}
   141  
   142  	return
   143  }
   144  
   145  func prepareZyEntryArgs(def *EntryDef, entry Entry, header *Header) (args string, err error) {
   146  	entryStr := entry.Content().(string)
   147  	switch def.DataFormat {
   148  	case DataFormatRawZygo:
   149  		args = entryStr
   150  	case DataFormatString:
   151  		args = "\"" + sanitizeZyString(entryStr) + "\""
   152  	case DataFormatLinks:
   153  		fallthrough
   154  	case DataFormatJSON:
   155  		args = fmt.Sprintf(`(unjson (raw "%s"))`, sanitizeZyString(entryStr))
   156  	default:
   157  		err = errors.New("data format not implemented: " + def.DataFormat)
   158  		return
   159  	}
   160  
   161  	var hdr string
   162  	if header != nil {
   163  		hdr = fmt.Sprintf(
   164  			`(hash EntryLink:"%s" Type:"%s" Time:"%s")`,
   165  			header.EntryLink.String(),
   166  			header.Type,
   167  			header.Time.UTC().Format(time.RFC3339),
   168  		)
   169  	} else {
   170  		hdr = `""`
   171  	}
   172  
   173  	args += " " + hdr
   174  	return
   175  }
   176  
   177  func prepareZyValidateArgs(action Action, def *EntryDef) (args string, err error) {
   178  	switch t := action.(type) {
   179  	case *ActionCommit:
   180  		args, err = prepareZyEntryArgs(def, t.entry, t.header)
   181  	case *ActionPut:
   182  		args, err = prepareZyEntryArgs(def, t.entry, t.header)
   183  	case *ActionMod:
   184  		args, err = prepareZyEntryArgs(def, t.entry, t.header)
   185  		if err == nil {
   186  			args += fmt.Sprintf(` "%s"`, t.replaces.String())
   187  		}
   188  	case *ActionDel:
   189  		args = fmt.Sprintf(`"%s"`, t.entry.Hash.String())
   190  	case *ActionLink:
   191  		var j []byte
   192  		j, err = json.Marshal(t.links)
   193  		if err == nil {
   194  			args = fmt.Sprintf(`"%s" (unjson (raw "%s"))`, t.validationBase.String(), sanitizeZyString(string(j)))
   195  		}
   196  	default:
   197  		err = fmt.Errorf("can't prepare args for %T: ", t)
   198  		return
   199  	}
   200  	return
   201  }
   202  
   203  func buildZyValidateAction(action Action, def *EntryDef, pkg *ValidationPackage, sources []string) (code string, err error) {
   204  	fnName := "validate" + strings.Title(action.Name())
   205  	var args string
   206  	args, err = prepareZyValidateArgs(action, def)
   207  	if err != nil {
   208  		return
   209  	}
   210  	srcs := mkZySources(sources)
   211  
   212  	var pkgObj string
   213  	if pkg == nil || pkg.Chain == nil {
   214  		pkgObj = "(hash)"
   215  	} else {
   216  		var j []byte
   217  		j, err = json.Marshal(pkg.Chain)
   218  		if err != nil {
   219  			return
   220  		}
   221  		pkgObj = fmt.Sprintf(`(unjson (raw "%s"))`, sanitizeZyString(string(j)))
   222  	}
   223  
   224  	code = fmt.Sprintf(`(%s "%s" %s %s %s)`, fnName, def.Name, args, pkgObj, srcs)
   225  
   226  	return
   227  }
   228  
   229  // ValidateAction builds the correct validation function based on the action an calls it
   230  func (z *ZygoRibosome) ValidateAction(action Action, def *EntryDef, pkg *ValidationPackage, sources []string) (err error) {
   231  	var code string
   232  	code, err = buildZyValidateAction(action, def, pkg, sources)
   233  	if err != nil {
   234  		return
   235  	}
   236  	z.h.Debug(code)
   237  	err = z.runValidate(action.Name(), code)
   238  	return
   239  }
   240  
   241  func mkZySources(sources []string) (srcs string) {
   242  	var err error
   243  	var b []byte
   244  	b, err = json.Marshal(sources)
   245  	if err != nil {
   246  		return
   247  	}
   248  	srcs = fmt.Sprintf(`(unjson (raw "%s"))`, sanitizeZyString(string(b)))
   249  	return
   250  }
   251  
   252  func (z *ZygoRibosome) prepareValidateArgs(def *EntryDef, entry Entry, sources []string) (e string, srcs string, err error) {
   253  	c := entry.Content().(string)
   254  	// @todo handle JSON if schema type is different
   255  	switch def.DataFormat {
   256  	case DataFormatRawZygo:
   257  		e = c
   258  	case DataFormatString:
   259  		e = "\"" + sanitizeZyString(c) + "\""
   260  	case DataFormatLinks:
   261  		fallthrough
   262  	case DataFormatJSON:
   263  		e = fmt.Sprintf(`(unjson (raw "%s"))`, sanitizeZyString(c))
   264  	default:
   265  		err = errors.New("data format not implemented: " + def.DataFormat)
   266  		return
   267  	}
   268  	srcs = mkZySources(sources)
   269  	return
   270  }
   271  
   272  func (z *ZygoRibosome) runValidate(fnName string, code string) (err error) {
   273  	err = z.env.LoadString(code)
   274  	if err != nil {
   275  		return
   276  	}
   277  	result, err := z.env.Run()
   278  	if err != nil {
   279  		err = fmt.Errorf("Error executing %s: %v", fnName, err)
   280  		return
   281  	}
   282  	switch v := result.(type) {
   283  	case *zygo.SexpBool:
   284  		r := v.Val
   285  		if !r {
   286  			err = ValidationFailed()
   287  		}
   288  	case *zygo.SexpStr:
   289  		if v.S != "" {
   290  			err = ValidationFailed(v.S)
   291  		}
   292  	case *zygo.SexpSentinel:
   293  		err = fmt.Errorf("%s should return boolean or string, got nil", fnName)
   294  
   295  	default:
   296  		err = fmt.Errorf("%s should return boolean or string, got: %v", fnName, result)
   297  	}
   298  	return
   299  }
   300  
   301  func (z *ZygoRibosome) validateEntry(fnName string, def *EntryDef, entry Entry, header *Header, sources []string) (err error) {
   302  	e, srcs, err := z.prepareValidateArgs(def, entry, sources)
   303  	if err != nil {
   304  		return
   305  	}
   306  
   307  	var hdr string
   308  	if header != nil {
   309  		hdr = fmt.Sprintf(
   310  			`(hash EntryLink:"%s" Type:"%s" Time:"%s")`,
   311  			header.EntryLink.String(),
   312  			header.Type,
   313  			header.Time.UTC().Format(time.RFC3339),
   314  		)
   315  	} else {
   316  		hdr = `""`
   317  	}
   318  
   319  	code := fmt.Sprintf(`(%s "%s" %s %s %s)`, fnName, def.Name, e, hdr, srcs)
   320  	z.h.Debugf("%s: %s", fnName, code)
   321  
   322  	err = z.runValidate(fnName, code)
   323  	return
   324  }
   325  
   326  // sanitizeZyString makes sure all quotes are quoted
   327  func sanitizeZyString(s string) string {
   328  	s = strings.Replace(s, "\"", "\\\"", -1)
   329  	return s
   330  }
   331  
   332  // Call calls the zygo function that was registered with expose
   333  func (z *ZygoRibosome) Call(fn *FunctionDef, params interface{}) (result interface{}, err error) {
   334  	var code string
   335  	switch fn.CallingType {
   336  	case STRING_CALLING:
   337  		code = fmt.Sprintf(`(%s "%s")`, fn.Name, sanitizeZyString(params.(string)))
   338  	case JSON_CALLING:
   339  		if params.(string) == "" {
   340  			code = fmt.Sprintf(`(json (%s (raw "%s")))`, fn.Name, sanitizeZyString(params.(string)))
   341  		} else {
   342  			code = fmt.Sprintf(`(json (%s (unjson (raw "%s"))))`, fn.Name, sanitizeZyString(params.(string)))
   343  		}
   344  	default:
   345  		err = errors.New("params type not implemented")
   346  		return
   347  	}
   348  	z.h.Debugf("Zygo Call: %s", code)
   349  	err = z.env.LoadString(code)
   350  	if err != nil {
   351  		return
   352  	}
   353  	result, err = z.env.Run()
   354  	if err == nil {
   355  		switch fn.CallingType {
   356  		case STRING_CALLING:
   357  			switch t := result.(type) {
   358  			case *zygo.SexpStr:
   359  				result = t.S
   360  			case *zygo.SexpInt:
   361  				result = fmt.Sprintf("%d", t.Val)
   362  			case *zygo.SexpRaw:
   363  				result = string(t.Val)
   364  			default:
   365  				result = fmt.Sprintf("%v", result)
   366  			}
   367  		case JSON_CALLING:
   368  			// type should always be SexpRaw
   369  			switch t := result.(type) {
   370  			case *zygo.SexpRaw:
   371  				result = cleanZygoJson(string(t.Val))
   372  			default:
   373  				err = errors.New("expected SexpRaw return type")
   374  			}
   375  		}
   376  
   377  	}
   378  	return
   379  }
   380  
   381  // These are the zygo implementations of the library functions that must available in
   382  // all Ribosome implementations.
   383  const (
   384  	ZygoLibrary = `(def HC_Version "` + VersionStr + `")` +
   385  		`(def HC_HashNotFound nil)` +
   386  		`(def HC_Status_Live ` + StatusLiveVal + ")" +
   387  		`(def HC_Status_Rejected ` + StatusRejectedVal + ")" +
   388  		`(def HC_Status_Deleted ` + StatusDeletedVal + ")" +
   389  		`(def HC_Status_Modified ` + StatusModifiedVal + ")" +
   390  		`(def HC_Status_Any ` + StatusAnyVal + ")" +
   391  		`(def HC_GetMask_Default ` + GetMaskDefaultStr + ")" +
   392  		`(def HC_GetMask_Entry ` + GetMaskEntryStr + ")" +
   393  		`(def HC_GetMask_EntryType ` + GetMaskEntryTypeStr + ")" +
   394  		`(def HC_GetMask_Sources ` + GetMaskSourcesStr + ")" +
   395  		`(def HC_GetMask_All ` + GetMaskAllStr + ")" +
   396  
   397  		`(def HC_Bridge_Caller ` + BridgeCallerStr + ")" +
   398  		`(def HC_Bridge_Callee ` + BridgeCalleeStr + ")" +
   399  
   400  		`(def HC_LinkAction_Add "` + AddLinkAction + "\")" +
   401  		`(def HC_LinkAction_Del "` + DelLinkAction + "\")" +
   402  		`(def HC_PkgReq_Chain "` + PkgReqChain + "\")" +
   403  		`(def HC_PkgReq_ChainOpt_None "` + PkgReqChainOptNoneStr + "\")" +
   404  		`(def HC_PkgReq_ChainOpt_Headers "` + PkgReqChainOptHeadersStr + "\")" +
   405  		`(def HC_PkgReq_ChainOpt_Entries "` + PkgReqChainOptEntriesStr + "\")" +
   406  		`(def HC_PkgReq_ChainOpt_Full "` + PkgReqChainOptFullStr + "\")" +
   407  
   408  		`(def HC_Migrate_Close "` + MigrateEntryTypeClose + `")` +
   409  		`(def HC_Migrate_Open "` + MigrateEntryTypeOpen + `")`
   410  )
   411  
   412  func makeResult(env *zygo.Zlisp, resultValue zygo.Sexp, resultError error) (zygo.Sexp, error) {
   413  	result, err := zygo.MakeHash(nil, "hash", env)
   414  	if err != nil {
   415  		return nil, err
   416  	}
   417  	if resultError != nil {
   418  		err = result.HashSet(env.MakeSymbol("error"), &zygo.SexpStr{S: resultError.Error()})
   419  	} else {
   420  		err = result.HashSet(env.MakeSymbol("result"), resultValue)
   421  	}
   422  	return result, err
   423  }
   424  
   425  // cleanZygoJson removes zygos crazy crap
   426  func cleanZygoJson(s string) string {
   427  	s = strings.Replace(s, `"Atype":"hash", `, "", -1)
   428  	re := regexp.MustCompile(`, "zKeyOrder":\[[^\]]+\]`)
   429  	s = string(re.ReplaceAll([]byte(s), []byte("")))
   430  	s = strings.Replace(s, `", "`, `","`, -1)
   431  	return s
   432  }
   433  
   434  func zyProcessArgs(z *ZygoRibosome, args []Arg, zyArgs []zygo.Sexp) (err error) {
   435  	err = checkArgCount(args, len(zyArgs))
   436  	if err != nil {
   437  		return err
   438  	}
   439  
   440  	// check arg types
   441  	for i, a := range zyArgs {
   442  		switch args[i].Type {
   443  		case StringArg:
   444  			var str string
   445  			switch t := a.(type) {
   446  			case *zygo.SexpStr:
   447  				str = t.S
   448  				args[i].value = str
   449  			default:
   450  				return argErr("string", i+1, args[i])
   451  			}
   452  		case HashArg:
   453  			switch t := a.(type) {
   454  			case *zygo.SexpStr:
   455  				var hash Hash
   456  				hash, err = NewHash(t.S)
   457  				if err != nil {
   458  					return
   459  				}
   460  				args[i].value = hash
   461  			default:
   462  				return argErr("string", i+1, args[i])
   463  			}
   464  		case IntArg:
   465  			var integer int64
   466  			switch t := a.(type) {
   467  			case *zygo.SexpInt:
   468  				integer = t.Val
   469  				args[i].value = integer
   470  			default:
   471  				return argErr("int", i+1, args[i])
   472  			}
   473  		case BoolArg:
   474  			var boolean bool
   475  			switch t := a.(type) {
   476  			case *zygo.SexpBool:
   477  				boolean = t.Val
   478  				args[i].value = boolean
   479  			default:
   480  				return argErr("boolean", i+1, args[i])
   481  			}
   482  		case ArgsArg:
   483  			switch t := a.(type) {
   484  			case *zygo.SexpStr:
   485  				args[i].value = t.S
   486  			case *zygo.SexpHash:
   487  				args[i].value = cleanZygoJson(zygo.SexpToJson(t))
   488  			default:
   489  				return argErr("string or hash", i+1, args[i])
   490  			}
   491  		case EntryArg:
   492  			// this a special case in that all EntryArgs must be preceeded by
   493  			// string arg that specifies the entry type
   494  
   495  			// don't have to do checking because the previous time through the loop
   496  			// should have done it
   497  			entryType := zyArgs[i-1].(*zygo.SexpStr).S
   498  			_, def, err := z.h.GetEntryDef(entryType)
   499  			if err != nil {
   500  				return err
   501  			}
   502  
   503  			var entry string
   504  			switch def.DataFormat {
   505  			case DataFormatRawZygo:
   506  				fallthrough
   507  			case DataFormatRawJS:
   508  				fallthrough
   509  			case DataFormatString:
   510  				switch t := a.(type) {
   511  				case *zygo.SexpStr:
   512  					entry = t.S
   513  				default:
   514  					return argErr("string", i+1, args[i])
   515  				}
   516  			case DataFormatLinks:
   517  				switch t := a.(type) {
   518  				case *zygo.SexpHash:
   519  					entry = cleanZygoJson(zygo.SexpToJson(t))
   520  				default:
   521  					return argErr("hash", i+1, args[i])
   522  				}
   523  			case DataFormatJSON:
   524  				switch a.(type) {
   525  				case *zygo.SexpSentinel:
   526  					entry = "undefined"
   527  				default:
   528  					entry = cleanZygoJson(zygo.SexpToJson(a))
   529  				}
   530  			default:
   531  				err = errors.New("data format not implemented: " + def.DataFormat)
   532  				return err
   533  			}
   534  			args[i].value = entry
   535  
   536  		case MapArg:
   537  			switch t := a.(type) {
   538  			case *zygo.SexpHash:
   539  				j := cleanZygoJson(zygo.SexpToJson(t))
   540  				m := make(map[string]interface{})
   541  				var err = json.Unmarshal([]byte(j), &m)
   542  				if err != nil {
   543  					return err
   544  				}
   545  				args[i].value = m
   546  			default:
   547  				return argErr("hash", i+1, args[i])
   548  			}
   549  		case ToStrArg:
   550  			var str string
   551  
   552  			switch t := a.(type) {
   553  			case *zygo.SexpStr:
   554  				str = t.S
   555  			case *zygo.SexpInt:
   556  				str = fmt.Sprintf("%d", t.Val)
   557  			case *zygo.SexpBool:
   558  				if t.Val {
   559  					str = "true"
   560  				} else {
   561  					str = "false"
   562  				}
   563  			case *zygo.SexpHash:
   564  				str = cleanZygoJson(zygo.SexpToJson(t))
   565  			case *zygo.SexpArray:
   566  				str = cleanZygoJson(zygo.SexpToJson(t))
   567  			default:
   568  				return argErr("int, boolean, string, array or hash", i+1, args[i])
   569  			}
   570  			args[i].value = str
   571  		}
   572  	}
   573  
   574  	return
   575  }
   576  
   577  // NewZygoRibosome factory function to build a zygo execution environment for a zome
   578  func NewZygoRibosome(h *Holochain, zome *Zome) (n Ribosome, err error) {
   579  	z := ZygoRibosome{
   580  		h:    h,
   581  		zome: zome,
   582  		env:  zygo.NewZlispSandbox(),
   583  	}
   584  
   585  	z.env.AddFunction("version",
   586  		func(env *zygo.Zlisp, name string, args []zygo.Sexp) (zygo.Sexp, error) {
   587  			return &zygo.SexpStr{S: VersionStr}, nil
   588  		})
   589  
   590  	addExtras(&z)
   591  
   592  	var appKeyHash, appAgentStr, appAgentHash, appAgentTopHash zygo.SexpStr
   593  	if h != nil {
   594  		appKeyHash = zygo.SexpStr{S: h.nodeIDStr}
   595  		appAgentStr = zygo.SexpStr{S: sanitizeZyString(string(h.Agent().Identity()))}
   596  		appAgentHash = zygo.SexpStr{S: h.agentHash.String()}
   597  		appAgentTopHash = zygo.SexpStr{S: h.agentTopHash.String()}
   598  	}
   599  
   600  	// use a closure so that the registered zygo function can call Expose on the correct ZygoRibosome obj
   601  
   602  	z.env.AddFunction("property",
   603  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   604  			a := &APIFnProperty{}
   605  			args := a.Args()
   606  			err := zyProcessArgs(&z, args, zyargs)
   607  			if err != nil {
   608  				return zygo.SexpNull, err
   609  			}
   610  
   611  			a.prop = args[0].value.(string)
   612  
   613  			var p interface{}
   614  			p, err = a.Call(h)
   615  
   616  			if err != nil {
   617  				return zygo.SexpNull, err
   618  			}
   619  			result := zygo.SexpStr{S: p.(string)}
   620  			return &result, err
   621  		})
   622  
   623  	z.env.AddFunction("debug",
   624  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   625  			a := &APIFnDebug{}
   626  			args := a.Args()
   627  			err := zyProcessArgs(&z, args, zyargs)
   628  			if err != nil {
   629  				return zygo.SexpNull, err
   630  			}
   631  			a.msg = args[0].value.(string)
   632  			a.Call(h)
   633  			return zygo.SexpNull, err
   634  		})
   635  
   636  	z.env.AddFunction("makeHash",
   637  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   638  			a := &APIFnMakeHash{}
   639  			args := a.Args()
   640  			err := zyProcessArgs(&z, args, zyargs)
   641  			if err != nil {
   642  				return zygo.SexpNull, err
   643  			}
   644  			a.entryType = args[0].value.(string)
   645  			a.entry = &GobEntry{C: args[1].value.(string)}
   646  			var r interface{}
   647  			r, err = a.Call(h)
   648  			if err != nil {
   649  				return zygo.SexpNull, err
   650  			}
   651  			var entryHash Hash
   652  			if r != nil {
   653  				entryHash = r.(Hash)
   654  			}
   655  			var result = zygo.SexpStr{S: entryHash.String()}
   656  			return &result, nil
   657  		})
   658  
   659  	z.env.AddFunction("getBridges",
   660  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   661  			a := &APIFnGetBridges{}
   662  			args := a.Args()
   663  			err := zyProcessArgs(&z, args, zyargs)
   664  			if err != nil {
   665  				return zygo.SexpNull, err
   666  			}
   667  			r, err := a.Call(h)
   668  			if err != nil {
   669  				return zygo.SexpNull, err
   670  			}
   671  			var zbridges *zygo.SexpArray
   672  
   673  			var bridges []zygo.Sexp
   674  			for _, b := range r.([]Bridge) {
   675  				var bridge *zygo.SexpHash
   676  				bridge, err = zygo.MakeHash(nil, "hash", env)
   677  				if err != nil {
   678  					return zygo.SexpNull, err
   679  				}
   680  				if b.Side == BridgeCallee {
   681  					err = bridge.HashSet(env.MakeSymbol("Side"), &zygo.SexpInt{Val: int64(b.Side)})
   682  					if err != nil {
   683  						return zygo.SexpNull, err
   684  					}
   685  					err = bridge.HashSet(env.MakeSymbol("Token"), &zygo.SexpStr{S: b.Token})
   686  					if err != nil {
   687  						return zygo.SexpNull, err
   688  					}
   689  				} else {
   690  					err = bridge.HashSet(env.MakeSymbol("Side"), &zygo.SexpInt{Val: int64(b.Side)})
   691  					if err != nil {
   692  						return zygo.SexpNull, err
   693  					}
   694  					err = bridge.HashSet(env.MakeSymbol("CalleeApp"), &zygo.SexpStr{S: b.CalleeApp.String()})
   695  					if err != nil {
   696  						return zygo.SexpNull, err
   697  					}
   698  					err = bridge.HashSet(env.MakeSymbol("CalleeName"), &zygo.SexpStr{S: b.CalleeName})
   699  					if err != nil {
   700  						return zygo.SexpNull, err
   701  					}
   702  				}
   703  				bridges = append(bridges, bridge)
   704  			}
   705  			zbridges = env.NewSexpArray(bridges)
   706  			return zbridges, err
   707  		})
   708  
   709  	z.env.AddFunction("send",
   710  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   711  			fn := &APIFnSend{}
   712  			a := &fn.action
   713  			args := fn.Args()
   714  			err := zyProcessArgs(&z, args, zyargs)
   715  			if err != nil {
   716  				return zygo.SexpNull, err
   717  			}
   718  
   719  			a.to, err = peer.IDB58Decode(args[0].value.(Hash).String())
   720  			if err != nil {
   721  				return zygo.SexpNull, err
   722  			}
   723  
   724  			msg := args[1].value.(map[string]interface{})
   725  			var j []byte
   726  			j, err = json.Marshal(msg)
   727  			if err != nil {
   728  				return zygo.SexpNull, err
   729  			}
   730  
   731  			a.msg.ZomeType = z.zome.Name
   732  			a.msg.Body = string(j)
   733  
   734  			if args[2].value != nil {
   735  				a.options = &SendOptions{}
   736  				opts := args[2].value.(map[string]interface{})
   737  				cbmap, ok := opts["Callback"]
   738  				if ok {
   739  					callback := Callback{zomeType: zome.Name}
   740  					v, ok := cbmap.(map[string]interface{})["Function"]
   741  					if !ok {
   742  						return zygo.SexpNull, errors.New("callback option requires Function")
   743  					}
   744  					callback.Function = v.(string)
   745  					v, ok = cbmap.(map[string]interface{})["ID"]
   746  					if !ok {
   747  						return zygo.SexpNull, errors.New("callback option requires ID")
   748  					}
   749  					callback.ID = v.(string)
   750  					a.options.Callback = &callback
   751  				}
   752  				timeout, ok := opts["Timeout"]
   753  				if ok {
   754  					a.options.Timeout = int(timeout.(int64))
   755  				}
   756  			}
   757  
   758  			var r interface{}
   759  			r, err = fn.Call(h)
   760  			var resp zygo.Sexp
   761  			if err == nil {
   762  				switch t := r.(type) {
   763  				case string:
   764  					resp = &zygo.SexpStr{S: t}
   765  				case nil:
   766  					resp = zygo.SexpNull
   767  				default:
   768  					return zygo.SexpNull, errors.New("send should return nil or string")
   769  				}
   770  			}
   771  			return makeResult(env, resp, err)
   772  		})
   773  
   774  	z.env.AddFunction("call",
   775  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   776  			a := &APIFnCall{}
   777  			args := a.Args()
   778  			err := zyProcessArgs(&z, args, zyargs)
   779  			if err != nil {
   780  				return zygo.SexpNull, err
   781  			}
   782  			a.zome = args[0].value.(string)
   783  			var zome *Zome
   784  			zome, err = h.GetZome(a.zome)
   785  			if err != nil {
   786  				return zygo.SexpNull, err
   787  			}
   788  			a.function = args[1].value.(string)
   789  			var fn *FunctionDef
   790  			fn, err = zome.GetFunctionDef(a.function)
   791  			if err != nil {
   792  				return zygo.SexpNull, err
   793  			}
   794  			if fn.CallingType == JSON_CALLING {
   795  				switch zyargs[2].(type) {
   796  				case *zygo.SexpHash:
   797  					a.args = args[2].value
   798  				default:
   799  					return zygo.SexpNull, errors.New("function calling type requires hash argument type")
   800  				}
   801  
   802  			} else {
   803  				a.args = args[2].value.(string)
   804  			}
   805  			var r interface{}
   806  			r, err = a.Call(h)
   807  			if err != nil {
   808  				return zygo.SexpNull, err
   809  			}
   810  			return &zygo.SexpStr{S: r.(string)}, err
   811  		})
   812  
   813  	z.env.AddFunction("bridge",
   814  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   815  			a := &APIFnBridge{}
   816  			args := a.Args()
   817  			err := zyProcessArgs(&z, args, zyargs)
   818  			if err != nil {
   819  				return zygo.SexpNull, err
   820  			}
   821  			hash := args[0].value.(Hash)
   822  			a.token, a.url, err = h.GetBridgeToken(hash)
   823  			if err != nil {
   824  				return zygo.SexpNull, err
   825  			}
   826  
   827  			a.zome = args[1].value.(string)
   828  			a.function = args[2].value.(string)
   829  			a.args = args[3].value.(string)
   830  
   831  			var r interface{}
   832  			r, err = a.Call(h)
   833  			if err != nil {
   834  				return zygo.SexpNull, err
   835  			}
   836  
   837  			return &zygo.SexpStr{S: r.(string)}, err
   838  		})
   839  
   840  	z.env.AddFunction("commit",
   841  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   842  			a := &APIFnCommit{}
   843  			args := a.Args()
   844  			err := zyProcessArgs(&z, args, zyargs)
   845  			if err != nil {
   846  				return zygo.SexpNull, err
   847  			}
   848  			entryType := args[0].value.(string)
   849  			entry := args[1].value.(string)
   850  			var r interface{}
   851  			e := GobEntry{C: entry}
   852  			a.action.entryType = entryType
   853  			a.action.entry = &e
   854  			r, err = a.Call(h)
   855  			if err != nil {
   856  				return zygo.SexpNull, err
   857  			}
   858  			var entryHash Hash
   859  			if r != nil {
   860  				entryHash = r.(Hash)
   861  			}
   862  			var result = zygo.SexpStr{S: entryHash.String()}
   863  			return &result, nil
   864  		})
   865  
   866  	z.env.AddFunction("migrate",
   867  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   868  			fn := &APIFnMigrate{}
   869  			args := fn.Args()
   870  			err := zyProcessArgs(&z, args, zyargs)
   871  			if err != nil {
   872  				return zygo.SexpNull, err
   873  			}
   874  
   875  			migrationType := args[0].value.(string)
   876  			DNAHash := args[1].value.(Hash)
   877  			Key := args[2].value.(Hash)
   878  			Data := args[3].value.(string)
   879  			var r interface{}
   880  			fn.action.entry.Type = migrationType
   881  			fn.action.entry.DNAHash = DNAHash
   882  			fn.action.entry.Key = Key
   883  			fn.action.entry.Data = Data
   884  
   885  			r, err = fn.Call(h)
   886  			if err != nil {
   887  				return zygo.SexpNull, err
   888  			}
   889  			var entryHash Hash
   890  			if r != nil {
   891  				entryHash = r.(Hash)
   892  			}
   893  
   894  			var result = zygo.SexpStr{S: entryHash.String()}
   895  			return &result, nil
   896  		})
   897  
   898  	z.env.AddFunction("query",
   899  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   900  			a := &APIFnQuery{}
   901  			args := a.Args()
   902  			err := zyProcessArgs(&z, args, zyargs)
   903  			if err != nil {
   904  				return zygo.SexpNull, err
   905  			}
   906  
   907  			if len(zyargs) == 1 {
   908  				options := QueryOptions{}
   909  				var j []byte
   910  				j, err = json.Marshal(args[0].value)
   911  				if err != nil {
   912  					return zygo.SexpNull, err
   913  				}
   914  				z.h.Debugf("Query options: %s", string(j))
   915  				err = json.Unmarshal(j, &options)
   916  				if err != nil {
   917  					return zygo.SexpNull, err
   918  				}
   919  				a.options = &options
   920  			}
   921  
   922  			r, err := a.Call(h)
   923  			if err != nil {
   924  				return zygo.SexpNull, err
   925  			}
   926  			qr := r.([]QueryResult)
   927  
   928  			defs := make(map[string]*EntryDef)
   929  			results := make([]zygo.Sexp, len(qr))
   930  			for i, result := range qr {
   931  				var sexp zygo.Sexp
   932  				var hashSexp, entrySexp *zygo.SexpStr
   933  				var headerSexp *zygo.SexpHash
   934  				var returnCount int
   935  				if a.options.Return.Hashes {
   936  					returnCount += 1
   937  					hashSexp = &zygo.SexpStr{S: result.Header.EntryLink.String()}
   938  					sexp = hashSexp
   939  				}
   940  				if a.options.Return.Headers {
   941  					returnCount += 1
   942  					headerSexp, err = zygo.MakeHash(nil, "hash", env)
   943  					if err != nil {
   944  						return zygo.SexpNull, err
   945  					}
   946  					sexp = headerSexp
   947  					// TODO REFACTOR!!
   948  					err = headerSexp.HashSet(env.MakeSymbol("Time"), &zygo.SexpStr{S: fmt.Sprintf("%v", result.Header.Time)})
   949  					if err != nil {
   950  						return zygo.SexpNull, err
   951  					}
   952  					err = headerSexp.HashSet(env.MakeSymbol("Type"), &zygo.SexpStr{S: result.Header.Type})
   953  					if err != nil {
   954  						return zygo.SexpNull, err
   955  					}
   956  					err = headerSexp.HashSet(env.MakeSymbol("EntryLink"), &zygo.SexpStr{S: result.Header.EntryLink.String()})
   957  					if err != nil {
   958  						return zygo.SexpNull, err
   959  					}
   960  					err = headerSexp.HashSet(env.MakeSymbol("HeaderLink"), &zygo.SexpStr{S: result.Header.HeaderLink.String()})
   961  					if err != nil {
   962  						return zygo.SexpNull, err
   963  					}
   964  					err = headerSexp.HashSet(env.MakeSymbol("TypeLink"), &zygo.SexpStr{S: result.Header.TypeLink.String()})
   965  					if err != nil {
   966  						return zygo.SexpNull, err
   967  					}
   968  				}
   969  
   970  				if a.options.Return.Entries {
   971  					returnCount += 1
   972  
   973  					var def *EntryDef
   974  					var ok bool
   975  					def, ok = defs[result.Header.Type]
   976  					if !ok {
   977  						_, def, err = h.GetEntryDef(result.Header.Type)
   978  						if err != nil {
   979  							return zygo.SexpNull, err
   980  						}
   981  						defs[result.Header.Type] = def
   982  					}
   983  					var content string
   984  					switch def.DataFormat {
   985  					case DataFormatRawZygo:
   986  						fallthrough
   987  					case DataFormatRawJS:
   988  						fallthrough
   989  					case DataFormatString:
   990  						fallthrough
   991  					case DataFormatLinks:
   992  						fallthrough
   993  					case DataFormatJSON:
   994  						content = result.Entry.Content().(string)
   995  					default:
   996  						return zygo.SexpNull, fmt.Errorf("data format not implemented: %s", def.DataFormat)
   997  					}
   998  					entrySexp = &zygo.SexpStr{S: content}
   999  					sexp = entrySexp
  1000  
  1001  				}
  1002  				if returnCount > 1 {
  1003  					var result *zygo.SexpHash
  1004  					result, err = zygo.MakeHash(nil, "hash", env)
  1005  					if err == nil && headerSexp != nil {
  1006  						err = result.HashSet(env.MakeSymbol("Header"), headerSexp)
  1007  					}
  1008  					if err == nil && hashSexp != nil {
  1009  						err = result.HashSet(env.MakeSymbol("Hash"), hashSexp)
  1010  					}
  1011  					if err == nil && entrySexp != nil {
  1012  						err = result.HashSet(env.MakeSymbol("Entry"), entrySexp)
  1013  					}
  1014  					if err != nil {
  1015  						return zygo.SexpNull, err
  1016  					}
  1017  					sexp = result
  1018  				}
  1019  				results[i] = sexp
  1020  			}
  1021  
  1022  			return env.NewSexpArray(results), nil
  1023  		})
  1024  
  1025  	z.env.AddFunction("get",
  1026  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
  1027  			fn := &APIFnGet{}
  1028  			args := fn.Args()
  1029  			err := zyProcessArgs(&z, args, zyargs)
  1030  			if err != nil {
  1031  				return zygo.SexpNull, err
  1032  			}
  1033  			options := GetOptions{StatusMask: StatusDefault, GetMask: GetMaskDefault}
  1034  			if len(zyargs) == 2 {
  1035  				opts := args[1].value.(map[string]interface{})
  1036  				mask, ok := opts["StatusMask"]
  1037  				if ok {
  1038  					maskval, ok := mask.(float64)
  1039  					if !ok {
  1040  						return zygo.SexpNull,
  1041  							fmt.Errorf("expecting int StatusMask attribute, got %T", mask)
  1042  					}
  1043  					options.StatusMask = int(maskval)
  1044  				}
  1045  				mask, ok = opts["GetMask"]
  1046  				if ok {
  1047  					maskval, ok := mask.(float64)
  1048  					if !ok {
  1049  						return zygo.SexpNull,
  1050  							fmt.Errorf("expecting int GetMask attribute, got %T", mask)
  1051  					}
  1052  					options.GetMask = int(maskval)
  1053  				}
  1054  				local, ok := opts["Local"]
  1055  				if ok {
  1056  					options.Local = local.(bool)
  1057  				}
  1058  
  1059  			}
  1060  			req := GetReq{H: args[0].value.(Hash), StatusMask: options.StatusMask, GetMask: options.GetMask}
  1061  
  1062  			var r interface{}
  1063  			r, err = callGet(h, req, &options)
  1064  			mask := options.GetMask
  1065  			if mask == GetMaskDefault {
  1066  				mask = GetMaskEntry
  1067  			}
  1068  			var resultValue zygo.Sexp
  1069  			resultValue = zygo.SexpNull
  1070  			if err == ErrHashNotFound {
  1071  				// if the hash wasn't found this isn't actually an error
  1072  				// so return nil which is the same as HC_HashNotFound
  1073  				err = nil
  1074  				return zygo.SexpNull, err
  1075  			} else if err == nil {
  1076  				getResp := r.(GetResp)
  1077  				var entryStr string
  1078  				var singleValueReturn bool
  1079  				if mask&GetMaskEntry != 0 {
  1080  					j, err := json.Marshal(getResp.Entry.Content())
  1081  					if err == nil {
  1082  						if GetMaskEntry == mask {
  1083  							singleValueReturn = true
  1084  							resultValue = &zygo.SexpStr{S: string(j)}
  1085  						} else {
  1086  							entryStr = string(j)
  1087  						}
  1088  					}
  1089  				}
  1090  				if mask&GetMaskEntryType != 0 {
  1091  					if GetMaskEntryType == mask {
  1092  						singleValueReturn = true
  1093  						resultValue = &zygo.SexpStr{S: getResp.EntryType}
  1094  					}
  1095  				}
  1096  				var zSources *zygo.SexpArray
  1097  				if mask&GetMaskSources != 0 {
  1098  					sources := make([]zygo.Sexp, len(getResp.Sources))
  1099  					for i := range getResp.Sources {
  1100  						sources[i] = &zygo.SexpStr{S: getResp.Sources[i]}
  1101  					}
  1102  					zSources = env.NewSexpArray(sources)
  1103  					if GetMaskSources == mask {
  1104  						singleValueReturn = true
  1105  						resultValue = zSources
  1106  					}
  1107  				}
  1108  				if err == nil && !singleValueReturn {
  1109  					// build the return object
  1110  					var respObj *zygo.SexpHash
  1111  					respObj, err = zygo.MakeHash(nil, "hash", env)
  1112  					if err == nil {
  1113  						resultValue = respObj
  1114  						if mask&GetMaskEntry != 0 {
  1115  							err = respObj.HashSet(env.MakeSymbol("Entry"), &zygo.SexpStr{S: entryStr})
  1116  						}
  1117  						if err == nil && mask&GetMaskEntryType != 0 {
  1118  							err = respObj.HashSet(env.MakeSymbol("EntryType"), &zygo.SexpStr{S: getResp.EntryType})
  1119  						}
  1120  						if err == nil && mask&GetMaskSources != 0 {
  1121  							err = respObj.HashSet(env.MakeSymbol("Sources"), zSources)
  1122  						}
  1123  					}
  1124  				}
  1125  			}
  1126  			return makeResult(env, resultValue, err)
  1127  		})
  1128  
  1129  	z.env.AddFunction("update",
  1130  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
  1131  			fn := &APIFnMod{}
  1132  			args := fn.Args()
  1133  			err := zyProcessArgs(&z, args, zyargs)
  1134  			if err != nil {
  1135  				return zygo.SexpNull, err
  1136  			}
  1137  			entryType := args[0].value.(string)
  1138  			entryStr := args[1].value.(string)
  1139  			replaces := args[2].value.(Hash)
  1140  
  1141  			entry := GobEntry{C: entryStr}
  1142  			fn.action = *NewModAction(entryType, &entry, replaces)
  1143  			resp, err := fn.Call(h)
  1144  			if err != nil {
  1145  				return zygo.SexpNull, err
  1146  			}
  1147  			var entryHash Hash
  1148  			if resp != nil {
  1149  				entryHash = resp.(Hash)
  1150  			}
  1151  			var result = zygo.SexpStr{S: entryHash.String()}
  1152  			return &result, nil
  1153  		})
  1154  
  1155  	z.env.AddFunction("updateAgent",
  1156  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
  1157  			a := &APIFnModAgent{}
  1158  			//		var a Action = &ActionModAgent{}
  1159  			args := a.Args()
  1160  			err := zyProcessArgs(&z, args, zyargs)
  1161  			if err != nil {
  1162  				return zygo.SexpNull, err
  1163  			}
  1164  
  1165  			opts := args[0].value.(map[string]interface{})
  1166  			id, idok := opts["Identity"]
  1167  			if idok {
  1168  				a.Identity = AgentIdentity(id.(string))
  1169  			}
  1170  			rev, revok := opts["Revocation"]
  1171  			if revok {
  1172  				a.Revocation = rev.(string)
  1173  			}
  1174  
  1175  			resp, err := a.Call(h)
  1176  			if err != nil {
  1177  				return zygo.SexpNull, err
  1178  			}
  1179  			var agentEntryHash Hash
  1180  			if resp != nil {
  1181  				agentEntryHash = resp.(Hash)
  1182  			}
  1183  
  1184  			if revok {
  1185  				appKeyHash.S = h.nodeIDStr
  1186  			}
  1187  
  1188  			// there's always a new agent entry
  1189  			appAgentTopHash.S = h.agentTopHash.String()
  1190  
  1191  			// but not always a new identity to update
  1192  			if idok {
  1193  				appAgentStr.S = string(a.Identity)
  1194  			}
  1195  			var result = zygo.SexpStr{S: agentEntryHash.String()}
  1196  			return &result, nil
  1197  		})
  1198  
  1199  	z.env.AddFunction("remove",
  1200  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
  1201  			fn := &APIFnDel{}
  1202  			args := fn.Args()
  1203  			err := zyProcessArgs(&z, args, zyargs)
  1204  			if err != nil {
  1205  				return zygo.SexpNull, err
  1206  			}
  1207  			entry := DelEntry{
  1208  				Hash:    args[0].value.(Hash),
  1209  				Message: args[1].value.(string),
  1210  			}
  1211  			fn.action = *NewDelAction(entry)
  1212  			resp, err := fn.Call(h)
  1213  			if err != nil {
  1214  				return zygo.SexpNull, err
  1215  			}
  1216  			var entryHash Hash
  1217  			if resp != nil {
  1218  				entryHash = resp.(Hash)
  1219  			}
  1220  			return &zygo.SexpStr{S: entryHash.String()}, err
  1221  
  1222  			return zygo.SexpNull, err
  1223  		})
  1224  
  1225  	z.env.AddFunction("getLinks",
  1226  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
  1227  			fn := &APIFnGetLinks{}
  1228  			args := fn.Args()
  1229  			err := zyProcessArgs(&z, args, zyargs)
  1230  			if err != nil {
  1231  				return zygo.SexpNull, err
  1232  			}
  1233  			base := args[0].value.(Hash)
  1234  			tag := args[1].value.(string)
  1235  
  1236  			options := GetLinksOptions{Load: false, StatusMask: StatusLive}
  1237  			if len(zyargs) == 3 {
  1238  				opts := args[2].value.(map[string]interface{})
  1239  				load, ok := opts["Load"]
  1240  				if ok {
  1241  					loadval, ok := load.(bool)
  1242  					if !ok {
  1243  						return zygo.SexpNull,
  1244  							fmt.Errorf("expecting boolean Load attribute in object, got %T", load)
  1245  					}
  1246  					options.Load = loadval
  1247  				}
  1248  				mask, ok := opts["StatusMask"]
  1249  				if ok {
  1250  					maskval, ok := mask.(float64)
  1251  					if !ok {
  1252  						return zygo.SexpNull,
  1253  							fmt.Errorf("expecting int StatusMask attribute in object, got %T", mask)
  1254  					}
  1255  					options.StatusMask = int(maskval)
  1256  				}
  1257  			}
  1258  
  1259  			var r interface{}
  1260  			fn.action = *NewGetLinksAction(&LinkQuery{Base: base, T: tag, StatusMask: options.StatusMask}, &options)
  1261  			r, err = fn.Call(h)
  1262  			var resultValue zygo.Sexp
  1263  			if err == nil {
  1264  				response := r.(*LinkQueryResp)
  1265  				resultValue = zygo.SexpNull
  1266  				var j []byte
  1267  				j, err = json.Marshal(response.Links)
  1268  				if err == nil {
  1269  					resultValue = &zygo.SexpStr{S: string(j)}
  1270  				}
  1271  			}
  1272  			return makeResult(env, resultValue, err)
  1273  		})
  1274  
  1275  	l := ZygoLibrary
  1276  	if h != nil {
  1277  		z.env.AddGlobal("App_Name", &zygo.SexpStr{S: h.Name()})
  1278  		z.env.AddGlobal("App_DNA_Hash", &zygo.SexpStr{S: h.dnaHash.String()})
  1279  		z.env.AddGlobal("App_Key_Hash", &appKeyHash)
  1280  		z.env.AddGlobal("App_Agent_String", &appAgentStr)
  1281  		z.env.AddGlobal("App_Agent_Hash", &appAgentHash)
  1282  		z.env.AddGlobal("App_Agent_TopHash", &appAgentTopHash)
  1283  	}
  1284  	z.library = l
  1285  
  1286  	_, err = z.Run(l + zome.Code)
  1287  	if err != nil {
  1288  		return
  1289  	}
  1290  	n = &z
  1291  	return
  1292  }
  1293  
  1294  // Run executes zygo code
  1295  func (z *ZygoRibosome) Run(code string) (result interface{}, err error) {
  1296  	c := fmt.Sprintf("(begin %s %s)", z.library, code)
  1297  	err = z.env.LoadString(c)
  1298  	if err != nil {
  1299  		err = errors.New("Zygomys load error: " + err.Error())
  1300  		return
  1301  	}
  1302  	var sexp zygo.Sexp
  1303  	sexp, err = z.env.Run()
  1304  	if err != nil {
  1305  		err = errors.New("Zygomys exec error: " + err.Error())
  1306  		return
  1307  	}
  1308  	z.lastResult = sexp
  1309  	result = sexp
  1310  	return
  1311  }
  1312  
  1313  // extra functions we want to have available for app developers in zygo
  1314  
  1315  func isPrime(t int64) bool {
  1316  
  1317  	// math.Mod requires floats.
  1318  	x := float64(t)
  1319  
  1320  	// 1 or less aren't primes.
  1321  	if x <= 1 {
  1322  		return false
  1323  	}
  1324  
  1325  	// Solve half of the integer set directly
  1326  	if math.Mod(x, 2) == 0 {
  1327  		return x == 2
  1328  	}
  1329  
  1330  	// Main loop. i needs to be float because of math.Mod.
  1331  	for i := 3.0; i <= math.Floor(math.Sqrt(x)); i += 2.0 {
  1332  		if math.Mod(x, i) == 0 {
  1333  			return false
  1334  		}
  1335  	}
  1336  
  1337  	// It's a prime!
  1338  	return true
  1339  }
  1340  
  1341  func addExtras(z *ZygoRibosome) {
  1342  	z.env.AddFunction("isprime",
  1343  		func(env *zygo.Zlisp, name string, args []zygo.Sexp) (zygo.Sexp, error) {
  1344  
  1345  			switch t := args[0].(type) {
  1346  			case *zygo.SexpInt:
  1347  				return &zygo.SexpBool{Val: isPrime(t.Val)}, nil
  1348  			default:
  1349  				return zygo.SexpNull,
  1350  					errors.New("argument to isprime should be int")
  1351  			}
  1352  		})
  1353  	z.env.AddFunction("atoi",
  1354  		func(env *zygo.Zlisp, name string, args []zygo.Sexp) (zygo.Sexp, error) {
  1355  
  1356  			var i int64
  1357  			var e error
  1358  			switch t := args[0].(type) {
  1359  			case *zygo.SexpStr:
  1360  				i, e = strconv.ParseInt(t.S, 10, 64)
  1361  				if e != nil {
  1362  					return zygo.SexpNull, e
  1363  				}
  1364  			default:
  1365  				return zygo.SexpNull,
  1366  					errors.New("argument to atoi should be string")
  1367  			}
  1368  
  1369  			return &zygo.SexpInt{Val: i}, nil
  1370  		})
  1371  }
  1372  
  1373  func (z *ZygoRibosome) RunAsyncSendResponse(response AppMsg, callback string, callbackID string) (result interface{}, err error) {
  1374  	code := fmt.Sprintf(`(%s (unjson (raw "%s")) "%s")`, callback, sanitizeZyString(response.Body), sanitizeZyString(callbackID))
  1375  	z.h.Debugf("Calling %s\n", code)
  1376  	result, err = z.Run(code)
  1377  	return
  1378  }