github.com/holochain/holochain-proto@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 }