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 // 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 Name: s.locked.String(),
93 Program: s.program.String(),
95 if s.locked.String() != contract.Value {
96 for _, r := range clause.Reqs {
97 if s.locked.String() == r.Name {
98 valueInfo.Asset = r.assetExpr.String()
99 valueInfo.Amount = r.amountExpr.String()
104 clause.Values = append(clause.Values, valueInfo)
105 case *unlockStatement:
106 valueInfo := ValueInfo{Name: contract.Value}
107 clause.Values = append(clause.Values, valueInfo)
113 return contracts, nil
116 func Instantiate(body []byte, params []*Param, recursive bool, args []ContractArg) ([]byte, error) {
117 if len(args) != len(params) {
118 return nil, fmt.Errorf("got %d argument(s), want %d", len(args), len(params))
121 // typecheck args against param types
122 for i, param := range params {
125 case amountType, intType:
127 return nil, fmt.Errorf("type mismatch in arg %d (want integer)", i)
129 case assetType, hashType, progType, pubkeyType, sigType, strType:
131 return nil, fmt.Errorf("type mismatch in arg %d (want string)", i)
135 return nil, fmt.Errorf("type mismatch in arg %d (want boolean)", i)
140 b := vmutil.NewBuilder()
142 for i := len(args) - 1; i >= 0; i-- {
159 // <argN> <argN-1> ... <arg1> <body> DEPTH OVER 0 CHECKPREDICATE
161 b.AddOp(vm.OP_DEPTH).AddOp(vm.OP_OVER)
163 // <argN> <argN-1> ... <arg1> DEPTH <body> 0 CHECKPREDICATE
168 b.AddOp(vm.OP_CHECKPREDICATE)
172 func compileContract(contract *Contract, globalEnv *environ) error {
175 if len(contract.Clauses) == 0 {
176 return fmt.Errorf("empty contract")
178 env := newEnviron(globalEnv)
179 for _, p := range contract.Params {
180 err = env.add(p.Name, p.Type, roleContractParam)
185 err = env.add(contract.Value, valueType, roleContractValue)
189 for _, c := range contract.Clauses {
190 err = env.add(c.Name, nilType, roleClause)
196 err = prohibitValueParams(contract)
200 err = prohibitSigParams(contract)
204 err = requireAllParamsUsedInClauses(contract.Params, contract.Clauses)
211 if len(contract.Clauses) > 1 {
212 stk = stk.add("<clause selector>")
215 for i := len(contract.Params) - 1; i >= 0; i-- {
216 p := contract.Params[i]
217 stk = stk.add(p.Name)
220 if contract.Recursive {
221 stk = stk.add(contract.Name)
226 if len(contract.Clauses) == 1 {
227 err = compileClause(b, stk, contract, env, contract.Clauses[0])
232 if len(contract.Params) > 0 {
233 // A clause selector is at the bottom of the stack. Roll it to the
235 n := len(contract.Params)
236 if contract.Recursive {
239 stk = b.addRoll(stk, n) // stack: [<clause params> <contract params> [<maybe contract body>] <clause selector>]
245 for i := len(contract.Clauses) - 1; i >= 2; i-- {
246 stk = b.addDup(stk) // stack: [... <clause selector> <clause selector>]
247 stk = b.addInt64(stk, int64(i)) // stack: [... <clause selector> <clause selector> <i>]
248 stk = b.addNumEqual(stk, fmt.Sprintf("(<clause selector> == %d)", i)) // stack: [... <clause selector> <i == clause selector>]
249 stk = b.addJumpIf(stk, contract.Clauses[i].Name) // stack: [... <clause selector>]
250 stk2 = stk // stack starts here for clauses 2 through N-1
254 stk = b.addJumpIf(stk, contract.Clauses[1].Name) // consumes the clause selector
256 // no jump needed for clause 0
258 for i, clause := range contract.Clauses {
260 // Clauses 0 and 1 have no clause selector on top of the
261 // stack. Clauses 2 and later do.
265 b.addJumpTarget(stk, clause.Name)
271 err = compileClause(b, stk, contract, env, clause)
273 return errors.Wrapf(err, "compiling clause \"%s\"", clause.Name)
275 b.forgetPendingVerify()
276 if i < len(contract.Clauses)-1 {
277 b.addJump(stk, "_end")
280 b.addJumpTarget(stk, "_end")
283 opcodes := optimize(b.opcodes())
284 prog, err := vm.Assemble(opcodes)
290 contract.Opcodes = opcodes
292 contract.Steps = b.steps()
297 func compileClause(b *builder, contractStk stack, contract *Contract, env *environ, clause *Clause) error {
300 // copy env to leave outerEnv unchanged
301 env = newEnviron(env)
302 for _, p := range clause.Params {
303 err = env.add(p.Name, p.Type, roleClauseParam)
308 for _, req := range clause.Reqs {
309 err = env.add(req.Name, valueType, roleClauseValue)
313 req.Asset = req.assetExpr.String()
314 req.Amount = req.amountExpr.String()
317 assignIndexes(clause)
320 for _, p := range clause.Params {
321 // NOTE: the order of clause params is not reversed, unlike
322 // contract params (and also unlike the arguments to Equity
324 stk = stk.add(p.Name)
326 stk = stk.addFromStack(contractStk)
328 // a count of the number of times each variable is referenced
329 counts := make(map[string]int)
330 for _, req := range clause.Reqs {
331 req.assetExpr.countVarRefs(counts)
332 req.amountExpr.countVarRefs(counts)
334 for _, s := range clause.statements {
335 s.countVarRefs(counts)
338 for _, s := range clause.statements {
339 switch stmt := s.(type) {
340 case *verifyStatement:
341 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
343 return errors.Wrapf(err, "in verify statement in clause \"%s\"", clause.Name)
345 stk = b.addVerify(stk)
347 // special-case reporting of certain function calls
348 if c, ok := stmt.expr.(*callExpr); ok && len(c.args) == 1 {
349 if b := referencedBuiltin(c.fn); b != nil {
352 clause.BlockHeight = append(clause.BlockHeight, c.args[0].String())
354 clause.BlockHeight = append(clause.BlockHeight, c.args[0].String())
361 stk = b.addInt64(stk, stmt.index)
363 // TODO: permit more complex expressions for locked,
364 // like "lock x+y with foo" (?)
366 if stmt.locked.String() == contract.Value {
367 stk = b.addAmount(stk)
368 stk = b.addAsset(stk)
371 for _, r := range clause.Reqs {
372 if stmt.locked.String() == r.Name {
378 return fmt.Errorf("unknown value \"%s\" in lock statement in clause \"%s\"", stmt.locked, clause.Name)
382 stk, err = compileExpr(b, stk, contract, clause, env, counts, req.amountExpr)
384 return errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
388 stk, err = compileExpr(b, stk, contract, clause, env, counts, req.assetExpr)
390 return errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
395 stk = b.addInt64(stk, 1)
398 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.program)
400 return errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
403 stk = b.addCheckOutput(stk, fmt.Sprintf("checkOutput(%s, %s)", stmt.locked, stmt.program))
404 stk = b.addVerify(stk)
406 case *unlockStatement:
407 if len(clause.statements) == 1 {
408 // This is the only statement in the clause, make sure TRUE is
410 stk = b.addBoolean(stk, true)
415 err = requireAllValuesDisposedOnce(contract, clause)
419 err = typeCheckClause(contract, clause, env)
423 err = requireAllParamsUsedInClause(clause.Params, clause)
431 func compileExpr(b *builder, stk stack, contract *Contract, clause *Clause, env *environ, counts map[string]int, expr expression) (stack, error) {
434 switch e := expr.(type) {
436 // Do typechecking after compiling subexpressions (because other
437 // compilation errors are more interesting than type mismatch
440 stk, err = compileExpr(b, stk, contract, clause, env, counts, e.left)
442 return stk, errors.Wrapf(err, "in left operand of \"%s\" expression", e.op.op)
444 stk, err = compileExpr(b, stk, contract, clause, env, counts, e.right)
446 return stk, errors.Wrapf(err, "in right operand of \"%s\" expression", e.op.op)
449 lType := e.left.typ(env)
450 if e.op.left != "" && lType != e.op.left {
451 return stk, fmt.Errorf("in \"%s\", left operand has type \"%s\", must be \"%s\"", e, lType, e.op.left)
454 rType := e.right.typ(env)
455 if e.op.right != "" && rType != e.op.right {
456 return stk, fmt.Errorf("in \"%s\", right operand has type \"%s\", must be \"%s\"", e, rType, e.op.right)
462 // Maybe one is Hash and the other is (more-specific-Hash subtype).
463 // TODO(bobg): generalize this mechanism
464 if lType == hashType && isHashSubtype(rType) {
465 propagateType(contract, clause, env, rType, e.left)
466 } else if rType == hashType && isHashSubtype(lType) {
467 propagateType(contract, clause, env, lType, e.right)
469 return stk, fmt.Errorf("type mismatch in \"%s\": left operand has type \"%s\", right operand has type \"%s\"", e, lType, rType)
472 if lType == "Boolean" {
473 return stk, fmt.Errorf("in \"%s\": using \"%s\" on Boolean values not allowed", e, e.op.op)
477 stk = b.addOps(stk.dropN(2), e.op.opcodes, e.String())
480 // Do typechecking after compiling subexpression (because other
481 // compilation errors are more interesting than type mismatch
485 stk, err = compileExpr(b, stk, contract, clause, env, counts, e.expr)
487 return stk, errors.Wrapf(err, "in \"%s\" expression", e.op.op)
490 if e.op.operand != "" && e.expr.typ(env) != e.op.operand {
491 return stk, fmt.Errorf("in \"%s\", operand has type \"%s\", must be \"%s\"", e, e.expr.typ(env), e.op.operand)
493 b.addOps(stk.drop(), e.op.opcodes, e.String())
496 bi := referencedBuiltin(e.fn)
498 if v, ok := e.fn.(varRef); ok {
499 if entry := env.lookup(string(v)); entry != nil && entry.t == contractType {
500 clause.Contracts = append(clause.Contracts, entry.c.Name)
502 partialName := fmt.Sprintf("%s(...)", v)
503 stk = b.addData(stk, nil)
505 if len(e.args) != len(entry.c.Params) {
506 return stk, fmt.Errorf("contract \"%s\" expects %d argument(s), got %d", entry.c.Name, len(entry.c.Params), len(e.args))
509 for i := len(e.args) - 1; i >= 0; i-- {
511 if entry.c.Params[i].Type != "" && arg.typ(env) != entry.c.Params[i].Type {
512 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)
514 stk, err = compileExpr(b, stk, contract, clause, env, counts, arg)
518 stk = b.addCatPushdata(stk, partialName)
522 case entry.c == contract:
523 // Recursive call - cannot use entry.c.Body
524 // <argN> <argN-1> ... <arg1> <body> DEPTH OVER 0 CHECKPREDICATE
525 stk, err = compileRef(b, stk, counts, varRef(contract.Name))
527 return stk, errors.Wrap(err, "compiling contract call")
529 stk = b.addCatPushdata(stk, partialName)
530 stk = b.addData(stk, []byte{byte(vm.OP_DEPTH), byte(vm.OP_OVER)})
531 stk = b.addCat(stk, partialName)
533 case entry.c.Recursive:
534 // Non-recursive call to a (different) recursive contract
535 // <argN> <argN-1> ... <arg1> <body> DEPTH OVER 0 CHECKPREDICATE
536 if len(entry.c.Body) == 0 {
537 // TODO(bobg): sort input contracts topologically to permit forward calling
538 return stk, fmt.Errorf("contract \"%s\" not defined", entry.c.Name)
540 stk = b.addData(stk, entry.c.Body)
541 stk = b.addCatPushdata(stk, partialName)
542 stk = b.addData(stk, []byte{byte(vm.OP_DEPTH), byte(vm.OP_OVER)})
543 stk = b.addCat(stk, partialName)
546 // Non-recursive call to non-recursive contract
547 // <argN> <argN-1> ... <arg1> DEPTH <body> 0 CHECKPREDICATE
548 stk = b.addData(stk, []byte{byte(vm.OP_DEPTH)})
549 stk = b.addCat(stk, partialName)
550 if len(entry.c.Body) == 0 {
551 // TODO(bobg): sort input contracts topologically to permit forward calling
552 return stk, fmt.Errorf("contract \"%s\" not defined", entry.c.Name)
554 stk = b.addData(stk, entry.c.Body)
555 stk = b.addCatPushdata(stk, partialName)
557 stk = b.addData(stk, vm.Int64Bytes(0))
558 stk = b.addCatPushdata(stk, partialName)
559 stk = b.addData(stk, []byte{byte(vm.OP_CHECKPREDICATE)})
560 stk = b.addCat(stk, e.String())
565 return stk, fmt.Errorf("unknown function \"%s\"", e.fn)
568 if len(e.args) != len(bi.args) {
569 return stk, fmt.Errorf("wrong number of args for \"%s\": have %d, want %d", bi.name, len(e.args), len(bi.args))
572 // WARNING WARNING WOOP WOOP
574 // WARNING WARNING WOOP WOOP
575 if bi.name == "checkTxMultiSig" {
576 if _, ok := e.args[0].(listExpr); !ok {
577 return stk, fmt.Errorf("checkTxMultiSig expects list literals, got %T for argument 0", e.args[0])
579 if _, ok := e.args[1].(listExpr); !ok {
580 return stk, fmt.Errorf("checkTxMultiSig expects list literals, got %T for argument 1", e.args[1])
585 stk, k1, err = compileArg(b, stk, contract, clause, env, counts, e.args[1])
590 // stack: [... sigM ... sig1 M]
593 stk, altEntry = b.addToAltStack(stk) // stack: [... sigM ... sig1]
594 stk = b.addTxSigHash(stk) // stack: [... sigM ... sig1 txsighash]
596 stk, k2, err = compileArg(b, stk, contract, clause, env, counts, e.args[0])
601 // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 N]
603 stk = b.addFromAltStack(stk, altEntry) // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 N M]
604 stk = b.addSwap(stk) // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 M N]
605 stk = b.addCheckMultisig(stk, k1+k2, e.String())
612 for i := len(e.args) - 1; i >= 0; i-- {
616 stk, k2, err = compileArg(b, stk, contract, clause, env, counts, a)
618 return stk, errors.Wrapf(err, "compiling argument %d in call expression", i)
623 // Do typechecking after compiling subexpressions (because other
624 // compilation errors are more interesting than type mismatch
626 for i, actual := range e.args {
627 if bi.args[i] != "" && actual.typ(env) != bi.args[i] {
628 return stk, fmt.Errorf("argument %d to \"%s\" has type \"%s\", must be \"%s\"", i, bi.name, actual.typ(env), bi.args[i])
632 stk = b.addOps(stk.dropN(k), bi.opcodes, e.String())
634 // special-case reporting
636 case "sha3", "sha256":
637 clause.HashCalls = append(clause.HashCalls, HashCall{bi.name, e.args[0].String(), string(e.args[0].typ(env))})
641 return compileRef(b, stk, counts, e)
644 stk = b.addInt64(stk, int64(e))
647 stk = b.addData(stk, []byte(e))
650 stk = b.addBoolean(stk, bool(e))
653 // Lists are excluded here because they disobey the invariant of
654 // this function: namely, that it increases the stack size by
655 // exactly one. (A list pushes its items and its length on the
656 // stack.) But they're OK as function-call arguments because the
657 // function (presumably) consumes all the stack items added.
658 return stk, fmt.Errorf("encountered list outside of function-call context")
663 func compileArg(b *builder, stk stack, contract *Contract, clause *Clause, env *environ, counts map[string]int, expr expression) (stack, int, error) {
665 if list, ok := expr.(listExpr); ok {
666 for i := 0; i < len(list); i++ {
667 elt := list[len(list)-i-1]
669 stk, err = compileExpr(b, stk, contract, clause, env, counts, elt)
675 stk = b.addInt64(stk, int64(len(list)))
680 stk, err = compileExpr(b, stk, contract, clause, env, counts, expr)
684 func compileRef(b *builder, stk stack, counts map[string]int, ref varRef) (stack, error) {
685 depth := stk.find(string(ref))
687 return stk, fmt.Errorf("undefined reference: \"%s\"", ref)
691 if count, ok := counts[string(ref)]; ok && count > 0 {
693 counts[string(ref)] = count
710 stk = b.addRoll(stk, depth)
712 stk = b.addPick(stk, depth)
718 func (a *ContractArg) UnmarshalJSON(b []byte) error {
719 var m map[string]json.RawMessage
720 err := json.Unmarshal(b, &m)
724 if r, ok := m["boolean"]; ok {
726 err = json.Unmarshal(r, &bval)
733 if r, ok := m["integer"]; ok {
735 err = json.Unmarshal(r, &ival)
744 return fmt.Errorf("contract arg must define one of boolean, integer, string")
746 var sval chainjson.HexBytes
747 err = json.Unmarshal(r, &sval)