github.com/clarenceb/holochain-proto@v0.0.1-alpha.0.20180918132555-dff351593ded/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_From ` + BridgeFromStr + ")" + 398 `(def HC_Bridge_To ` + BridgeToStr + ")" + 399 400 `(def HC_LinkAction_Add "` + AddAction + "\")" + 401 `(def HC_LinkAction_Del "` + DelAction + "\")" + 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 409 func makeResult(env *zygo.Zlisp, resultValue zygo.Sexp, resultError error) (zygo.Sexp, error) { 410 result, err := zygo.MakeHash(nil, "hash", env) 411 if err != nil { 412 return nil, err 413 } 414 if resultError != nil { 415 err = result.HashSet(env.MakeSymbol("error"), &zygo.SexpStr{S: resultError.Error()}) 416 } else { 417 err = result.HashSet(env.MakeSymbol("result"), resultValue) 418 } 419 return result, err 420 } 421 422 // cleanZygoJson removes zygos crazy crap 423 func cleanZygoJson(s string) string { 424 s = strings.Replace(s, `"Atype":"hash", `, "", -1) 425 re := regexp.MustCompile(`, "zKeyOrder":\[[^\]]+\]`) 426 s = string(re.ReplaceAll([]byte(s), []byte(""))) 427 s = strings.Replace(s, `", "`, `","`, -1) 428 return s 429 } 430 431 func zyProcessArgs(z *ZygoRibosome, args []Arg, zyArgs []zygo.Sexp) (err error) { 432 err = checkArgCount(args, len(zyArgs)) 433 if err != nil { 434 return err 435 } 436 437 // check arg types 438 for i, a := range zyArgs { 439 switch args[i].Type { 440 case StringArg: 441 var str string 442 switch t := a.(type) { 443 case *zygo.SexpStr: 444 str = t.S 445 args[i].value = str 446 default: 447 return argErr("string", i+1, args[i]) 448 } 449 case HashArg: 450 switch t := a.(type) { 451 case *zygo.SexpStr: 452 var hash Hash 453 hash, err = NewHash(t.S) 454 if err != nil { 455 return 456 } 457 args[i].value = hash 458 default: 459 return argErr("string", i+1, args[i]) 460 } 461 case IntArg: 462 var integer int64 463 switch t := a.(type) { 464 case *zygo.SexpInt: 465 integer = t.Val 466 args[i].value = integer 467 default: 468 return argErr("int", i+1, args[i]) 469 } 470 case BoolArg: 471 var boolean bool 472 switch t := a.(type) { 473 case *zygo.SexpBool: 474 boolean = t.Val 475 args[i].value = boolean 476 default: 477 return argErr("boolean", i+1, args[i]) 478 } 479 case ArgsArg: 480 switch t := a.(type) { 481 case *zygo.SexpStr: 482 args[i].value = t.S 483 case *zygo.SexpHash: 484 args[i].value = cleanZygoJson(zygo.SexpToJson(t)) 485 default: 486 return argErr("string or hash", i+1, args[i]) 487 } 488 case EntryArg: 489 // this a special case in that all EntryArgs must be preceeded by 490 // string arg that specifies the entry type 491 492 // don't have to do checking because the previous time through the loop 493 // should have done it 494 entryType := zyArgs[i-1].(*zygo.SexpStr).S 495 _, def, err := z.h.GetEntryDef(entryType) 496 if err != nil { 497 return err 498 } 499 500 var entry string 501 switch def.DataFormat { 502 case DataFormatRawZygo: 503 fallthrough 504 case DataFormatRawJS: 505 fallthrough 506 case DataFormatString: 507 switch t := a.(type) { 508 case *zygo.SexpStr: 509 entry = t.S 510 default: 511 return argErr("string", i+1, args[i]) 512 } 513 case DataFormatLinks: 514 switch t := a.(type) { 515 case *zygo.SexpHash: 516 entry = cleanZygoJson(zygo.SexpToJson(t)) 517 default: 518 return argErr("hash", i+1, args[i]) 519 } 520 case DataFormatJSON: 521 switch a.(type) { 522 case *zygo.SexpSentinel: 523 entry = "undefined" 524 default: 525 entry = cleanZygoJson(zygo.SexpToJson(a)) 526 } 527 default: 528 err = errors.New("data format not implemented: " + def.DataFormat) 529 return err 530 } 531 args[i].value = entry 532 533 case MapArg: 534 switch t := a.(type) { 535 case *zygo.SexpHash: 536 j := cleanZygoJson(zygo.SexpToJson(t)) 537 m := make(map[string]interface{}) 538 var err = json.Unmarshal([]byte(j), &m) 539 if err != nil { 540 return err 541 } 542 args[i].value = m 543 default: 544 return argErr("hash", i+1, args[i]) 545 } 546 case ToStrArg: 547 var str string 548 549 switch t := a.(type) { 550 case *zygo.SexpStr: 551 str = t.S 552 case *zygo.SexpInt: 553 str = fmt.Sprintf("%d", t.Val) 554 case *zygo.SexpBool: 555 if t.Val { 556 str = "true" 557 } else { 558 str = "false" 559 } 560 case *zygo.SexpHash: 561 str = cleanZygoJson(zygo.SexpToJson(t)) 562 case *zygo.SexpArray: 563 str = cleanZygoJson(zygo.SexpToJson(t)) 564 default: 565 return argErr("int, boolean, string, array or hash", i+1, args[i]) 566 } 567 args[i].value = str 568 } 569 } 570 571 return 572 } 573 574 // NewZygoRibosome factory function to build a zygo execution environment for a zome 575 func NewZygoRibosome(h *Holochain, zome *Zome) (n Ribosome, err error) { 576 z := ZygoRibosome{ 577 h: h, 578 zome: zome, 579 env: zygo.NewZlispSandbox(), 580 } 581 582 z.env.AddFunction("version", 583 func(env *zygo.Zlisp, name string, args []zygo.Sexp) (zygo.Sexp, error) { 584 return &zygo.SexpStr{S: VersionStr}, nil 585 }) 586 587 addExtras(&z) 588 589 var appKeyHash, appAgentStr, appAgentHash, appAgentTopHash zygo.SexpStr 590 if h != nil { 591 appKeyHash = zygo.SexpStr{S: h.nodeIDStr} 592 appAgentStr = zygo.SexpStr{S: sanitizeZyString(string(h.Agent().Identity()))} 593 appAgentHash = zygo.SexpStr{S: h.agentHash.String()} 594 appAgentTopHash = zygo.SexpStr{S: h.agentTopHash.String()} 595 } 596 597 // use a closure so that the registered zygo function can call Expose on the correct ZygoRibosome obj 598 599 z.env.AddFunction("property", 600 func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) { 601 a := &ActionProperty{} 602 args := a.Args() 603 err := zyProcessArgs(&z, args, zyargs) 604 if err != nil { 605 return zygo.SexpNull, err 606 } 607 608 a.prop = args[0].value.(string) 609 610 var p interface{} 611 p, err = a.Do(h) 612 613 if err != nil { 614 return zygo.SexpNull, err 615 } 616 result := zygo.SexpStr{S: p.(string)} 617 return &result, err 618 }) 619 620 z.env.AddFunction("debug", 621 func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) { 622 a := &ActionDebug{} 623 args := a.Args() 624 err := zyProcessArgs(&z, args, zyargs) 625 if err != nil { 626 return zygo.SexpNull, err 627 } 628 a.msg = args[0].value.(string) 629 a.Do(h) 630 return zygo.SexpNull, err 631 }) 632 633 z.env.AddFunction("makeHash", 634 func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) { 635 a := &ActionMakeHash{} 636 args := a.Args() 637 err := zyProcessArgs(&z, args, zyargs) 638 if err != nil { 639 return zygo.SexpNull, err 640 } 641 a.entryType = args[0].value.(string) 642 a.entry = &GobEntry{C: args[1].value.(string)} 643 var r interface{} 644 r, err = a.Do(h) 645 if err != nil { 646 return zygo.SexpNull, err 647 } 648 var entryHash Hash 649 if r != nil { 650 entryHash = r.(Hash) 651 } 652 var result = zygo.SexpStr{S: entryHash.String()} 653 return &result, nil 654 }) 655 656 z.env.AddFunction("getBridges", 657 func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) { 658 a := &ActionGetBridges{} 659 args := a.Args() 660 err := zyProcessArgs(&z, args, zyargs) 661 if err != nil { 662 return zygo.SexpNull, err 663 } 664 r, err := a.Do(h) 665 if err != nil { 666 return zygo.SexpNull, err 667 } 668 var zbridges *zygo.SexpArray 669 670 var bridges []zygo.Sexp 671 for _, b := range r.([]Bridge) { 672 var bridge *zygo.SexpHash 673 bridge, err = zygo.MakeHash(nil, "hash", env) 674 if err != nil { 675 return zygo.SexpNull, err 676 } 677 if b.Side == BridgeTo { 678 err = bridge.HashSet(env.MakeSymbol("Side"), &zygo.SexpInt{Val: int64(b.Side)}) 679 if err != nil { 680 return zygo.SexpNull, err 681 } 682 err = bridge.HashSet(env.MakeSymbol("Token"), &zygo.SexpStr{S: b.Token}) 683 if err != nil { 684 return zygo.SexpNull, err 685 } 686 } else { 687 err = bridge.HashSet(env.MakeSymbol("Side"), &zygo.SexpInt{Val: int64(b.Side)}) 688 if err != nil { 689 return zygo.SexpNull, err 690 } 691 err = bridge.HashSet(env.MakeSymbol("ToApp"), &zygo.SexpStr{S: b.ToApp.String()}) 692 if err != nil { 693 return zygo.SexpNull, err 694 } 695 } 696 bridges = append(bridges, bridge) 697 } 698 zbridges = env.NewSexpArray(bridges) 699 return zbridges, err 700 }) 701 702 z.env.AddFunction("send", 703 func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) { 704 a := &ActionSend{} 705 args := a.Args() 706 err := zyProcessArgs(&z, args, zyargs) 707 if err != nil { 708 return zygo.SexpNull, err 709 } 710 711 a.to, err = peer.IDB58Decode(args[0].value.(Hash).String()) 712 if err != nil { 713 return zygo.SexpNull, err 714 } 715 716 msg := args[1].value.(map[string]interface{}) 717 var j []byte 718 j, err = json.Marshal(msg) 719 if err != nil { 720 return zygo.SexpNull, err 721 } 722 723 a.msg.ZomeType = z.zome.Name 724 a.msg.Body = string(j) 725 726 if args[2].value != nil { 727 a.options = &SendOptions{} 728 opts := args[2].value.(map[string]interface{}) 729 cbmap, ok := opts["Callback"] 730 if ok { 731 callback := Callback{zomeType: zome.Name} 732 v, ok := cbmap.(map[string]interface{})["Function"] 733 if !ok { 734 return zygo.SexpNull, errors.New("callback option requires Function") 735 } 736 callback.Function = v.(string) 737 v, ok = cbmap.(map[string]interface{})["ID"] 738 if !ok { 739 return zygo.SexpNull, errors.New("callback option requires ID") 740 } 741 callback.ID = v.(string) 742 a.options.Callback = &callback 743 } 744 timeout, ok := opts["Timeout"] 745 if ok { 746 a.options.Timeout = int(timeout.(int64)) 747 } 748 } 749 750 var r interface{} 751 r, err = a.Do(h) 752 var resp zygo.Sexp 753 if err == nil { 754 switch t := r.(type) { 755 case string: 756 resp = &zygo.SexpStr{S: t} 757 case nil: 758 resp = zygo.SexpNull 759 default: 760 return zygo.SexpNull, errors.New("send should return nil or string") 761 } 762 } 763 return makeResult(env, resp, err) 764 }) 765 766 z.env.AddFunction("call", 767 func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) { 768 a := &ActionCall{} 769 args := a.Args() 770 err := zyProcessArgs(&z, args, zyargs) 771 if err != nil { 772 return zygo.SexpNull, err 773 } 774 a.zome = args[0].value.(string) 775 var zome *Zome 776 zome, err = h.GetZome(a.zome) 777 if err != nil { 778 return zygo.SexpNull, err 779 } 780 a.function = args[1].value.(string) 781 var fn *FunctionDef 782 fn, err = zome.GetFunctionDef(a.function) 783 if err != nil { 784 return zygo.SexpNull, err 785 } 786 if fn.CallingType == JSON_CALLING { 787 switch zyargs[2].(type) { 788 case *zygo.SexpHash: 789 a.args = args[2].value 790 default: 791 return zygo.SexpNull, errors.New("function calling type requires hash argument type") 792 } 793 794 } else { 795 a.args = args[2].value.(string) 796 } 797 var r interface{} 798 r, err = a.Do(h) 799 if err != nil { 800 return zygo.SexpNull, err 801 } 802 return &zygo.SexpStr{S: r.(string)}, err 803 }) 804 805 z.env.AddFunction("bridge", 806 func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) { 807 a := &ActionBridge{} 808 args := a.Args() 809 err := zyProcessArgs(&z, args, zyargs) 810 if err != nil { 811 return zygo.SexpNull, err 812 } 813 hash := args[0].value.(Hash) 814 a.token, a.url, err = h.GetBridgeToken(hash) 815 if err != nil { 816 return zygo.SexpNull, err 817 } 818 819 a.zome = args[1].value.(string) 820 a.function = args[2].value.(string) 821 a.args = args[3].value.(string) 822 823 var r interface{} 824 r, err = a.Do(h) 825 if err != nil { 826 return zygo.SexpNull, err 827 } 828 829 return &zygo.SexpStr{S: r.(string)}, err 830 }) 831 832 z.env.AddFunction("commit", 833 func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) { 834 var a Action = &ActionCommit{} 835 args := a.Args() 836 err := zyProcessArgs(&z, args, zyargs) 837 if err != nil { 838 return zygo.SexpNull, err 839 } 840 entryType := args[0].value.(string) 841 entry := args[1].value.(string) 842 var r interface{} 843 e := GobEntry{C: entry} 844 r, err = NewCommitAction(entryType, &e).Do(h) 845 if err != nil { 846 return zygo.SexpNull, err 847 } 848 var entryHash Hash 849 if r != nil { 850 entryHash = r.(Hash) 851 } 852 var result = zygo.SexpStr{S: entryHash.String()} 853 return &result, nil 854 }) 855 856 z.env.AddFunction("query", 857 func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) { 858 a := &ActionQuery{} 859 args := a.Args() 860 err := zyProcessArgs(&z, args, zyargs) 861 if err != nil { 862 return zygo.SexpNull, err 863 } 864 865 if len(zyargs) == 1 { 866 options := QueryOptions{} 867 var j []byte 868 j, err = json.Marshal(args[0].value) 869 if err != nil { 870 return zygo.SexpNull, err 871 } 872 z.h.Debugf("Query options: %s", string(j)) 873 err = json.Unmarshal(j, &options) 874 if err != nil { 875 return zygo.SexpNull, err 876 } 877 a.options = &options 878 } 879 880 r, err := a.Do(h) 881 if err != nil { 882 return zygo.SexpNull, err 883 } 884 qr := r.([]QueryResult) 885 886 defs := make(map[string]*EntryDef) 887 results := make([]zygo.Sexp, len(qr)) 888 for i, result := range qr { 889 var sexp zygo.Sexp 890 var hashSexp, entrySexp *zygo.SexpStr 891 var headerSexp *zygo.SexpHash 892 var returnCount int 893 if a.options.Return.Hashes { 894 returnCount += 1 895 hashSexp = &zygo.SexpStr{S: result.Header.EntryLink.String()} 896 sexp = hashSexp 897 } 898 if a.options.Return.Headers { 899 returnCount += 1 900 headerSexp, err = zygo.MakeHash(nil, "hash", env) 901 if err != nil { 902 return zygo.SexpNull, err 903 } 904 sexp = headerSexp 905 // TODO REFACTOR!! 906 err = headerSexp.HashSet(env.MakeSymbol("Time"), &zygo.SexpStr{S: fmt.Sprintf("%v", result.Header.Time)}) 907 if err != nil { 908 return zygo.SexpNull, err 909 } 910 err = headerSexp.HashSet(env.MakeSymbol("Type"), &zygo.SexpStr{S: result.Header.Type}) 911 if err != nil { 912 return zygo.SexpNull, err 913 } 914 err = headerSexp.HashSet(env.MakeSymbol("EntryLink"), &zygo.SexpStr{S: result.Header.EntryLink.String()}) 915 if err != nil { 916 return zygo.SexpNull, err 917 } 918 err = headerSexp.HashSet(env.MakeSymbol("HeaderLink"), &zygo.SexpStr{S: result.Header.HeaderLink.String()}) 919 if err != nil { 920 return zygo.SexpNull, err 921 } 922 err = headerSexp.HashSet(env.MakeSymbol("TypeLink"), &zygo.SexpStr{S: result.Header.TypeLink.String()}) 923 if err != nil { 924 return zygo.SexpNull, err 925 } 926 } 927 928 if a.options.Return.Entries { 929 returnCount += 1 930 931 var def *EntryDef 932 var ok bool 933 def, ok = defs[result.Header.Type] 934 if !ok { 935 _, def, err = h.GetEntryDef(result.Header.Type) 936 if err != nil { 937 return zygo.SexpNull, err 938 } 939 defs[result.Header.Type] = def 940 } 941 var content string 942 switch def.DataFormat { 943 case DataFormatRawZygo: 944 fallthrough 945 case DataFormatRawJS: 946 fallthrough 947 case DataFormatString: 948 fallthrough 949 case DataFormatLinks: 950 fallthrough 951 case DataFormatJSON: 952 content = result.Entry.Content().(string) 953 default: 954 return zygo.SexpNull, fmt.Errorf("data format not implemented: %s", def.DataFormat) 955 } 956 entrySexp = &zygo.SexpStr{S: content} 957 sexp = entrySexp 958 959 } 960 if returnCount > 1 { 961 var result *zygo.SexpHash 962 result, err = zygo.MakeHash(nil, "hash", env) 963 if err == nil && headerSexp != nil { 964 err = result.HashSet(env.MakeSymbol("Header"), headerSexp) 965 } 966 if err == nil && hashSexp != nil { 967 err = result.HashSet(env.MakeSymbol("Hash"), hashSexp) 968 } 969 if err == nil && entrySexp != nil { 970 err = result.HashSet(env.MakeSymbol("Entry"), entrySexp) 971 } 972 if err != nil { 973 return zygo.SexpNull, err 974 } 975 sexp = result 976 } 977 results[i] = sexp 978 } 979 980 return env.NewSexpArray(results), nil 981 }) 982 983 z.env.AddFunction("get", 984 func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) { 985 var a Action = &ActionGet{} 986 args := a.Args() 987 err := zyProcessArgs(&z, args, zyargs) 988 if err != nil { 989 return zygo.SexpNull, err 990 } 991 options := GetOptions{StatusMask: StatusDefault, GetMask: GetMaskDefault} 992 if len(zyargs) == 2 { 993 opts := args[1].value.(map[string]interface{}) 994 mask, ok := opts["StatusMask"] 995 if ok { 996 maskval, ok := mask.(float64) 997 if !ok { 998 return zygo.SexpNull, 999 fmt.Errorf("expecting int StatusMask attribute, got %T", mask) 1000 } 1001 options.StatusMask = int(maskval) 1002 } 1003 mask, ok = opts["GetMask"] 1004 if ok { 1005 maskval, ok := mask.(float64) 1006 if !ok { 1007 return zygo.SexpNull, 1008 fmt.Errorf("expecting int GetMask attribute, got %T", mask) 1009 } 1010 options.GetMask = int(maskval) 1011 } 1012 local, ok := opts["Local"] 1013 if ok { 1014 options.Local = local.(bool) 1015 } 1016 1017 } 1018 req := GetReq{H: args[0].value.(Hash), StatusMask: options.StatusMask, GetMask: options.GetMask} 1019 1020 var r interface{} 1021 r, err = NewGetAction(req, &options).Do(h) 1022 mask := options.GetMask 1023 if mask == GetMaskDefault { 1024 mask = GetMaskEntry 1025 } 1026 var resultValue zygo.Sexp 1027 resultValue = zygo.SexpNull 1028 if err == ErrHashNotFound { 1029 // if the hash wasn't found this isn't actually an error 1030 // so return nil which is the same as HC_HashNotFound 1031 err = nil 1032 return zygo.SexpNull, err 1033 } else if err == nil { 1034 getResp := r.(GetResp) 1035 var entryStr string 1036 var singleValueReturn bool 1037 if mask&GetMaskEntry != 0 { 1038 j, err := json.Marshal(getResp.Entry.Content()) 1039 if err == nil { 1040 if GetMaskEntry == mask { 1041 singleValueReturn = true 1042 resultValue = &zygo.SexpStr{S: string(j)} 1043 } else { 1044 entryStr = string(j) 1045 } 1046 } 1047 } 1048 if mask&GetMaskEntryType != 0 { 1049 if GetMaskEntryType == mask { 1050 singleValueReturn = true 1051 resultValue = &zygo.SexpStr{S: getResp.EntryType} 1052 } 1053 } 1054 var zSources *zygo.SexpArray 1055 if mask&GetMaskSources != 0 { 1056 sources := make([]zygo.Sexp, len(getResp.Sources)) 1057 for i := range getResp.Sources { 1058 sources[i] = &zygo.SexpStr{S: getResp.Sources[i]} 1059 } 1060 zSources = env.NewSexpArray(sources) 1061 if GetMaskSources == mask { 1062 singleValueReturn = true 1063 resultValue = zSources 1064 } 1065 } 1066 if err == nil && !singleValueReturn { 1067 // build the return object 1068 var respObj *zygo.SexpHash 1069 respObj, err = zygo.MakeHash(nil, "hash", env) 1070 if err == nil { 1071 resultValue = respObj 1072 if mask&GetMaskEntry != 0 { 1073 err = respObj.HashSet(env.MakeSymbol("Entry"), &zygo.SexpStr{S: entryStr}) 1074 } 1075 if err == nil && mask&GetMaskEntryType != 0 { 1076 err = respObj.HashSet(env.MakeSymbol("EntryType"), &zygo.SexpStr{S: getResp.EntryType}) 1077 } 1078 if err == nil && mask&GetMaskSources != 0 { 1079 err = respObj.HashSet(env.MakeSymbol("Sources"), zSources) 1080 } 1081 } 1082 } 1083 } 1084 return makeResult(env, resultValue, err) 1085 }) 1086 1087 z.env.AddFunction("update", 1088 func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) { 1089 var a Action = &ActionMod{} 1090 args := a.Args() 1091 err := zyProcessArgs(&z, args, zyargs) 1092 if err != nil { 1093 return zygo.SexpNull, err 1094 } 1095 entryType := args[0].value.(string) 1096 entryStr := args[1].value.(string) 1097 replaces := args[2].value.(Hash) 1098 1099 entry := GobEntry{C: entryStr} 1100 resp, err := NewModAction(entryType, &entry, replaces).Do(h) 1101 if err != nil { 1102 return zygo.SexpNull, err 1103 } 1104 var entryHash Hash 1105 if resp != nil { 1106 entryHash = resp.(Hash) 1107 } 1108 var result = zygo.SexpStr{S: entryHash.String()} 1109 return &result, nil 1110 }) 1111 1112 z.env.AddFunction("updateAgent", 1113 func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) { 1114 a := &ActionModAgent{} 1115 // var a Action = &ActionModAgent{} 1116 args := a.Args() 1117 err := zyProcessArgs(&z, args, zyargs) 1118 if err != nil { 1119 return zygo.SexpNull, err 1120 } 1121 1122 opts := args[0].value.(map[string]interface{}) 1123 id, idok := opts["Identity"] 1124 if idok { 1125 a.Identity = AgentIdentity(id.(string)) 1126 } 1127 rev, revok := opts["Revocation"] 1128 if revok { 1129 a.Revocation = rev.(string) 1130 } 1131 1132 resp, err := a.Do(h) 1133 if err != nil { 1134 return zygo.SexpNull, err 1135 } 1136 var agentEntryHash Hash 1137 if resp != nil { 1138 agentEntryHash = resp.(Hash) 1139 } 1140 1141 if revok { 1142 appKeyHash.S = h.nodeIDStr 1143 } 1144 1145 // there's always a new agent entry 1146 appAgentTopHash.S = h.agentTopHash.String() 1147 1148 // but not always a new identity to update 1149 if idok { 1150 appAgentStr.S = string(a.Identity) 1151 } 1152 var result = zygo.SexpStr{S: agentEntryHash.String()} 1153 return &result, nil 1154 }) 1155 1156 z.env.AddFunction("remove", 1157 func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) { 1158 var a Action = &ActionDel{} 1159 args := a.Args() 1160 err := zyProcessArgs(&z, args, zyargs) 1161 if err != nil { 1162 return zygo.SexpNull, err 1163 } 1164 entry := DelEntry{ 1165 Hash: args[0].value.(Hash), 1166 Message: args[1].value.(string), 1167 } 1168 header, err := h.chain.GetEntryHeader(entry.Hash) 1169 if err == nil { 1170 resp, err := NewDelAction(header.Type, entry).Do(h) 1171 if err != nil { 1172 return zygo.SexpNull, err 1173 } 1174 var entryHash Hash 1175 if resp != nil { 1176 entryHash = resp.(Hash) 1177 } 1178 return &zygo.SexpStr{S: entryHash.String()}, err 1179 } 1180 return zygo.SexpNull, err 1181 }) 1182 1183 z.env.AddFunction("getLinks", 1184 func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) { 1185 var a Action = &ActionGetLinks{} 1186 args := a.Args() 1187 err := zyProcessArgs(&z, args, zyargs) 1188 if err != nil { 1189 return zygo.SexpNull, err 1190 } 1191 base := args[0].value.(Hash) 1192 tag := args[1].value.(string) 1193 1194 options := GetLinksOptions{Load: false, StatusMask: StatusLive} 1195 if len(zyargs) == 3 { 1196 opts := args[2].value.(map[string]interface{}) 1197 load, ok := opts["Load"] 1198 if ok { 1199 loadval, ok := load.(bool) 1200 if !ok { 1201 return zygo.SexpNull, 1202 fmt.Errorf("expecting boolean Load attribute in object, got %T", load) 1203 } 1204 options.Load = loadval 1205 } 1206 mask, ok := opts["StatusMask"] 1207 if ok { 1208 maskval, ok := mask.(float64) 1209 if !ok { 1210 return zygo.SexpNull, 1211 fmt.Errorf("expecting int StatusMask attribute in object, got %T", mask) 1212 } 1213 options.StatusMask = int(maskval) 1214 } 1215 } 1216 1217 var r interface{} 1218 r, err = NewGetLinksAction(&LinkQuery{Base: base, T: tag, StatusMask: options.StatusMask}, &options).Do(h) 1219 var resultValue zygo.Sexp 1220 if err == nil { 1221 response := r.(*LinkQueryResp) 1222 resultValue = zygo.SexpNull 1223 var j []byte 1224 j, err = json.Marshal(response.Links) 1225 if err == nil { 1226 resultValue = &zygo.SexpStr{S: string(j)} 1227 } 1228 } 1229 return makeResult(env, resultValue, err) 1230 }) 1231 1232 l := ZygoLibrary 1233 if h != nil { 1234 z.env.AddGlobal("App_Name", &zygo.SexpStr{S: h.Name()}) 1235 z.env.AddGlobal("App_DNA_Hash", &zygo.SexpStr{S: h.dnaHash.String()}) 1236 z.env.AddGlobal("App_Key_Hash", &appKeyHash) 1237 z.env.AddGlobal("App_Agent_String", &appAgentStr) 1238 z.env.AddGlobal("App_Agent_Hash", &appAgentHash) 1239 z.env.AddGlobal("App_Agent_TopHash", &appAgentTopHash) 1240 } 1241 z.library = l 1242 1243 _, err = z.Run(l + zome.Code) 1244 if err != nil { 1245 return 1246 } 1247 n = &z 1248 return 1249 } 1250 1251 // Run executes zygo code 1252 func (z *ZygoRibosome) Run(code string) (result interface{}, err error) { 1253 c := fmt.Sprintf("(begin %s %s)", z.library, code) 1254 err = z.env.LoadString(c) 1255 if err != nil { 1256 err = errors.New("Zygomys load error: " + err.Error()) 1257 return 1258 } 1259 var sexp zygo.Sexp 1260 sexp, err = z.env.Run() 1261 if err != nil { 1262 err = errors.New("Zygomys exec error: " + err.Error()) 1263 return 1264 } 1265 z.lastResult = sexp 1266 result = sexp 1267 return 1268 } 1269 1270 // extra functions we want to have available for app developers in zygo 1271 1272 func isPrime(t int64) bool { 1273 1274 // math.Mod requires floats. 1275 x := float64(t) 1276 1277 // 1 or less aren't primes. 1278 if x <= 1 { 1279 return false 1280 } 1281 1282 // Solve half of the integer set directly 1283 if math.Mod(x, 2) == 0 { 1284 return x == 2 1285 } 1286 1287 // Main loop. i needs to be float because of math.Mod. 1288 for i := 3.0; i <= math.Floor(math.Sqrt(x)); i += 2.0 { 1289 if math.Mod(x, i) == 0 { 1290 return false 1291 } 1292 } 1293 1294 // It's a prime! 1295 return true 1296 } 1297 1298 func addExtras(z *ZygoRibosome) { 1299 z.env.AddFunction("isprime", 1300 func(env *zygo.Zlisp, name string, args []zygo.Sexp) (zygo.Sexp, error) { 1301 1302 switch t := args[0].(type) { 1303 case *zygo.SexpInt: 1304 return &zygo.SexpBool{Val: isPrime(t.Val)}, nil 1305 default: 1306 return zygo.SexpNull, 1307 errors.New("argument to isprime should be int") 1308 } 1309 }) 1310 z.env.AddFunction("atoi", 1311 func(env *zygo.Zlisp, name string, args []zygo.Sexp) (zygo.Sexp, error) { 1312 1313 var i int64 1314 var e error 1315 switch t := args[0].(type) { 1316 case *zygo.SexpStr: 1317 i, e = strconv.ParseInt(t.S, 10, 64) 1318 if e != nil { 1319 return zygo.SexpNull, e 1320 } 1321 default: 1322 return zygo.SexpNull, 1323 errors.New("argument to atoi should be string") 1324 } 1325 1326 return &zygo.SexpInt{Val: i}, nil 1327 }) 1328 } 1329 1330 func (z *ZygoRibosome) RunAsyncSendResponse(response AppMsg, callback string, callbackID string) (result interface{}, err error) { 1331 code := fmt.Sprintf(`(%s (unjson (raw "%s")) "%s")`, callback, sanitizeZyString(response.Body), sanitizeZyString(callbackID)) 1332 z.h.Debugf("Calling %s\n", code) 1333 result, err = z.Run(code) 1334 return 1335 }