10 chainjson "github.com/bytom/encoding/json"
11 "github.com/bytom/errors"
12 "github.com/bytom/protocol/vm"
13 "github.com/bytom/protocol/vm/vmutil"
16 // ValueInfo describes how a blockchain value is used in a contract
18 type ValueInfo struct {
19 // Name is the clause's name for this value.
20 Name string `json:"name"`
22 // Program is the program expression used to the lock the value, if
23 // the value is locked with "lock." If it's unlocked with "unlock"
24 // instead, this is empty.
25 Program string `json:"program,omitempty"`
27 // Asset is the expression describing the asset type the value must
28 // have, as it appears in a clause's "requires" section. If this is
29 // the contract value instead, this is empty.
30 Asset string `json:"asset,omitempty"`
32 // Amount is the expression describing the amount the value must
33 // have, as it appears in a clause's "requires" section. If this is
34 // the contract value instead, this is empty.
35 Amount string `json:"amount,omitempty"`
38 // ContractArg is an argument with which to instantiate a contract as
39 // a program. Exactly one of B, I, and S should be supplied.
40 type ContractArg struct {
41 B *bool `json:"boolean,omitempty"`
42 I *int64 `json:"integer,omitempty"`
43 S *chainjson.HexBytes `json:"string,omitempty"`
46 // Compile parses a sequence of Equity contracts from the supplied reader
47 // and produces Contract objects containing the compiled bytecode and
48 // other analysis. If argMap is non-nil, it maps contract names to
49 // lists of arguments with which to instantiate them as programs, with
50 // the results placed in the contract's Program field. A contract
51 // named in argMap but not found in the input is silently ignored.
52 func Compile(r io.Reader) ([]*Contract, error) {
53 inp, err := ioutil.ReadAll(r)
55 return nil, errors.Wrap(err, "reading input")
57 contracts, err := parse(inp)
59 return nil, errors.Wrap(err, "parse error")
62 globalEnv := newEnviron(nil)
63 for _, k := range keywords {
64 globalEnv.add(k, nilType, roleKeyword)
66 for _, b := range builtins {
67 globalEnv.add(b.name, nilType, roleBuiltin)
70 // All contracts must be checked for recursiveness before any are
72 for _, contract := range contracts {
73 contract.Recursive = checkRecursive(contract)
76 for _, contract := range contracts {
77 err = globalEnv.addContract(contract)
83 for _, contract := range contracts {
84 err = compileContract(contract, globalEnv)
86 return nil, errors.Wrap(err, "compiling contract")
88 for _, clause := range contract.Clauses {
89 for _, stmt := range clause.statements {
90 switch s := stmt.(type) {
92 valueInfo := ValueInfo{
93 Amount: s.lockedAmount.String(),
94 Asset: s.lockedAsset.String(),
95 Program: s.program.String(),
98 clause.Values = append(clause.Values, valueInfo)
99 case *unlockStatement:
100 valueInfo := ValueInfo{
101 Amount: contract.Value.Amount,
102 Asset: contract.Value.Asset,
104 clause.Values = append(clause.Values, valueInfo)
110 return contracts, nil
113 func Instantiate(body []byte, params []*Param, recursive bool, args []ContractArg) ([]byte, error) {
114 if len(args) != len(params) {
115 return nil, fmt.Errorf("got %d argument(s), want %d", len(args), len(params))
118 // typecheck args against param types
119 for i, param := range params {
122 case amountType, intType:
124 return nil, fmt.Errorf("type mismatch in arg %d (want integer)", i)
126 case assetType, hashType, progType, pubkeyType, sigType, strType:
128 return nil, fmt.Errorf("type mismatch in arg %d (want string)", i)
132 return nil, fmt.Errorf("type mismatch in arg %d (want boolean)", i)
137 b := vmutil.NewBuilder()
139 for i := len(args) - 1; i >= 0; i-- {
156 // <argN> <argN-1> ... <arg1> <body> DEPTH OVER 0 CHECKPREDICATE
158 b.AddOp(vm.OP_DEPTH).AddOp(vm.OP_OVER)
160 // <argN> <argN-1> ... <arg1> DEPTH <body> 0 CHECKPREDICATE
165 b.AddOp(vm.OP_CHECKPREDICATE)
169 func compileContract(contract *Contract, globalEnv *environ) error {
172 if len(contract.Clauses) == 0 {
173 return fmt.Errorf("empty contract")
175 env := newEnviron(globalEnv)
176 for _, p := range contract.Params {
177 err = env.add(p.Name, p.Type, roleContractParam)
183 // value is spilt with valueAmount and valueAsset
184 if err = env.add(contract.Value.Amount, amountType, roleContractValue); err != nil {
187 if err = env.add(contract.Value.Asset, assetType, roleContractValue); err != nil {
191 for _, c := range contract.Clauses {
192 err = env.add(c.Name, nilType, roleClause)
198 err = prohibitSigParams(contract)
202 err = requireAllParamsUsedInClauses(contract.Params, contract.Clauses)
209 if len(contract.Clauses) > 1 {
210 stk = stk.add("<clause selector>")
213 for i := len(contract.Params) - 1; i >= 0; i-- {
214 p := contract.Params[i]
215 stk = stk.add(p.Name)
218 if contract.Recursive {
219 stk = stk.add(contract.Name)
223 sequence := 0 // sequence is used to count the number of ifStatements
225 if len(contract.Clauses) == 1 {
226 err = compileClause(b, stk, contract, env, contract.Clauses[0], &sequence)
231 if len(contract.Params) > 0 {
232 // A clause selector is at the bottom of the stack. Roll it to the
234 n := len(contract.Params)
235 if contract.Recursive {
238 stk = b.addRoll(stk, n) // stack: [<clause params> <contract params> [<maybe contract body>] <clause selector>]
244 for i := len(contract.Clauses) - 1; i >= 2; i-- {
245 stk = b.addDup(stk) // stack: [... <clause selector> <clause selector>]
246 stk = b.addInt64(stk, int64(i)) // stack: [... <clause selector> <clause selector> <i>]
247 stk = b.addNumEqual(stk, fmt.Sprintf("(<clause selector> == %d)", i)) // stack: [... <clause selector> <i == clause selector>]
248 stk = b.addJumpIf(stk, contract.Clauses[i].Name) // stack: [... <clause selector>]
249 stk2 = stk // stack starts here for clauses 2 through N-1
253 stk = b.addJumpIf(stk, contract.Clauses[1].Name) // consumes the clause selector
255 // no jump needed for clause 0
257 for i, clause := range contract.Clauses {
259 // Clauses 0 and 1 have no clause selector on top of the
260 // stack. Clauses 2 and later do.
264 b.addJumpTarget(stk, clause.Name)
270 err = compileClause(b, stk, contract, env, clause, &sequence)
272 return errors.Wrapf(err, "compiling clause \"%s\"", clause.Name)
274 b.forgetPendingVerify()
275 if i < len(contract.Clauses)-1 {
276 b.addJump(stk, "_end")
279 b.addJumpTarget(stk, "_end")
282 opcodes := optimize(b.opcodes())
283 prog, err := vm.Assemble(opcodes)
289 contract.Opcodes = opcodes
291 contract.Steps = b.steps()
296 func compileClause(b *builder, contractStk stack, contract *Contract, env *environ, clause *Clause, sequence *int) error {
299 // copy env to leave outerEnv unchanged
300 env = newEnviron(env)
301 for _, p := range clause.Params {
302 err = env.add(p.Name, p.Type, roleClauseParam)
308 if err = assignIndexes(clause); err != nil {
313 for _, p := range clause.Params {
314 // NOTE: the order of clause params is not reversed, unlike
315 // contract params (and also unlike the arguments to Equity
317 stk = stk.add(p.Name)
319 stk = stk.addFromStack(contractStk)
321 // a count of the number of times each variable is referenced
322 counts := make(map[string]int)
323 for _, s := range clause.statements {
324 s.countVarRefs(counts)
325 if stmt, ok := s.(*ifStatement); ok {
326 for _, trueStmt := range stmt.body.trueBody {
327 trueStmt.countVarRefs(counts)
330 for _, falseStmt := range stmt.body.falseBody {
331 falseStmt.countVarRefs(counts)
336 for _, stat := range clause.statements {
337 if stk, err = compileStatement(b, stk, contract, env, clause, counts, stat, sequence); err != nil {
342 err = requireAllValuesDisposedOnce(contract, clause)
346 err = typeCheckClause(contract, clause, env)
350 err = requireAllParamsUsedInClause(clause.Params, clause)
358 func compileStatement(b *builder, stk stack, contract *Contract, env *environ, clause *Clause, counts map[string]int, stat statement, sequence *int) (stack, error) {
360 switch stmt := stat.(type) {
362 // sequence add 1 when the statement is ifStatement
364 strSequence := fmt.Sprintf("%d", *sequence)
366 // compile condition expression
367 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.condition)
369 return stk, errors.Wrapf(err, "in check condition of ifStatement in clause \"%s\"", clause.Name)
372 // jump to falseBody when condition is false, while the JUMPIF instruction will be run success when
373 // the value of dataStack is true, therefore add this check
374 conditionExpr := stk.str
375 stk = b.addBoolean(stk, false)
376 stk = b.addEqual(stk, fmt.Sprintf("(%s == false)", conditionExpr)) // stack: [... <condition_result == false>]
380 if len(stmt.body.falseBody) != 0 {
381 label = "else_" + strSequence
383 label = "endif_" + strSequence
385 stk = b.addJumpIf(stk, label)
386 b.addJumpTarget(stk, "if_"+strSequence)
388 // temporary store stack and counts for falseBody
390 elseCounts := make(map[string]int)
391 for k, v := range counts {
395 // compile trueBody statements
396 if len(stmt.body.trueBody) != 0 {
397 for _, st := range stmt.body.trueBody {
398 st.countVarRefs(counts)
401 // modify value amount because of using only once
402 if counts[contract.Value.Amount] > 1 {
403 counts[contract.Value.Amount] = 1
406 // modify value asset because of using only once
407 if counts[contract.Value.Asset] > 1 {
408 counts[contract.Value.Asset] = 1
411 for _, st := range stmt.body.trueBody {
412 if stk, err = compileStatement(b, stk, contract, env, clause, counts, st, sequence); err != nil {
418 // compile falseBody statements
419 if len(stmt.body.falseBody) != 0 {
420 counts := make(map[string]int)
421 for k, v := range elseCounts {
425 for _, st := range stmt.body.falseBody {
426 st.countVarRefs(counts)
429 // modify value amount because of using only once
430 if counts[contract.Value.Amount] > 1 {
431 counts[contract.Value.Amount] = 1
434 // modify value asset because of using only once
435 if counts[contract.Value.Asset] > 1 {
436 counts[contract.Value.Asset] = 1
440 b.addJump(stk, "endif_"+strSequence)
441 b.addJumpTarget(stk, "else_"+strSequence)
443 for _, st := range stmt.body.falseBody {
444 if stk, err = compileStatement(b, stk, contract, env, clause, counts, st, sequence); err != nil {
449 b.addJumpTarget(stk, "endif_"+strSequence)
451 case *defineStatement:
453 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
455 return stk, errors.Wrapf(err, "in define statement in clause \"%s\"", clause.Name)
459 stk.str = stmt.varName.Name
461 // add environ for define variable
462 if err = env.add(stmt.varName.Name, stmt.varName.Type, roleClauseVariable); err != nil {
466 case *verifyStatement:
467 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
469 return stk, errors.Wrapf(err, "in verify statement in clause \"%s\"", clause.Name)
471 stk = b.addVerify(stk)
473 // special-case reporting of certain function calls
474 if c, ok := stmt.expr.(*callExpr); ok && len(c.args) == 1 {
475 if b := referencedBuiltin(c.fn); b != nil {
478 clause.BlockHeight = append(clause.BlockHeight, c.args[0].String())
480 clause.BlockHeight = append(clause.BlockHeight, c.args[0].String())
487 stk = b.addInt64(stk, stmt.index)
489 // TODO: permit more complex expressions for locked,
490 // like "lock x+y with foo" (?)
492 if stmt.lockedAmount.String() == contract.Value.Amount && stmt.lockedAsset.String() == contract.Value.Asset {
493 stk = b.addAmount(stk, contract.Value.Amount)
494 stk = b.addAsset(stk, contract.Value.Asset)
497 if stmt.lockedAmount.String() == contract.Value.Amount {
498 stk = b.addAmount(stk, contract.Value.Amount)
499 } else if strings.Contains(stmt.lockedAmount.String(), contract.Value.Amount) {
500 stk = b.addAmount(stk, contract.Value.Amount)
501 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAmount)
503 return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
506 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAmount)
508 return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
513 if stmt.lockedAsset.String() == contract.Value.Asset {
514 stk = b.addAsset(stk, contract.Value.Asset)
515 } else if strings.Contains(stmt.lockedAsset.String(), contract.Value.Asset) {
516 stk = b.addAsset(stk, contract.Value.Asset)
517 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAsset)
519 return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
522 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAsset)
524 return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
530 stk = b.addInt64(stk, 1)
533 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.program)
535 return stk, errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
538 stk = b.addCheckOutput(stk, fmt.Sprintf("checkOutput(%s, %s, %s)",
539 stmt.lockedAmount.String(), stmt.lockedAsset.String(), stmt.program))
540 stk = b.addVerify(stk)
542 case *unlockStatement:
543 if len(clause.statements) == 1 {
544 // This is the only statement in the clause, make sure TRUE is
546 stk = b.addBoolean(stk, true)
553 func compileExpr(b *builder, stk stack, contract *Contract, clause *Clause, env *environ, counts map[string]int, expr expression) (stack, error) {
556 switch e := expr.(type) {
558 // Do typechecking after compiling subexpressions (because other
559 // compilation errors are more interesting than type mismatch
562 stk, err = compileExpr(b, stk, contract, clause, env, counts, e.left)
564 return stk, errors.Wrapf(err, "in left operand of \"%s\" expression", e.op.op)
566 stk, err = compileExpr(b, stk, contract, clause, env, counts, e.right)
568 return stk, errors.Wrapf(err, "in right operand of \"%s\" expression", e.op.op)
571 lType := e.left.typ(env)
572 if e.op.left != "" && !(lType == e.op.left || lType == amountType) {
573 return stk, fmt.Errorf("in \"%s\", left operand has type \"%s\", must be \"%s\"", e, lType, e.op.left)
576 rType := e.right.typ(env)
577 if e.op.right != "" && !(rType == e.op.right || rType == amountType) {
578 return stk, fmt.Errorf("in \"%s\", right operand has type \"%s\", must be \"%s\"", e, rType, e.op.right)
584 // Maybe one is Hash and the other is (more-specific-Hash subtype).
585 // TODO(bobg): generalize this mechanism
586 if lType == hashType && isHashSubtype(rType) {
587 propagateType(contract, clause, env, rType, e.left)
588 } else if rType == hashType && isHashSubtype(lType) {
589 propagateType(contract, clause, env, lType, e.right)
591 return stk, fmt.Errorf("type mismatch in \"%s\": left operand has type \"%s\", right operand has type \"%s\"", e, lType, rType)
594 if lType == "Boolean" {
595 return stk, fmt.Errorf("in \"%s\": using \"%s\" on Boolean values not allowed", e, e.op.op)
599 stk = b.addOps(stk.dropN(2), e.op.opcodes, e.String())
602 // Do typechecking after compiling subexpression (because other
603 // compilation errors are more interesting than type mismatch
607 stk, err = compileExpr(b, stk, contract, clause, env, counts, e.expr)
609 return stk, errors.Wrapf(err, "in \"%s\" expression", e.op.op)
612 if e.op.operand != "" && e.expr.typ(env) != e.op.operand {
613 return stk, fmt.Errorf("in \"%s\", operand has type \"%s\", must be \"%s\"", e, e.expr.typ(env), e.op.operand)
615 b.addOps(stk.drop(), e.op.opcodes, e.String())
618 bi := referencedBuiltin(e.fn)
620 if v, ok := e.fn.(varRef); ok {
621 if entry := env.lookup(string(v)); entry != nil && entry.t == contractType {
622 clause.Contracts = append(clause.Contracts, entry.c.Name)
624 partialName := fmt.Sprintf("%s(...)", v)
625 stk = b.addData(stk, nil)
627 if len(e.args) != len(entry.c.Params) {
628 return stk, fmt.Errorf("contract \"%s\" expects %d argument(s), got %d", entry.c.Name, len(entry.c.Params), len(e.args))
631 for i := len(e.args) - 1; i >= 0; i-- {
633 if entry.c.Params[i].Type != "" && arg.typ(env) != entry.c.Params[i].Type {
634 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)
636 stk, err = compileExpr(b, stk, contract, clause, env, counts, arg)
640 stk = b.addCatPushdata(stk, partialName)
644 case entry.c == contract:
645 // Recursive call - cannot use entry.c.Body
646 // <argN> <argN-1> ... <arg1> <body> DEPTH OVER 0 CHECKPREDICATE
647 stk, err = compileRef(b, stk, counts, varRef(contract.Name))
649 return stk, errors.Wrap(err, "compiling contract call")
651 stk = b.addCatPushdata(stk, partialName)
652 stk = b.addData(stk, []byte{byte(vm.OP_DEPTH), byte(vm.OP_OVER)})
653 stk = b.addCat(stk, partialName)
655 case entry.c.Recursive:
656 // Non-recursive call to a (different) recursive contract
657 // <argN> <argN-1> ... <arg1> <body> DEPTH OVER 0 CHECKPREDICATE
658 if len(entry.c.Body) == 0 {
659 // TODO(bobg): sort input contracts topologically to permit forward calling
660 return stk, fmt.Errorf("contract \"%s\" not defined", entry.c.Name)
662 stk = b.addData(stk, entry.c.Body)
663 stk = b.addCatPushdata(stk, partialName)
664 stk = b.addData(stk, []byte{byte(vm.OP_DEPTH), byte(vm.OP_OVER)})
665 stk = b.addCat(stk, partialName)
668 // Non-recursive call to non-recursive contract
669 // <argN> <argN-1> ... <arg1> DEPTH <body> 0 CHECKPREDICATE
670 stk = b.addData(stk, []byte{byte(vm.OP_DEPTH)})
671 stk = b.addCat(stk, partialName)
672 if len(entry.c.Body) == 0 {
673 // TODO(bobg): sort input contracts topologically to permit forward calling
674 return stk, fmt.Errorf("contract \"%s\" not defined", entry.c.Name)
676 stk = b.addData(stk, entry.c.Body)
677 stk = b.addCatPushdata(stk, partialName)
679 stk = b.addData(stk, vm.Int64Bytes(0))
680 stk = b.addCatPushdata(stk, partialName)
681 stk = b.addData(stk, []byte{byte(vm.OP_CHECKPREDICATE)})
682 stk = b.addCat(stk, e.String())
687 return stk, fmt.Errorf("unknown function \"%s\"", e.fn)
690 if len(e.args) != len(bi.args) {
691 return stk, fmt.Errorf("wrong number of args for \"%s\": have %d, want %d", bi.name, len(e.args), len(bi.args))
694 // WARNING WARNING WOOP WOOP
696 // WARNING WARNING WOOP WOOP
697 if bi.name == "checkTxMultiSig" {
698 if _, ok := e.args[0].(listExpr); !ok {
699 return stk, fmt.Errorf("checkTxMultiSig expects list literals, got %T for argument 0", e.args[0])
701 if _, ok := e.args[1].(listExpr); !ok {
702 return stk, fmt.Errorf("checkTxMultiSig expects list literals, got %T for argument 1", e.args[1])
707 stk, k1, err = compileArg(b, stk, contract, clause, env, counts, e.args[1])
712 // stack: [... sigM ... sig1 M]
715 stk, altEntry = b.addToAltStack(stk) // stack: [... sigM ... sig1]
716 stk = b.addTxSigHash(stk) // stack: [... sigM ... sig1 txsighash]
718 stk, k2, err = compileArg(b, stk, contract, clause, env, counts, e.args[0])
723 // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 N]
725 stk = b.addFromAltStack(stk, altEntry) // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 N M]
726 stk = b.addSwap(stk) // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 M N]
727 stk = b.addCheckMultisig(stk, k1+k2, e.String())
734 for i := len(e.args) - 1; i >= 0; i-- {
738 stk, k2, err = compileArg(b, stk, contract, clause, env, counts, a)
740 return stk, errors.Wrapf(err, "compiling argument %d in call expression", i)
745 // Do typechecking after compiling subexpressions (because other
746 // compilation errors are more interesting than type mismatch
748 for i, actual := range e.args {
749 if bi.args[i] != "" && actual.typ(env) != bi.args[i] {
750 return stk, fmt.Errorf("argument %d to \"%s\" has type \"%s\", must be \"%s\"", i, bi.name, actual.typ(env), bi.args[i])
754 stk = b.addOps(stk.dropN(k), bi.opcodes, e.String())
756 // special-case reporting
758 case "sha3", "sha256":
759 clause.HashCalls = append(clause.HashCalls, HashCall{bi.name, e.args[0].String(), string(e.args[0].typ(env))})
763 return compileRef(b, stk, counts, e)
766 stk = b.addInt64(stk, int64(e))
769 stk = b.addData(stk, []byte(e))
772 stk = b.addBoolean(stk, bool(e))
775 // Lists are excluded here because they disobey the invariant of
776 // this function: namely, that it increases the stack size by
777 // exactly one. (A list pushes its items and its length on the
778 // stack.) But they're OK as function-call arguments because the
779 // function (presumably) consumes all the stack items added.
780 return stk, fmt.Errorf("encountered list outside of function-call context")
785 func compileArg(b *builder, stk stack, contract *Contract, clause *Clause, env *environ, counts map[string]int, expr expression) (stack, int, error) {
787 if list, ok := expr.(listExpr); ok {
788 for i := 0; i < len(list); i++ {
789 elt := list[len(list)-i-1]
791 stk, err = compileExpr(b, stk, contract, clause, env, counts, elt)
797 stk = b.addInt64(stk, int64(len(list)))
802 stk, err = compileExpr(b, stk, contract, clause, env, counts, expr)
806 func compileRef(b *builder, stk stack, counts map[string]int, ref varRef) (stack, error) {
807 depth := stk.find(string(ref))
809 return stk, fmt.Errorf("undefined reference: \"%s\"", ref)
813 if count, ok := counts[string(ref)]; ok && count > 0 {
815 counts[string(ref)] = count
832 stk = b.addRoll(stk, depth)
834 stk = b.addPick(stk, depth)
840 func (a *ContractArg) UnmarshalJSON(b []byte) error {
841 var m map[string]json.RawMessage
842 err := json.Unmarshal(b, &m)
846 if r, ok := m["boolean"]; ok {
848 err = json.Unmarshal(r, &bval)
855 if r, ok := m["integer"]; ok {
857 err = json.Unmarshal(r, &ival)
866 return fmt.Errorf("contract arg must define one of boolean, integer, string")
868 var sval chainjson.HexBytes
869 err = json.Unmarshal(r, &sval)