9 chainjson "github.com/bytom/encoding/json"
10 "github.com/bytom/errors"
11 "github.com/bytom/protocol/vm"
12 "github.com/bytom/protocol/vm/vmutil"
15 // Compile parses a sequence of Equity contracts from the supplied reader
16 // and produces Contract objects containing the compiled bytecode and
17 // other analysis. If argMap is non-nil, it maps contract names to
18 // lists of arguments with which to instantiate them as programs, with
19 // the results placed in the contract's Program field. A contract
20 // named in argMap but not found in the input is silently ignored.
21 func Compile(r io.Reader) ([]*Contract, error) {
22 inp, err := ioutil.ReadAll(r)
24 return nil, errors.Wrap(err, "reading input")
26 contracts, err := parse(inp)
28 return nil, errors.Wrap(err, "parse error")
31 globalEnv := newEnviron(nil)
32 for _, k := range keywords {
33 globalEnv.add(k, nilType, roleKeyword)
35 for _, b := range builtins {
36 globalEnv.add(b.name, nilType, roleBuiltin)
39 // All contracts must be checked for recursiveness before any are
41 for _, contract := range contracts {
42 contract.Recursive = checkRecursive(contract)
45 for _, contract := range contracts {
46 err = globalEnv.addContract(contract)
52 for _, contract := range contracts {
53 err = compileContract(contract, globalEnv)
55 return nil, errors.Wrap(err, "compiling contract")
62 func Instantiate(body []byte, params []*Param, recursive bool, args []ContractArg) ([]byte, error) {
63 if len(args) != len(params) {
64 return nil, fmt.Errorf("got %d argument(s), want %d", len(args), len(params))
67 // typecheck args against param types
68 for i, param := range params {
71 case amountType, intType:
73 return nil, fmt.Errorf("type mismatch in arg %d (want integer)", i)
75 case assetType, hashType, progType, pubkeyType, sigType, signType, strType:
77 return nil, fmt.Errorf("type mismatch in arg %d (want string)", i)
81 return nil, fmt.Errorf("type mismatch in arg %d (want boolean)", i)
86 b := vmutil.NewBuilder()
88 for i := len(args) - 1; i >= 0; i-- {
105 // <argN> <argN-1> ... <arg1> <body> DEPTH OVER 0 CHECKPREDICATE
107 b.AddOp(vm.OP_DEPTH).AddOp(vm.OP_OVER)
109 // <argN> <argN-1> ... <arg1> DEPTH <body> 0 CHECKPREDICATE
114 b.AddOp(vm.OP_CHECKPREDICATE)
118 func compileContract(contract *Contract, globalEnv *environ) error {
121 if len(contract.Clauses) == 0 {
122 return fmt.Errorf("empty contract")
124 env := newEnviron(globalEnv)
125 for _, p := range contract.Params {
126 err = env.add(p.Name, p.Type, roleContractParam)
132 // value is spilt with valueAmount and valueAsset
133 if err = env.add(contract.Value.Amount, amountType, roleContractValue); err != nil {
136 if err = env.add(contract.Value.Asset, assetType, roleContractValue); err != nil {
140 for _, c := range contract.Clauses {
141 err = env.add(c.Name, nilType, roleClause)
147 err = prohibitSigParams(contract)
151 err = requireAllParamsUsedInClauses(contract.Params, contract.Clauses)
158 if len(contract.Clauses) > 1 {
159 stk = stk.add("<clause selector>")
162 for i := len(contract.Params) - 1; i >= 0; i-- {
163 p := contract.Params[i]
164 stk = stk.add(p.Name)
167 if contract.Recursive {
168 stk = stk.add(contract.Name)
172 sequence := 0 // sequence is used to count the number of ifStatements
174 if len(contract.Clauses) == 1 {
175 err = compileClause(b, stk, contract, env, contract.Clauses[0], &sequence)
180 if len(contract.Params) > 0 {
181 // A clause selector is at the bottom of the stack. Roll it to the
183 n := len(contract.Params)
184 if contract.Recursive {
187 stk = b.addRoll(stk, n) // stack: [<clause params> <contract params> [<maybe contract body>] <clause selector>]
193 for i := len(contract.Clauses) - 1; i >= 2; i-- {
194 stk = b.addDup(stk) // stack: [... <clause selector> <clause selector>]
195 stk = b.addInt64(stk, int64(i)) // stack: [... <clause selector> <clause selector> <i>]
196 stk = b.addNumEqual(stk, fmt.Sprintf("(<clause selector> == %d)", i)) // stack: [... <clause selector> <i == clause selector>]
197 stk = b.addJumpIf(stk, contract.Clauses[i].Name) // stack: [... <clause selector>]
198 stk2 = stk // stack starts here for clauses 2 through N-1
202 stk = b.addJumpIf(stk, contract.Clauses[1].Name) // consumes the clause selector
204 // no jump needed for clause 0
206 for i, clause := range contract.Clauses {
208 // Clauses 0 and 1 have no clause selector on top of the
209 // stack. Clauses 2 and later do.
213 b.addJumpTarget(stk, clause.Name)
219 err = compileClause(b, stk, contract, env, clause, &sequence)
221 return errors.Wrapf(err, "compiling clause \"%s\"", clause.Name)
223 b.forgetPendingVerify()
224 if i < len(contract.Clauses)-1 {
225 b.addJump(stk, "_end")
228 b.addJumpTarget(stk, "_end")
231 opcodes := optimize(b.opcodes())
232 prog, err := vm.Assemble(opcodes)
238 contract.Opcodes = opcodes
240 contract.Steps = b.steps()
245 func compileClause(b *builder, contractStk stack, contract *Contract, env *environ, clause *Clause, sequence *int) error {
248 // copy env to leave outerEnv unchanged
249 env = newEnviron(env)
250 for _, p := range clause.Params {
251 err = env.add(p.Name, p.Type, roleClauseParam)
257 if err = assignIndexes(clause); err != nil {
262 for _, p := range clause.Params {
263 // NOTE: the order of clause params is not reversed, unlike
264 // contract params (and also unlike the arguments to Equity
266 stk = stk.add(p.Name)
268 stk = stk.addFromStack(contractStk)
270 // a count of the number of times each variable is referenced
271 counts := make(map[string]int)
272 for _, stat := range clause.statements {
273 counts = countsVarRef(stat, counts)
276 for _, stat := range clause.statements {
277 if stk, err = compileStatement(b, stk, contract, env, clause, counts, stat, sequence); err != nil {
282 var condValues []CondValueInfo
283 tempVariables := map[string]ExpressionInfo{}
284 for _, stmt := range clause.statements {
285 valueInfo := calClauseValues(contract, env, stmt, &condValues, tempVariables)
286 if valueInfo != nil {
287 clause.Values = append(clause.Values, *valueInfo)
289 clause.CondValues = condValues
293 err = typeCheckClause(contract, clause, env)
297 err = requireAllParamsUsedInClause(clause.Params, clause)
305 func compileStatement(b *builder, stk stack, contract *Contract, env *environ, clause *Clause, counts map[string]int, stat statement, sequence *int) (stack, error) {
307 switch stmt := stat.(type) {
309 // sequence add 1 when the statement is ifStatement
311 strSequence := fmt.Sprintf("%d", *sequence)
313 // compile the contract valueAmount and valueAsset for expression
314 stk, counts = compileContractValue(b, stmt.condition, contract.Value, stk, counts)
316 // compile condition expression
317 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.condition)
319 return stk, errors.Wrapf(err, "in check condition of ifStatement in clause \"%s\"", clause.Name)
322 // jump to falseBody when condition is false, while the JUMPIF instruction will be run success when
323 // the value of dataStack is true, therefore add this negation
324 conditionExpr := stk.str
325 stk = b.addNot(stk, fmt.Sprintf("!%s", conditionExpr))
327 // add nop instruction to differ with clause selector for JUMPIF instruction
332 if len(stmt.body.falseBody) != 0 {
333 label = "else_" + strSequence
335 label = "endif_" + strSequence
337 stk = b.addJumpIf(stk, label)
338 b.addJumpTarget(stk, "if_"+strSequence)
340 // temporary store stack and counts for falseBody
342 elseCounts := make(map[string]int)
343 for k, v := range counts {
347 // compile trueBody statements
348 if len(stmt.body.trueBody) != 0 {
349 for _, st := range stmt.body.trueBody {
350 st.countVarRefs(counts)
353 for _, st := range stmt.body.trueBody {
354 if stk, err = compileStatement(b, stk, contract, env, clause, counts, st, sequence); err != nil {
360 // compile falseBody statements
361 if len(stmt.body.falseBody) != 0 {
362 counts := make(map[string]int)
363 for k, v := range elseCounts {
367 for _, st := range stmt.body.falseBody {
368 st.countVarRefs(counts)
372 b.addJump(stk, "endif_"+strSequence)
373 b.addJumpTarget(stk, "else_"+strSequence)
375 for _, st := range stmt.body.falseBody {
376 if stk, err = compileStatement(b, stk, contract, env, clause, counts, st, sequence); err != nil {
381 b.addJumpTarget(stk, "endif_"+strSequence)
383 case *defineStatement:
384 // add environ for define variable
385 if err = env.add(stmt.variable.Name, stmt.variable.Type, roleClauseVariable); err != nil {
389 // check whether the variable is used or not
390 if counts[stmt.variable.Name] == 0 {
391 return stk, fmt.Errorf("the defined variable \"%s\" is unused in clause \"%s\"", stmt.variable.Name, clause.Name)
394 if stmt.expr != nil {
395 // compile the contract valueAmount and valueAsset for expression
396 stk, counts = compileContractValue(b, stmt.expr, contract.Value, stk, counts)
399 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
401 return stk, errors.Wrapf(err, "in define statement in clause \"%s\"", clause.Name)
405 stk.str = stmt.variable.Name
408 case *assignStatement:
409 // find variable from environ with roleClauseVariable
410 if entry := env.lookup(string(stmt.variable.Name)); entry != nil {
411 if entry.r != roleClauseVariable {
412 return stk, fmt.Errorf("the type of variable is not roleClauseVariable in assign statement in clause \"%s\"", clause.Name)
414 stmt.variable.Type = entry.t
416 return stk, fmt.Errorf("the variable \"%s\" is not defined before the assign statement in clause \"%s\"", stmt.variable.Name, clause.Name)
419 // temporary store the counts of defined variable
420 varCount := counts[stmt.variable.Name]
422 // calculate the counts of variable for assign statement
423 tmpCounts := make(map[string]int)
424 stmt.countVarRefs(tmpCounts)
426 // modify the map counts of defined variable to 1 and minus the number of defined variable
427 // when the assign expression contains the defined variable
428 if tmpCounts[stmt.variable.Name] > 0 {
429 counts[stmt.variable.Name] = 1
430 varCount -= tmpCounts[stmt.variable.Name]
432 depth := stk.find(stmt.variable.Name)
439 stk = b.addRoll(stk, depth)
444 // compile the contract valueAmount and valueAsset for expression
445 stk, counts = compileContractValue(b, stmt.expr, contract.Value, stk, counts)
448 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
450 return stk, errors.Wrapf(err, "in define statement in clause \"%s\"", clause.Name)
453 // restore the defined variable counts
454 if tmpCounts[stmt.variable.Name] > 0 {
455 counts[stmt.variable.Name] = varCount
459 stk.str = stmt.variable.Name
461 case *verifyStatement:
462 // compile the contract valueAmount and valueAsset for expression
463 stk, counts = compileContractValue(b, stmt.expr, contract.Value, stk, counts)
465 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
467 return stk, errors.Wrapf(err, "in verify statement in clause \"%s\"", clause.Name)
469 stk = b.addVerify(stk)
471 // special-case reporting of certain function calls
472 if c, ok := stmt.expr.(*callExpr); ok && len(c.args) == 1 {
473 if b := referencedBuiltin(c.fn); b != nil {
476 clause.BlockHeight = append(clause.BlockHeight, c.args[0].String())
478 clause.BlockHeight = append(clause.BlockHeight, c.args[0].String())
485 stk = b.addInt64(stk, stmt.index)
487 // TODO: permit more complex expressions for locked,
488 // like "lock x+y with foo" (?)
490 if stmt.lockedAmount.String() == contract.Value.Amount && stmt.lockedAsset.String() == contract.Value.Asset {
491 stk = b.addAmount(stk, contract.Value.Amount)
492 stk = b.addAsset(stk, contract.Value.Asset)
494 // calculate the counts of variable for lockStatement
495 lockCounts := make(map[string]int)
496 stmt.countVarRefs(lockCounts)
500 case stmt.lockedAmount.String() == contract.Value.Amount:
501 stk = b.addAmount(stk, contract.Value.Amount)
502 case stmt.lockedAmount.String() != contract.Value.Amount && lockCounts[contract.Value.Amount] > 0:
503 counts[contract.Value.Amount] = lockCounts[contract.Value.Amount]
504 stk = b.addAmount(stk, contract.Value.Amount)
505 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAmount)
507 return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
510 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAmount)
512 return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
518 case stmt.lockedAsset.String() == contract.Value.Asset:
519 stk = b.addAsset(stk, contract.Value.Asset)
520 case stmt.lockedAsset.String() != contract.Value.Asset && lockCounts[contract.Value.Asset] > 0:
521 counts[contract.Value.Asset] = lockCounts[contract.Value.Asset]
522 stk = b.addAsset(stk, contract.Value.Asset)
523 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAsset)
525 return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
528 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAsset)
530 return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
536 stk = b.addInt64(stk, 1)
539 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.program)
541 return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
544 stk = b.addCheckOutput(stk, fmt.Sprintf("checkOutput(%s, %s, %s)",
545 stmt.lockedAmount.String(), stmt.lockedAsset.String(), stmt.program))
546 stk = b.addVerify(stk)
548 case *unlockStatement:
549 if len(clause.statements) == 1 {
550 // This is the only statement in the clause, make sure TRUE is
552 stk = b.addBoolean(stk, true)
559 func compileExpr(b *builder, stk stack, contract *Contract, clause *Clause, env *environ, counts map[string]int, expr expression) (stack, error) {
562 switch e := expr.(type) {
564 // Do typechecking after compiling subexpressions (because other
565 // compilation errors are more interesting than type mismatch
568 stk, err = compileExpr(b, stk, contract, clause, env, counts, e.left)
570 return stk, errors.Wrapf(err, "in left operand of \"%s\" expression", e.op.op)
572 stk, err = compileExpr(b, stk, contract, clause, env, counts, e.right)
574 return stk, errors.Wrapf(err, "in right operand of \"%s\" expression", e.op.op)
577 lType := e.left.typ(env)
578 if e.op.left != "" && ((e.op.left == intType && !(lType == amountType || lType == intType)) ||
579 (e.op.left == boolType && !(lType == boolType))) {
580 return stk, fmt.Errorf("in \"%s\", left operand has type \"%s\", must be \"%s\"", e, lType, e.op.left)
583 rType := e.right.typ(env)
584 if e.op.right != "" && ((e.op.right == intType && !(rType == amountType || rType == intType)) ||
585 (e.op.right == boolType && !(rType == boolType))) {
586 return stk, fmt.Errorf("in \"%s\", right operand has type \"%s\", must be \"%s\"", e, rType, e.op.right)
592 // Maybe one is Hash and the other is (more-specific-Hash subtype).
593 // TODO(bobg): generalize this mechanism
594 if lType == hashType && isHashSubtype(rType) {
595 propagateType(contract, clause, env, rType, e.left)
596 } else if rType == hashType && isHashSubtype(lType) {
597 propagateType(contract, clause, env, lType, e.right)
599 return stk, fmt.Errorf("type mismatch in \"%s\": left operand has type \"%s\", right operand has type \"%s\"", e, lType, rType)
602 if lType == "Boolean" {
603 return stk, fmt.Errorf("in \"%s\": using \"%s\" on Boolean values not allowed", e, e.op.op)
607 stk = b.addOps(stk.dropN(2), e.op.opcodes, e.String())
610 // Do typechecking after compiling subexpression (because other
611 // compilation errors are more interesting than type mismatch
615 stk, err = compileExpr(b, stk, contract, clause, env, counts, e.expr)
617 return stk, errors.Wrapf(err, "in \"%s\" expression", e.op.op)
620 if e.op.operand != "" && e.expr.typ(env) != e.op.operand {
621 return stk, fmt.Errorf("in \"%s\", operand has type \"%s\", must be \"%s\"", e, e.expr.typ(env), e.op.operand)
623 b.addOps(stk.drop(), e.op.opcodes, e.String())
626 bi := referencedBuiltin(e.fn)
628 if v, ok := e.fn.(varRef); ok {
629 if entry := env.lookup(string(v)); entry != nil && entry.t == contractType {
630 clause.Contracts = append(clause.Contracts, entry.c.Name)
632 partialName := fmt.Sprintf("%s(...)", v)
633 stk = b.addData(stk, nil)
635 if len(e.args) != len(entry.c.Params) {
636 return stk, fmt.Errorf("contract \"%s\" expects %d argument(s), got %d", entry.c.Name, len(entry.c.Params), len(e.args))
639 for i := len(e.args) - 1; i >= 0; i-- {
641 if entry.c.Params[i].Type != "" && arg.typ(env) != entry.c.Params[i].Type &&
642 !(arg.typ(env) == intType && entry.c.Params[i].Type == amountType) {
643 return stk, fmt.Errorf("argument %d to contract \"%s\" has type \"%s\", must be \"%s\"", i, entry.c.Name, arg.typ(env), entry.c.Params[i].Type)
645 stk, err = compileExpr(b, stk, contract, clause, env, counts, arg)
649 stk = b.addCatPushdata(stk, partialName)
653 case entry.c == contract:
654 // Recursive call - cannot use entry.c.Body
655 // <argN> <argN-1> ... <arg1> <body> DEPTH OVER 0 CHECKPREDICATE
656 stk, err = compileRef(b, stk, counts, varRef(contract.Name))
658 return stk, errors.Wrap(err, "compiling contract call")
660 stk = b.addCatPushdata(stk, partialName)
661 stk = b.addData(stk, []byte{byte(vm.OP_DEPTH), byte(vm.OP_OVER)})
662 stk = b.addCat(stk, partialName)
664 case entry.c.Recursive:
665 // Non-recursive call to a (different) recursive contract
666 // <argN> <argN-1> ... <arg1> <body> DEPTH OVER 0 CHECKPREDICATE
667 if len(entry.c.Body) == 0 {
668 // TODO(bobg): sort input contracts topologically to permit forward calling
669 return stk, fmt.Errorf("contract \"%s\" not defined", entry.c.Name)
671 stk = b.addData(stk, entry.c.Body)
672 stk = b.addCatPushdata(stk, partialName)
673 stk = b.addData(stk, []byte{byte(vm.OP_DEPTH), byte(vm.OP_OVER)})
674 stk = b.addCat(stk, partialName)
677 // Non-recursive call to non-recursive contract
678 // <argN> <argN-1> ... <arg1> DEPTH <body> 0 CHECKPREDICATE
679 stk = b.addData(stk, []byte{byte(vm.OP_DEPTH)})
680 stk = b.addCat(stk, partialName)
681 if len(entry.c.Body) == 0 {
682 // TODO(bobg): sort input contracts topologically to permit forward calling
683 return stk, fmt.Errorf("contract \"%s\" not defined", entry.c.Name)
685 stk = b.addData(stk, entry.c.Body)
686 stk = b.addCatPushdata(stk, partialName)
688 stk = b.addData(stk, vm.Int64Bytes(0))
689 stk = b.addCatPushdata(stk, partialName)
690 stk = b.addData(stk, []byte{byte(vm.OP_CHECKPREDICATE)})
691 stk = b.addCat(stk, e.String())
696 return stk, fmt.Errorf("unknown function \"%s\"", e.fn)
699 if len(e.args) != len(bi.args) {
700 return stk, fmt.Errorf("wrong number of args for \"%s\": have %d, want %d", bi.name, len(e.args), len(bi.args))
703 // WARNING WARNING WOOP WOOP
705 // WARNING WARNING WOOP WOOP
706 if bi.name == "checkTxMultiSig" {
707 if _, ok := e.args[0].(listExpr); !ok {
708 return stk, fmt.Errorf("checkTxMultiSig expects list literals, got %T for argument 0", e.args[0])
710 if _, ok := e.args[1].(listExpr); !ok {
711 return stk, fmt.Errorf("checkTxMultiSig expects list literals, got %T for argument 1", e.args[1])
716 stk, k1, err = compileArg(b, stk, contract, clause, env, counts, e.args[1])
721 // stack: [... sigM ... sig1 M]
724 stk, altEntry = b.addToAltStack(stk) // stack: [... sigM ... sig1]
725 stk = b.addTxSigHash(stk) // stack: [... sigM ... sig1 txsighash]
727 stk, k2, err = compileArg(b, stk, contract, clause, env, counts, e.args[0])
732 // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 N]
734 stk = b.addFromAltStack(stk, altEntry) // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 N M]
735 stk = b.addSwap(stk) // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 M N]
736 stk = b.addCheckMultisig(stk, k1+k2, e.String())
743 for i := len(e.args) - 1; i >= 0; i-- {
747 stk, k2, err = compileArg(b, stk, contract, clause, env, counts, a)
749 return stk, errors.Wrapf(err, "compiling argument %d in call expression", i)
754 // Do typechecking after compiling subexpressions (because other
755 // compilation errors are more interesting than type mismatch
757 for i, actual := range e.args {
758 if bi.args[i] != "" && actual.typ(env) != bi.args[i] {
759 return stk, fmt.Errorf("argument %d to \"%s\" has type \"%s\", must be \"%s\"", i, bi.name, actual.typ(env), bi.args[i])
763 stk = b.addOps(stk.dropN(k), bi.opcodes, e.String())
765 // special-case reporting
767 case "sha3", "sha256":
768 clause.HashCalls = append(clause.HashCalls, HashCall{bi.name, e.args[0].String(), string(e.args[0].typ(env))})
772 return compileRef(b, stk, counts, e)
775 stk = b.addInt64(stk, int64(e))
778 stk = b.addData(stk, []byte(e))
781 stk = b.addBoolean(stk, bool(e))
784 // Lists are excluded here because they disobey the invariant of
785 // this function: namely, that it increases the stack size by
786 // exactly one. (A list pushes its items and its length on the
787 // stack.) But they're OK as function-call arguments because the
788 // function (presumably) consumes all the stack items added.
789 return stk, fmt.Errorf("encountered list outside of function-call context")
794 func compileArg(b *builder, stk stack, contract *Contract, clause *Clause, env *environ, counts map[string]int, expr expression) (stack, int, error) {
796 if list, ok := expr.(listExpr); ok {
797 for i := 0; i < len(list); i++ {
798 elt := list[len(list)-i-1]
800 stk, err = compileExpr(b, stk, contract, clause, env, counts, elt)
806 stk = b.addInt64(stk, int64(len(list)))
811 stk, err = compileExpr(b, stk, contract, clause, env, counts, expr)
815 func compileContractValue(b *builder, expr expression, contractValue ValueInfo, stk stack, counts map[string]int) (stack, map[string]int) {
816 valueCounts := make(map[string]int)
817 expr.countVarRefs(valueCounts)
818 if valueCounts[contractValue.Amount] > 0 {
819 counts[contractValue.Amount] = valueCounts[contractValue.Amount]
820 stk = b.addAmount(stk, contractValue.Amount)
823 if valueCounts[contractValue.Asset] > 0 {
824 counts[contractValue.Asset] = valueCounts[contractValue.Asset]
825 stk = b.addAsset(stk, contractValue.Asset)
831 func compileRef(b *builder, stk stack, counts map[string]int, ref varRef) (stack, error) {
832 depth := stk.find(string(ref))
834 return stk, fmt.Errorf("undefined reference: \"%s\"", ref)
838 if count, ok := counts[string(ref)]; ok && count > 0 {
840 counts[string(ref)] = count
857 stk = b.addRoll(stk, depth)
859 stk = b.addPick(stk, depth)
865 func (a *ContractArg) UnmarshalJSON(b []byte) error {
866 var m map[string]json.RawMessage
867 err := json.Unmarshal(b, &m)
871 if r, ok := m["boolean"]; ok {
873 err = json.Unmarshal(r, &bval)
880 if r, ok := m["integer"]; ok {
882 err = json.Unmarshal(r, &ival)
891 return fmt.Errorf("contract arg must define one of boolean, integer, string")
893 var sval chainjson.HexBytes
894 err = json.Unmarshal(r, &sval)