9 chainjson "github.com/vapor/encoding/json"
10 "github.com/vapor/errors"
11 "github.com/vapor/protocol/vm"
12 "github.com/vapor/protocol/vm/vmutil"
15 // ValueInfo describes how a blockchain value is used in a contract
17 type ValueInfo struct {
18 // Name is the clause's name for this value.
19 Name string `json:"name"`
21 // Program is the program expression used to the lock the value, if
22 // the value is locked with "lock." If it's unlocked with "unlock"
23 // instead, this is empty.
24 Program string `json:"program,omitempty"`
26 // Asset is the expression describing the asset type the value must
27 // have, as it appears in a clause's "requires" section. If this is
28 // the contract value instead, this is empty.
29 Asset string `json:"asset,omitempty"`
31 // Amount is the expression describing the amount the value must
32 // have, as it appears in a clause's "requires" section. If this is
33 // the contract value instead, this is empty.
34 Amount string `json:"amount,omitempty"`
37 // ContractArg is an argument with which to instantiate a contract as
38 // a program. Exactly one of B, I, and S should be supplied.
39 type ContractArg struct {
40 B *bool `json:"boolean,omitempty"`
41 I *int64 `json:"integer,omitempty"`
42 S *chainjson.HexBytes `json:"string,omitempty"`
45 // Compile parses a sequence of Equity contracts from the supplied reader
46 // and produces Contract objects containing the compiled bytecode and
47 // other analysis. If argMap is non-nil, it maps contract names to
48 // lists of arguments with which to instantiate them as programs, with
49 // the results placed in the contract's Program field. A contract
50 // named in argMap but not found in the input is silently ignored.
51 func Compile(r io.Reader) ([]*Contract, error) {
52 inp, err := ioutil.ReadAll(r)
54 return nil, errors.Wrap(err, "reading input")
56 contracts, err := parse(inp)
58 return nil, errors.Wrap(err, "parse error")
61 globalEnv := newEnviron(nil)
62 for _, k := range keywords {
63 globalEnv.add(k, nilType, roleKeyword)
65 for _, b := range builtins {
66 globalEnv.add(b.name, nilType, roleBuiltin)
69 // All contracts must be checked for recursiveness before any are
71 for _, contract := range contracts {
72 contract.Recursive = checkRecursive(contract)
75 for _, contract := range contracts {
76 err = globalEnv.addContract(contract)
82 for _, contract := range contracts {
83 err = compileContract(contract, globalEnv)
85 return nil, errors.Wrap(err, "compiling contract")
87 for _, clause := range contract.Clauses {
88 for _, stmt := range clause.statements {
89 switch s := stmt.(type) {
91 valueInfo := ValueInfo{
92 Amount: s.lockedAmount.String(),
93 Asset: s.lockedAsset.String(),
94 Program: s.program.String(),
97 clause.Values = append(clause.Values, valueInfo)
98 case *unlockStatement:
99 valueInfo := ValueInfo{
100 Amount: contract.Value.Amount,
101 Asset: contract.Value.Asset,
103 clause.Values = append(clause.Values, valueInfo)
109 return contracts, nil
112 func Instantiate(body []byte, params []*Param, recursive bool, args []ContractArg) ([]byte, error) {
113 if len(args) != len(params) {
114 return nil, fmt.Errorf("got %d argument(s), want %d", len(args), len(params))
117 // typecheck args against param types
118 for i, param := range params {
121 case amountType, intType:
123 return nil, fmt.Errorf("type mismatch in arg %d (want integer)", i)
125 case assetType, hashType, progType, pubkeyType, sigType, strType:
127 return nil, fmt.Errorf("type mismatch in arg %d (want string)", i)
131 return nil, fmt.Errorf("type mismatch in arg %d (want boolean)", i)
136 b := vmutil.NewBuilder()
138 for i := len(args) - 1; i >= 0; i-- {
155 // <argN> <argN-1> ... <arg1> <body> DEPTH OVER 0 CHECKPREDICATE
157 b.AddOp(vm.OP_DEPTH).AddOp(vm.OP_OVER)
159 // <argN> <argN-1> ... <arg1> DEPTH <body> 0 CHECKPREDICATE
164 b.AddOp(vm.OP_CHECKPREDICATE)
168 func compileContract(contract *Contract, globalEnv *environ) error {
171 if len(contract.Clauses) == 0 {
172 return fmt.Errorf("empty contract")
174 env := newEnviron(globalEnv)
175 for _, p := range contract.Params {
176 err = env.add(p.Name, p.Type, roleContractParam)
182 // value is spilt with valueAmount and valueAsset
183 if err = env.add(contract.Value.Amount, amountType, roleContractValue); err != nil {
186 if err = env.add(contract.Value.Asset, assetType, roleContractValue); err != nil {
190 for _, c := range contract.Clauses {
191 err = env.add(c.Name, nilType, roleClause)
197 err = prohibitSigParams(contract)
201 err = requireAllParamsUsedInClauses(contract.Params, contract.Clauses)
208 if len(contract.Clauses) > 1 {
209 stk = stk.add("<clause selector>")
212 for i := len(contract.Params) - 1; i >= 0; i-- {
213 p := contract.Params[i]
214 stk = stk.add(p.Name)
217 if contract.Recursive {
218 stk = stk.add(contract.Name)
222 sequence := 0 // sequence is used to count the number of ifStatements
224 if len(contract.Clauses) == 1 {
225 err = compileClause(b, stk, contract, env, contract.Clauses[0], &sequence)
230 if len(contract.Params) > 0 {
231 // A clause selector is at the bottom of the stack. Roll it to the
233 n := len(contract.Params)
234 if contract.Recursive {
237 stk = b.addRoll(stk, n) // stack: [<clause params> <contract params> [<maybe contract body>] <clause selector>]
243 for i := len(contract.Clauses) - 1; i >= 2; i-- {
244 stk = b.addDup(stk) // stack: [... <clause selector> <clause selector>]
245 stk = b.addInt64(stk, int64(i)) // stack: [... <clause selector> <clause selector> <i>]
246 stk = b.addNumEqual(stk, fmt.Sprintf("(<clause selector> == %d)", i)) // stack: [... <clause selector> <i == clause selector>]
247 stk = b.addJumpIf(stk, contract.Clauses[i].Name) // stack: [... <clause selector>]
248 stk2 = stk // stack starts here for clauses 2 through N-1
252 stk = b.addJumpIf(stk, contract.Clauses[1].Name) // consumes the clause selector
254 // no jump needed for clause 0
256 for i, clause := range contract.Clauses {
258 // Clauses 0 and 1 have no clause selector on top of the
259 // stack. Clauses 2 and later do.
263 b.addJumpTarget(stk, clause.Name)
269 err = compileClause(b, stk, contract, env, clause, &sequence)
271 return errors.Wrapf(err, "compiling clause \"%s\"", clause.Name)
273 b.forgetPendingVerify()
274 if i < len(contract.Clauses)-1 {
275 b.addJump(stk, "_end")
278 b.addJumpTarget(stk, "_end")
281 opcodes := optimize(b.opcodes())
282 prog, err := vm.Assemble(opcodes)
288 contract.Opcodes = opcodes
290 contract.Steps = b.steps()
295 func compileClause(b *builder, contractStk stack, contract *Contract, env *environ, clause *Clause, sequence *int) error {
298 // copy env to leave outerEnv unchanged
299 env = newEnviron(env)
300 for _, p := range clause.Params {
301 err = env.add(p.Name, p.Type, roleClauseParam)
307 if err = assignIndexes(clause); err != nil {
312 for _, p := range clause.Params {
313 // NOTE: the order of clause params is not reversed, unlike
314 // contract params (and also unlike the arguments to Equity
316 stk = stk.add(p.Name)
318 stk = stk.addFromStack(contractStk)
320 // a count of the number of times each variable is referenced
321 counts := make(map[string]int)
322 for _, s := range clause.statements {
323 if stmt, ok := s.(*defineStatement); ok && stmt.expr == nil {
327 s.countVarRefs(counts)
328 if stmt, ok := s.(*ifStatement); ok {
329 for _, trueStmt := range stmt.body.trueBody {
330 trueStmt.countVarRefs(counts)
333 for _, falseStmt := range stmt.body.falseBody {
334 falseStmt.countVarRefs(counts)
339 for _, stat := range clause.statements {
340 if stk, err = compileStatement(b, stk, contract, env, clause, counts, stat, sequence); err != nil {
345 err = typeCheckClause(contract, clause, env)
349 err = requireAllParamsUsedInClause(clause.Params, clause)
357 func compileStatement(b *builder, stk stack, contract *Contract, env *environ, clause *Clause, counts map[string]int, stat statement, sequence *int) (stack, error) {
359 switch stmt := stat.(type) {
361 // sequence add 1 when the statement is ifStatement
363 strSequence := fmt.Sprintf("%d", *sequence)
365 // compile condition expression
366 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.condition)
368 return stk, errors.Wrapf(err, "in check condition of ifStatement in clause \"%s\"", clause.Name)
371 // jump to falseBody when condition is false, while the JUMPIF instruction will be run success when
372 // the value of dataStack is true, therefore add this negation
373 conditionExpr := stk.str
374 stk = b.addNot(stk, fmt.Sprintf("!%s", conditionExpr))
376 // add nop instruction to differ with clause selector for JUMPIF instruction
381 if len(stmt.body.falseBody) != 0 {
382 label = "else_" + strSequence
384 label = "endif_" + strSequence
386 stk = b.addJumpIf(stk, label)
387 b.addJumpTarget(stk, "if_"+strSequence)
389 // temporary store stack and counts for falseBody
391 elseCounts := make(map[string]int)
392 for k, v := range counts {
396 // compile trueBody statements
397 if len(stmt.body.trueBody) != 0 {
398 for _, st := range stmt.body.trueBody {
399 st.countVarRefs(counts)
402 for _, st := range stmt.body.trueBody {
403 if stk, err = compileStatement(b, stk, contract, env, clause, counts, st, sequence); err != nil {
409 // compile falseBody statements
410 if len(stmt.body.falseBody) != 0 {
411 counts := make(map[string]int)
412 for k, v := range elseCounts {
416 for _, st := range stmt.body.falseBody {
417 st.countVarRefs(counts)
421 b.addJump(stk, "endif_"+strSequence)
422 b.addJumpTarget(stk, "else_"+strSequence)
424 for _, st := range stmt.body.falseBody {
425 if stk, err = compileStatement(b, stk, contract, env, clause, counts, st, sequence); err != nil {
430 b.addJumpTarget(stk, "endif_"+strSequence)
432 case *defineStatement:
433 // add environ for define variable
434 if err = env.add(stmt.variable.Name, stmt.variable.Type, roleClauseVariable); err != nil {
438 // check whether the variable is used or not
439 if counts[stmt.variable.Name] == 0 {
440 return stk, fmt.Errorf("the defined variable \"%s\" is unused in clause \"%s\"", stmt.variable.Name, clause.Name)
443 if stmt.expr != nil {
445 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
447 return stk, errors.Wrapf(err, "in define statement in clause \"%s\"", clause.Name)
451 stk.str = stmt.variable.Name
454 case *assignStatement:
455 // find variable from environ with roleClauseVariable
456 if entry := env.lookup(string(stmt.variable.Name)); entry != nil {
457 if entry.r != roleClauseVariable {
458 return stk, fmt.Errorf("the type of variable is not roleClauseVariable in assign statement in clause \"%s\"", clause.Name)
460 stmt.variable.Type = entry.t
462 return stk, fmt.Errorf("the variable \"%s\" is not defined before the assign statement in clause \"%s\"", stmt.variable.Name, clause.Name)
465 // temporary store the counts of defined variable
466 varCount := counts[stmt.variable.Name]
468 // calculate the counts of variable for assign statement
469 tmpCounts := make(map[string]int)
470 stmt.countVarRefs(tmpCounts)
472 // modify the map counts of defined variable to 1 and minus the number of defined variable
473 // when the assign expression contains the defined variable
474 if tmpCounts[stmt.variable.Name] > 0 {
475 counts[stmt.variable.Name] = 1
476 varCount -= tmpCounts[stmt.variable.Name]
478 depth := stk.find(stmt.variable.Name)
485 stk = b.addRoll(stk, depth)
491 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
493 return stk, errors.Wrapf(err, "in define statement in clause \"%s\"", clause.Name)
496 // restore the defined variable counts
497 if tmpCounts[stmt.variable.Name] > 0 {
498 counts[stmt.variable.Name] = varCount
502 stk.str = stmt.variable.Name
504 case *verifyStatement:
505 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
507 return stk, errors.Wrapf(err, "in verify statement in clause \"%s\"", clause.Name)
509 stk = b.addVerify(stk)
511 // special-case reporting of certain function calls
512 if c, ok := stmt.expr.(*callExpr); ok && len(c.args) == 1 {
513 if b := referencedBuiltin(c.fn); b != nil {
516 clause.BlockHeight = append(clause.BlockHeight, c.args[0].String())
518 clause.BlockHeight = append(clause.BlockHeight, c.args[0].String())
525 stk = b.addInt64(stk, stmt.index)
527 // TODO: permit more complex expressions for locked,
528 // like "lock x+y with foo" (?)
530 if stmt.lockedAmount.String() == contract.Value.Amount && stmt.lockedAsset.String() == contract.Value.Asset {
531 stk = b.addAmount(stk, contract.Value.Amount)
532 stk = b.addAsset(stk, contract.Value.Asset)
534 // calculate the counts of variable for lockStatement
535 lockCounts := make(map[string]int)
536 stmt.countVarRefs(lockCounts)
540 case stmt.lockedAmount.String() == contract.Value.Amount:
541 stk = b.addAmount(stk, contract.Value.Amount)
542 case stmt.lockedAmount.String() != contract.Value.Amount && lockCounts[contract.Value.Amount] > 0:
543 counts[contract.Value.Amount] = lockCounts[contract.Value.Amount]
544 stk = b.addAmount(stk, contract.Value.Amount)
545 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAmount)
547 return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
550 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAmount)
552 return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
558 case stmt.lockedAsset.String() == contract.Value.Asset:
559 stk = b.addAsset(stk, contract.Value.Asset)
560 case stmt.lockedAsset.String() != contract.Value.Asset && lockCounts[contract.Value.Asset] > 0:
561 counts[contract.Value.Asset] = lockCounts[contract.Value.Asset]
562 stk = b.addAsset(stk, contract.Value.Asset)
563 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAsset)
565 return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
568 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAsset)
570 return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
576 stk = b.addInt64(stk, 1)
579 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.program)
581 return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
584 stk = b.addCheckOutput(stk, fmt.Sprintf("checkOutput(%s, %s, %s)",
585 stmt.lockedAmount.String(), stmt.lockedAsset.String(), stmt.program))
586 stk = b.addVerify(stk)
588 case *unlockStatement:
589 if len(clause.statements) == 1 {
590 // This is the only statement in the clause, make sure TRUE is
592 stk = b.addBoolean(stk, true)
599 func compileExpr(b *builder, stk stack, contract *Contract, clause *Clause, env *environ, counts map[string]int, expr expression) (stack, error) {
602 switch e := expr.(type) {
604 // Do typechecking after compiling subexpressions (because other
605 // compilation errors are more interesting than type mismatch
608 stk, err = compileExpr(b, stk, contract, clause, env, counts, e.left)
610 return stk, errors.Wrapf(err, "in left operand of \"%s\" expression", e.op.op)
612 stk, err = compileExpr(b, stk, contract, clause, env, counts, e.right)
614 return stk, errors.Wrapf(err, "in right operand of \"%s\" expression", e.op.op)
617 lType := e.left.typ(env)
618 if e.op.left != "" && ((e.op.left == intType && !(lType == amountType || lType == intType)) ||
619 (e.op.left == boolType && !(lType == boolType))) {
620 return stk, fmt.Errorf("in \"%s\", left operand has type \"%s\", must be \"%s\"", e, lType, e.op.left)
623 rType := e.right.typ(env)
624 if e.op.right != "" && ((e.op.right == intType && !(rType == amountType || rType == intType)) ||
625 (e.op.right == boolType && !(rType == boolType))) {
626 return stk, fmt.Errorf("in \"%s\", right operand has type \"%s\", must be \"%s\"", e, rType, e.op.right)
632 // Maybe one is Hash and the other is (more-specific-Hash subtype).
633 // TODO(bobg): generalize this mechanism
634 if lType == hashType && isHashSubtype(rType) {
635 propagateType(contract, clause, env, rType, e.left)
636 } else if rType == hashType && isHashSubtype(lType) {
637 propagateType(contract, clause, env, lType, e.right)
639 return stk, fmt.Errorf("type mismatch in \"%s\": left operand has type \"%s\", right operand has type \"%s\"", e, lType, rType)
642 if lType == "Boolean" {
643 return stk, fmt.Errorf("in \"%s\": using \"%s\" on Boolean values not allowed", e, e.op.op)
647 stk = b.addOps(stk.dropN(2), e.op.opcodes, e.String())
650 // Do typechecking after compiling subexpression (because other
651 // compilation errors are more interesting than type mismatch
655 stk, err = compileExpr(b, stk, contract, clause, env, counts, e.expr)
657 return stk, errors.Wrapf(err, "in \"%s\" expression", e.op.op)
660 if e.op.operand != "" && e.expr.typ(env) != e.op.operand {
661 return stk, fmt.Errorf("in \"%s\", operand has type \"%s\", must be \"%s\"", e, e.expr.typ(env), e.op.operand)
663 b.addOps(stk.drop(), e.op.opcodes, e.String())
666 bi := referencedBuiltin(e.fn)
668 if v, ok := e.fn.(varRef); ok {
669 if entry := env.lookup(string(v)); entry != nil && entry.t == contractType {
670 clause.Contracts = append(clause.Contracts, entry.c.Name)
672 partialName := fmt.Sprintf("%s(...)", v)
673 stk = b.addData(stk, nil)
675 if len(e.args) != len(entry.c.Params) {
676 return stk, fmt.Errorf("contract \"%s\" expects %d argument(s), got %d", entry.c.Name, len(entry.c.Params), len(e.args))
679 for i := len(e.args) - 1; i >= 0; i-- {
681 if entry.c.Params[i].Type != "" && arg.typ(env) != entry.c.Params[i].Type &&
682 !(arg.typ(env) == intType && entry.c.Params[i].Type == amountType) {
683 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)
685 stk, err = compileExpr(b, stk, contract, clause, env, counts, arg)
689 stk = b.addCatPushdata(stk, partialName)
693 case entry.c == contract:
694 // Recursive call - cannot use entry.c.Body
695 // <argN> <argN-1> ... <arg1> <body> DEPTH OVER 0 CHECKPREDICATE
696 stk, err = compileRef(b, stk, counts, varRef(contract.Name))
698 return stk, errors.Wrap(err, "compiling contract call")
700 stk = b.addCatPushdata(stk, partialName)
701 stk = b.addData(stk, []byte{byte(vm.OP_DEPTH), byte(vm.OP_OVER)})
702 stk = b.addCat(stk, partialName)
704 case entry.c.Recursive:
705 // Non-recursive call to a (different) recursive contract
706 // <argN> <argN-1> ... <arg1> <body> DEPTH OVER 0 CHECKPREDICATE
707 if len(entry.c.Body) == 0 {
708 // TODO(bobg): sort input contracts topologically to permit forward calling
709 return stk, fmt.Errorf("contract \"%s\" not defined", entry.c.Name)
711 stk = b.addData(stk, entry.c.Body)
712 stk = b.addCatPushdata(stk, partialName)
713 stk = b.addData(stk, []byte{byte(vm.OP_DEPTH), byte(vm.OP_OVER)})
714 stk = b.addCat(stk, partialName)
717 // Non-recursive call to non-recursive contract
718 // <argN> <argN-1> ... <arg1> DEPTH <body> 0 CHECKPREDICATE
719 stk = b.addData(stk, []byte{byte(vm.OP_DEPTH)})
720 stk = b.addCat(stk, partialName)
721 if len(entry.c.Body) == 0 {
722 // TODO(bobg): sort input contracts topologically to permit forward calling
723 return stk, fmt.Errorf("contract \"%s\" not defined", entry.c.Name)
725 stk = b.addData(stk, entry.c.Body)
726 stk = b.addCatPushdata(stk, partialName)
728 stk = b.addData(stk, vm.Int64Bytes(0))
729 stk = b.addCatPushdata(stk, partialName)
730 stk = b.addData(stk, []byte{byte(vm.OP_CHECKPREDICATE)})
731 stk = b.addCat(stk, e.String())
736 return stk, fmt.Errorf("unknown function \"%s\"", e.fn)
739 if len(e.args) != len(bi.args) {
740 return stk, fmt.Errorf("wrong number of args for \"%s\": have %d, want %d", bi.name, len(e.args), len(bi.args))
743 // WARNING WARNING WOOP WOOP
745 // WARNING WARNING WOOP WOOP
746 if bi.name == "checkTxMultiSig" {
747 if _, ok := e.args[0].(listExpr); !ok {
748 return stk, fmt.Errorf("checkTxMultiSig expects list literals, got %T for argument 0", e.args[0])
750 if _, ok := e.args[1].(listExpr); !ok {
751 return stk, fmt.Errorf("checkTxMultiSig expects list literals, got %T for argument 1", e.args[1])
756 stk, k1, err = compileArg(b, stk, contract, clause, env, counts, e.args[1])
761 // stack: [... sigM ... sig1 M]
764 stk, altEntry = b.addToAltStack(stk) // stack: [... sigM ... sig1]
765 stk = b.addTxSigHash(stk) // stack: [... sigM ... sig1 txsighash]
767 stk, k2, err = compileArg(b, stk, contract, clause, env, counts, e.args[0])
772 // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 N]
774 stk = b.addFromAltStack(stk, altEntry) // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 N M]
775 stk = b.addSwap(stk) // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 M N]
776 stk = b.addCheckMultisig(stk, k1+k2, e.String())
783 for i := len(e.args) - 1; i >= 0; i-- {
787 stk, k2, err = compileArg(b, stk, contract, clause, env, counts, a)
789 return stk, errors.Wrapf(err, "compiling argument %d in call expression", i)
794 // Do typechecking after compiling subexpressions (because other
795 // compilation errors are more interesting than type mismatch
797 for i, actual := range e.args {
798 if bi.args[i] != "" && actual.typ(env) != bi.args[i] {
799 return stk, fmt.Errorf("argument %d to \"%s\" has type \"%s\", must be \"%s\"", i, bi.name, actual.typ(env), bi.args[i])
803 stk = b.addOps(stk.dropN(k), bi.opcodes, e.String())
805 // special-case reporting
807 case "sha3", "sha256":
808 clause.HashCalls = append(clause.HashCalls, HashCall{bi.name, e.args[0].String(), string(e.args[0].typ(env))})
812 return compileRef(b, stk, counts, e)
815 stk = b.addInt64(stk, int64(e))
818 stk = b.addData(stk, []byte(e))
821 stk = b.addBoolean(stk, bool(e))
824 // Lists are excluded here because they disobey the invariant of
825 // this function: namely, that it increases the stack size by
826 // exactly one. (A list pushes its items and its length on the
827 // stack.) But they're OK as function-call arguments because the
828 // function (presumably) consumes all the stack items added.
829 return stk, fmt.Errorf("encountered list outside of function-call context")
834 func compileArg(b *builder, stk stack, contract *Contract, clause *Clause, env *environ, counts map[string]int, expr expression) (stack, int, error) {
836 if list, ok := expr.(listExpr); ok {
837 for i := 0; i < len(list); i++ {
838 elt := list[len(list)-i-1]
840 stk, err = compileExpr(b, stk, contract, clause, env, counts, elt)
846 stk = b.addInt64(stk, int64(len(list)))
851 stk, err = compileExpr(b, stk, contract, clause, env, counts, expr)
855 func compileRef(b *builder, stk stack, counts map[string]int, ref varRef) (stack, error) {
856 depth := stk.find(string(ref))
858 return stk, fmt.Errorf("undefined reference: \"%s\"", ref)
862 if count, ok := counts[string(ref)]; ok && count > 0 {
864 counts[string(ref)] = count
881 stk = b.addRoll(stk, depth)
883 stk = b.addPick(stk, depth)
889 func (a *ContractArg) UnmarshalJSON(b []byte) error {
890 var m map[string]json.RawMessage
891 err := json.Unmarshal(b, &m)
895 if r, ok := m["boolean"]; ok {
897 err = json.Unmarshal(r, &bval)
904 if r, ok := m["integer"]; ok {
906 err = json.Unmarshal(r, &ival)
915 return fmt.Errorf("contract arg must define one of boolean, integer, string")
917 var sval chainjson.HexBytes
918 err = json.Unmarshal(r, &sval)