OSDN Git Service

add define statement for equity (#6)
[bytom/equity.git] / compiler / compile.go
1 package compiler
2
3 import (
4         "encoding/json"
5         "fmt"
6         "io"
7         "io/ioutil"
8         "strings"
9
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"
14 )
15
16 // ValueInfo describes how a blockchain value is used in a contract
17 // clause.
18 type ValueInfo struct {
19         // Name is the clause's name for this value.
20         Name string `json:"name"`
21
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"`
26
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"`
31
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"`
36 }
37
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"`
44 }
45
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)
54         if err != nil {
55                 return nil, errors.Wrap(err, "reading input")
56         }
57         contracts, err := parse(inp)
58         if err != nil {
59                 return nil, errors.Wrap(err, "parse error")
60         }
61
62         globalEnv := newEnviron(nil)
63         for _, k := range keywords {
64                 globalEnv.add(k, nilType, roleKeyword)
65         }
66         for _, b := range builtins {
67                 globalEnv.add(b.name, nilType, roleBuiltin)
68         }
69
70         // All contracts must be checked for recursiveness before any are
71         // compiled.
72         for _, contract := range contracts {
73                 contract.Recursive = checkRecursive(contract)
74         }
75
76         for _, contract := range contracts {
77                 err = globalEnv.addContract(contract)
78                 if err != nil {
79                         return nil, err
80                 }
81         }
82
83         for _, contract := range contracts {
84                 err = compileContract(contract, globalEnv)
85                 if err != nil {
86                         return nil, errors.Wrap(err, "compiling contract")
87                 }
88                 for _, clause := range contract.Clauses {
89                         for _, stmt := range clause.statements {
90                                 switch s := stmt.(type) {
91                                 case *lockStatement:
92                                         valueInfo := ValueInfo{
93                                                 Amount:  s.lockedAmount.String(),
94                                                 Asset:   s.lockedAsset.String(),
95                                                 Program: s.program.String(),
96                                         }
97
98                                         clause.Values = append(clause.Values, valueInfo)
99                                 case *unlockStatement:
100                                         valueInfo := ValueInfo{
101                                                 Amount: contract.Value.Amount,
102                                                 Asset:  contract.Value.Asset,
103                                         }
104                                         clause.Values = append(clause.Values, valueInfo)
105                                 }
106                         }
107                 }
108         }
109
110         return contracts, nil
111 }
112
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))
116         }
117
118         // typecheck args against param types
119         for i, param := range params {
120                 arg := args[i]
121                 switch param.Type {
122                 case amountType, intType:
123                         if arg.I == nil {
124                                 return nil, fmt.Errorf("type mismatch in arg %d (want integer)", i)
125                         }
126                 case assetType, hashType, progType, pubkeyType, sigType, strType:
127                         if arg.S == nil {
128                                 return nil, fmt.Errorf("type mismatch in arg %d (want string)", i)
129                         }
130                 case boolType:
131                         if arg.B == nil {
132                                 return nil, fmt.Errorf("type mismatch in arg %d (want boolean)", i)
133                         }
134                 }
135         }
136
137         b := vmutil.NewBuilder()
138
139         for i := len(args) - 1; i >= 0; i-- {
140                 a := args[i]
141                 switch {
142                 case a.B != nil:
143                         var n int64
144                         if *a.B {
145                                 n = 1
146                         }
147                         b.AddInt64(n)
148                 case a.I != nil:
149                         b.AddInt64(*a.I)
150                 case a.S != nil:
151                         b.AddData(*a.S)
152                 }
153         }
154
155         if recursive {
156                 // <argN> <argN-1> ... <arg1> <body> DEPTH OVER 0 CHECKPREDICATE
157                 b.AddData(body)
158                 b.AddOp(vm.OP_DEPTH).AddOp(vm.OP_OVER)
159         } else {
160                 // <argN> <argN-1> ... <arg1> DEPTH <body> 0 CHECKPREDICATE
161                 b.AddOp(vm.OP_DEPTH)
162                 b.AddData(body)
163         }
164         b.AddInt64(0)
165         b.AddOp(vm.OP_CHECKPREDICATE)
166         return b.Build()
167 }
168
169 func compileContract(contract *Contract, globalEnv *environ) error {
170         var err error
171
172         if len(contract.Clauses) == 0 {
173                 return fmt.Errorf("empty contract")
174         }
175         env := newEnviron(globalEnv)
176         for _, p := range contract.Params {
177                 err = env.add(p.Name, p.Type, roleContractParam)
178                 if err != nil {
179                         return err
180                 }
181         }
182
183         // value is spilt with valueAmount and valueAsset
184         if err = env.add(contract.Value.Amount, amountType, roleContractValue); err != nil {
185                 return err
186         }
187         if err = env.add(contract.Value.Asset, assetType, roleContractValue); err != nil {
188                 return err
189         }
190
191         for _, c := range contract.Clauses {
192                 err = env.add(c.Name, nilType, roleClause)
193                 if err != nil {
194                         return err
195                 }
196         }
197
198         err = prohibitSigParams(contract)
199         if err != nil {
200                 return err
201         }
202         err = requireAllParamsUsedInClauses(contract.Params, contract.Clauses)
203         if err != nil {
204                 return err
205         }
206
207         var stk stack
208
209         if len(contract.Clauses) > 1 {
210                 stk = stk.add("<clause selector>")
211         }
212
213         for i := len(contract.Params) - 1; i >= 0; i-- {
214                 p := contract.Params[i]
215                 stk = stk.add(p.Name)
216         }
217
218         if contract.Recursive {
219                 stk = stk.add(contract.Name)
220         }
221
222         b := &builder{}
223
224         if len(contract.Clauses) == 1 {
225                 err = compileClause(b, stk, contract, env, contract.Clauses[0])
226                 if err != nil {
227                         return err
228                 }
229         } else {
230                 if len(contract.Params) > 0 {
231                         // A clause selector is at the bottom of the stack. Roll it to the
232                         // top.
233                         n := len(contract.Params)
234                         if contract.Recursive {
235                                 n++
236                         }
237                         stk = b.addRoll(stk, n) // stack: [<clause params> <contract params> [<maybe contract body>] <clause selector>]
238                 }
239
240                 var stk2 stack
241
242                 // clauses 2..N-1
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
249                 }
250
251                 // clause 1
252                 stk = b.addJumpIf(stk, contract.Clauses[1].Name) // consumes the clause selector
253
254                 // no jump needed for clause 0
255
256                 for i, clause := range contract.Clauses {
257                         if i > 1 {
258                                 // Clauses 0 and 1 have no clause selector on top of the
259                                 // stack. Clauses 2 and later do.
260                                 stk = stk2
261                         }
262
263                         b.addJumpTarget(stk, clause.Name)
264
265                         if i > 1 {
266                                 stk = b.addDrop(stk)
267                         }
268
269                         err = compileClause(b, stk, contract, env, clause)
270                         if err != nil {
271                                 return errors.Wrapf(err, "compiling clause \"%s\"", clause.Name)
272                         }
273                         b.forgetPendingVerify()
274                         if i < len(contract.Clauses)-1 {
275                                 b.addJump(stk, "_end")
276                         }
277                 }
278                 b.addJumpTarget(stk, "_end")
279         }
280
281         opcodes := optimize(b.opcodes())
282         prog, err := vm.Assemble(opcodes)
283         if err != nil {
284                 return err
285         }
286
287         contract.Body = prog
288         contract.Opcodes = opcodes
289
290         contract.Steps = b.steps()
291
292         return nil
293 }
294
295 func compileClause(b *builder, contractStk stack, contract *Contract, env *environ, clause *Clause) error {
296         var err error
297
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)
302                 if err != nil {
303                         return err
304                 }
305         }
306         assignIndexes(clause)
307
308         var stk stack
309         for _, p := range clause.Params {
310                 // NOTE: the order of clause params is not reversed, unlike
311                 // contract params (and also unlike the arguments to Equity
312                 // function-calls).
313                 stk = stk.add(p.Name)
314         }
315         stk = stk.addFromStack(contractStk)
316
317         // a count of the number of times each variable is referenced
318         counts := make(map[string]int)
319         for _, s := range clause.statements {
320                 s.countVarRefs(counts)
321         }
322
323         for _, s := range clause.statements {
324                 switch stmt := s.(type) {
325                 case *defineStatement:
326                         // variable
327                         stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
328                         if err != nil {
329                                 return errors.Wrapf(err, "in define statement in clause \"%s\"", clause.Name)
330                         }
331
332                         // check variable type
333                         if stmt.expr.typ(env) != stmt.varName.Type {
334                                 return fmt.Errorf("expression in define statement in clause \"%s\" has type \"%s\", must be \"%s\"",
335                                         clause.Name, stmt.expr.typ(env), stmt.varName.Type)
336                         }
337
338                         // modify stack name
339                         stk.str = stmt.varName.Name
340
341                         // add environ for define variable
342                         if err = env.add(stmt.varName.Name, stmt.varName.Type, roleClauseVariable); err != nil {
343                                 return err
344                         }
345
346                 case *verifyStatement:
347                         stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr)
348                         if err != nil {
349                                 return errors.Wrapf(err, "in verify statement in clause \"%s\"", clause.Name)
350                         }
351                         stk = b.addVerify(stk)
352
353                         // special-case reporting of certain function calls
354                         if c, ok := stmt.expr.(*callExpr); ok && len(c.args) == 1 {
355                                 if b := referencedBuiltin(c.fn); b != nil {
356                                         switch b.name {
357                                         case "below":
358                                                 clause.BlockHeight = append(clause.BlockHeight, c.args[0].String())
359                                         case "above":
360                                                 clause.BlockHeight = append(clause.BlockHeight, c.args[0].String())
361                                         }
362                                 }
363                         }
364
365                 case *lockStatement:
366                         // index
367                         stk = b.addInt64(stk, stmt.index)
368
369                         // TODO: permit more complex expressions for locked,
370                         // like "lock x+y with foo" (?)
371
372                         if stmt.lockedAmount.String() == contract.Value.Amount && stmt.lockedAsset.String() == contract.Value.Asset {
373                                 stk = b.addAmount(stk, contract.Value.Amount)
374                                 stk = b.addAsset(stk, contract.Value.Asset)
375                         } else {
376                                 if strings.Contains(stmt.lockedAmount.String(), contract.Value.Amount) {
377                                         stk = b.addAmount(stk, contract.Value.Amount)
378                                 }
379
380                                 if strings.Contains(stmt.lockedAsset.String(), contract.Value.Asset) {
381                                         stk = b.addAsset(stk, contract.Value.Asset)
382                                 }
383
384                                 // amount
385                                 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAmount)
386                                 if err != nil {
387                                         return errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
388                                 }
389
390                                 // asset
391                                 stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAsset)
392                                 if err != nil {
393                                         return errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
394                                 }
395                         }
396
397                         // version
398                         stk = b.addInt64(stk, 1)
399
400                         // prog
401                         stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.program)
402                         if err != nil {
403                                 return errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
404                         }
405
406                         stk = b.addCheckOutput(stk, fmt.Sprintf("checkOutput(%s, %s, %s)",
407                                 stmt.lockedAmount.String(), stmt.lockedAsset.String(), stmt.program))
408                         stk = b.addVerify(stk)
409
410                 case *unlockStatement:
411                         if len(clause.statements) == 1 {
412                                 // This is the only statement in the clause, make sure TRUE is
413                                 // on the stack.
414                                 stk = b.addBoolean(stk, true)
415                         }
416                 }
417         }
418
419         err = requireAllValuesDisposedOnce(contract, clause)
420         if err != nil {
421                 return err
422         }
423         err = typeCheckClause(contract, clause, env)
424         if err != nil {
425                 return err
426         }
427         err = requireAllParamsUsedInClause(clause.Params, clause)
428         if err != nil {
429                 return err
430         }
431
432         return nil
433 }
434
435 func compileExpr(b *builder, stk stack, contract *Contract, clause *Clause, env *environ, counts map[string]int, expr expression) (stack, error) {
436         var err error
437
438         switch e := expr.(type) {
439         case *binaryExpr:
440                 // Do typechecking after compiling subexpressions (because other
441                 // compilation errors are more interesting than type mismatch
442                 // errors).
443
444                 stk, err = compileExpr(b, stk, contract, clause, env, counts, e.left)
445                 if err != nil {
446                         return stk, errors.Wrapf(err, "in left operand of \"%s\" expression", e.op.op)
447                 }
448                 stk, err = compileExpr(b, stk, contract, clause, env, counts, e.right)
449                 if err != nil {
450                         return stk, errors.Wrapf(err, "in right operand of \"%s\" expression", e.op.op)
451                 }
452
453                 lType := e.left.typ(env)
454                 if e.op.left != "" && !(lType == e.op.left || lType == amountType) {
455                         return stk, fmt.Errorf("in \"%s\", left operand has type \"%s\", must be \"%s\"", e, lType, e.op.left)
456                 }
457
458                 rType := e.right.typ(env)
459                 if e.op.right != "" && !(rType == e.op.right || rType == amountType) {
460                         return stk, fmt.Errorf("in \"%s\", right operand has type \"%s\", must be \"%s\"", e, rType, e.op.right)
461                 }
462
463                 switch e.op.op {
464                 case "==", "!=":
465                         if lType != rType {
466                                 // Maybe one is Hash and the other is (more-specific-Hash subtype).
467                                 // TODO(bobg): generalize this mechanism
468                                 if lType == hashType && isHashSubtype(rType) {
469                                         propagateType(contract, clause, env, rType, e.left)
470                                 } else if rType == hashType && isHashSubtype(lType) {
471                                         propagateType(contract, clause, env, lType, e.right)
472                                 } else {
473                                         return stk, fmt.Errorf("type mismatch in \"%s\": left operand has type \"%s\", right operand has type \"%s\"", e, lType, rType)
474                                 }
475                         }
476                         if lType == "Boolean" {
477                                 return stk, fmt.Errorf("in \"%s\": using \"%s\" on Boolean values not allowed", e, e.op.op)
478                         }
479                 }
480
481                 stk = b.addOps(stk.dropN(2), e.op.opcodes, e.String())
482
483         case *unaryExpr:
484                 // Do typechecking after compiling subexpression (because other
485                 // compilation errors are more interesting than type mismatch
486                 // errors).
487
488                 var err error
489                 stk, err = compileExpr(b, stk, contract, clause, env, counts, e.expr)
490                 if err != nil {
491                         return stk, errors.Wrapf(err, "in \"%s\" expression", e.op.op)
492                 }
493
494                 if e.op.operand != "" && e.expr.typ(env) != e.op.operand {
495                         return stk, fmt.Errorf("in \"%s\", operand has type \"%s\", must be \"%s\"", e, e.expr.typ(env), e.op.operand)
496                 }
497                 b.addOps(stk.drop(), e.op.opcodes, e.String())
498
499         case *callExpr:
500                 bi := referencedBuiltin(e.fn)
501                 if bi == nil {
502                         if v, ok := e.fn.(varRef); ok {
503                                 if entry := env.lookup(string(v)); entry != nil && entry.t == contractType {
504                                         clause.Contracts = append(clause.Contracts, entry.c.Name)
505
506                                         partialName := fmt.Sprintf("%s(...)", v)
507                                         stk = b.addData(stk, nil)
508
509                                         if len(e.args) != len(entry.c.Params) {
510                                                 return stk, fmt.Errorf("contract \"%s\" expects %d argument(s), got %d", entry.c.Name, len(entry.c.Params), len(e.args))
511                                         }
512
513                                         for i := len(e.args) - 1; i >= 0; i-- {
514                                                 arg := e.args[i]
515                                                 if entry.c.Params[i].Type != "" && arg.typ(env) != entry.c.Params[i].Type {
516                                                         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)
517                                                 }
518                                                 stk, err = compileExpr(b, stk, contract, clause, env, counts, arg)
519                                                 if err != nil {
520                                                         return stk, err
521                                                 }
522                                                 stk = b.addCatPushdata(stk, partialName)
523                                         }
524
525                                         switch {
526                                         case entry.c == contract:
527                                                 // Recursive call - cannot use entry.c.Body
528                                                 // <argN> <argN-1> ... <arg1> <body> DEPTH OVER 0 CHECKPREDICATE
529                                                 stk, err = compileRef(b, stk, counts, varRef(contract.Name))
530                                                 if err != nil {
531                                                         return stk, errors.Wrap(err, "compiling contract call")
532                                                 }
533                                                 stk = b.addCatPushdata(stk, partialName)
534                                                 stk = b.addData(stk, []byte{byte(vm.OP_DEPTH), byte(vm.OP_OVER)})
535                                                 stk = b.addCat(stk, partialName)
536
537                                         case entry.c.Recursive:
538                                                 // Non-recursive call to a (different) recursive contract
539                                                 // <argN> <argN-1> ... <arg1> <body> DEPTH OVER 0 CHECKPREDICATE
540                                                 if len(entry.c.Body) == 0 {
541                                                         // TODO(bobg): sort input contracts topologically to permit forward calling
542                                                         return stk, fmt.Errorf("contract \"%s\" not defined", entry.c.Name)
543                                                 }
544                                                 stk = b.addData(stk, entry.c.Body)
545                                                 stk = b.addCatPushdata(stk, partialName)
546                                                 stk = b.addData(stk, []byte{byte(vm.OP_DEPTH), byte(vm.OP_OVER)})
547                                                 stk = b.addCat(stk, partialName)
548
549                                         default:
550                                                 // Non-recursive call to non-recursive contract
551                                                 // <argN> <argN-1> ... <arg1> DEPTH <body> 0 CHECKPREDICATE
552                                                 stk = b.addData(stk, []byte{byte(vm.OP_DEPTH)})
553                                                 stk = b.addCat(stk, partialName)
554                                                 if len(entry.c.Body) == 0 {
555                                                         // TODO(bobg): sort input contracts topologically to permit forward calling
556                                                         return stk, fmt.Errorf("contract \"%s\" not defined", entry.c.Name)
557                                                 }
558                                                 stk = b.addData(stk, entry.c.Body)
559                                                 stk = b.addCatPushdata(stk, partialName)
560                                         }
561                                         stk = b.addData(stk, vm.Int64Bytes(0))
562                                         stk = b.addCatPushdata(stk, partialName)
563                                         stk = b.addData(stk, []byte{byte(vm.OP_CHECKPREDICATE)})
564                                         stk = b.addCat(stk, e.String())
565
566                                         return stk, nil
567                                 }
568                         }
569                         return stk, fmt.Errorf("unknown function \"%s\"", e.fn)
570                 }
571
572                 if len(e.args) != len(bi.args) {
573                         return stk, fmt.Errorf("wrong number of args for \"%s\": have %d, want %d", bi.name, len(e.args), len(bi.args))
574                 }
575
576                 // WARNING WARNING WOOP WOOP
577                 // special-case hack
578                 // WARNING WARNING WOOP WOOP
579                 if bi.name == "checkTxMultiSig" {
580                         if _, ok := e.args[0].(listExpr); !ok {
581                                 return stk, fmt.Errorf("checkTxMultiSig expects list literals, got %T for argument 0", e.args[0])
582                         }
583                         if _, ok := e.args[1].(listExpr); !ok {
584                                 return stk, fmt.Errorf("checkTxMultiSig expects list literals, got %T for argument 1", e.args[1])
585                         }
586
587                         var k1, k2 int
588
589                         stk, k1, err = compileArg(b, stk, contract, clause, env, counts, e.args[1])
590                         if err != nil {
591                                 return stk, err
592                         }
593
594                         // stack: [... sigM ... sig1 M]
595
596                         var altEntry string
597                         stk, altEntry = b.addToAltStack(stk) // stack: [... sigM ... sig1]
598                         stk = b.addTxSigHash(stk)            // stack: [... sigM ... sig1 txsighash]
599
600                         stk, k2, err = compileArg(b, stk, contract, clause, env, counts, e.args[0])
601                         if err != nil {
602                                 return stk, err
603                         }
604
605                         // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 N]
606
607                         stk = b.addFromAltStack(stk, altEntry) // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 N M]
608                         stk = b.addSwap(stk)                   // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 M N]
609                         stk = b.addCheckMultisig(stk, k1+k2, e.String())
610
611                         return stk, nil
612                 }
613
614                 var k int
615
616                 for i := len(e.args) - 1; i >= 0; i-- {
617                         a := e.args[i]
618                         var k2 int
619                         var err error
620                         stk, k2, err = compileArg(b, stk, contract, clause, env, counts, a)
621                         if err != nil {
622                                 return stk, errors.Wrapf(err, "compiling argument %d in call expression", i)
623                         }
624                         k += k2
625                 }
626
627                 // Do typechecking after compiling subexpressions (because other
628                 // compilation errors are more interesting than type mismatch
629                 // errors).
630                 for i, actual := range e.args {
631                         if bi.args[i] != "" && actual.typ(env) != bi.args[i] {
632                                 return stk, fmt.Errorf("argument %d to \"%s\" has type \"%s\", must be \"%s\"", i, bi.name, actual.typ(env), bi.args[i])
633                         }
634                 }
635
636                 stk = b.addOps(stk.dropN(k), bi.opcodes, e.String())
637
638                 // special-case reporting
639                 switch bi.name {
640                 case "sha3", "sha256":
641                         clause.HashCalls = append(clause.HashCalls, HashCall{bi.name, e.args[0].String(), string(e.args[0].typ(env))})
642                 }
643
644         case varRef:
645                 return compileRef(b, stk, counts, e)
646
647         case integerLiteral:
648                 stk = b.addInt64(stk, int64(e))
649
650         case bytesLiteral:
651                 stk = b.addData(stk, []byte(e))
652
653         case booleanLiteral:
654                 stk = b.addBoolean(stk, bool(e))
655
656         case listExpr:
657                 // Lists are excluded here because they disobey the invariant of
658                 // this function: namely, that it increases the stack size by
659                 // exactly one. (A list pushes its items and its length on the
660                 // stack.) But they're OK as function-call arguments because the
661                 // function (presumably) consumes all the stack items added.
662                 return stk, fmt.Errorf("encountered list outside of function-call context")
663         }
664         return stk, nil
665 }
666
667 func compileArg(b *builder, stk stack, contract *Contract, clause *Clause, env *environ, counts map[string]int, expr expression) (stack, int, error) {
668         var n int
669         if list, ok := expr.(listExpr); ok {
670                 for i := 0; i < len(list); i++ {
671                         elt := list[len(list)-i-1]
672                         var err error
673                         stk, err = compileExpr(b, stk, contract, clause, env, counts, elt)
674                         if err != nil {
675                                 return stk, 0, err
676                         }
677                         n++
678                 }
679                 stk = b.addInt64(stk, int64(len(list)))
680                 n++
681                 return stk, n, nil
682         }
683         var err error
684         stk, err = compileExpr(b, stk, contract, clause, env, counts, expr)
685         return stk, 1, err
686 }
687
688 func compileRef(b *builder, stk stack, counts map[string]int, ref varRef) (stack, error) {
689         depth := stk.find(string(ref))
690         if depth < 0 {
691                 return stk, fmt.Errorf("undefined reference: \"%s\"", ref)
692         }
693
694         var isFinal bool
695         if count, ok := counts[string(ref)]; ok && count > 0 {
696                 count--
697                 counts[string(ref)] = count
698                 isFinal = count == 0
699         }
700
701         switch depth {
702         case 0:
703                 if !isFinal {
704                         stk = b.addDup(stk)
705                 }
706         case 1:
707                 if isFinal {
708                         stk = b.addSwap(stk)
709                 } else {
710                         stk = b.addOver(stk)
711                 }
712         default:
713                 if isFinal {
714                         stk = b.addRoll(stk, depth)
715                 } else {
716                         stk = b.addPick(stk, depth)
717                 }
718         }
719         return stk, nil
720 }
721
722 func (a *ContractArg) UnmarshalJSON(b []byte) error {
723         var m map[string]json.RawMessage
724         err := json.Unmarshal(b, &m)
725         if err != nil {
726                 return err
727         }
728         if r, ok := m["boolean"]; ok {
729                 var bval bool
730                 err = json.Unmarshal(r, &bval)
731                 if err != nil {
732                         return err
733                 }
734                 a.B = &bval
735                 return nil
736         }
737         if r, ok := m["integer"]; ok {
738                 var ival int64
739                 err = json.Unmarshal(r, &ival)
740                 if err != nil {
741                         return err
742                 }
743                 a.I = &ival
744                 return nil
745         }
746         r, ok := m["string"]
747         if !ok {
748                 return fmt.Errorf("contract arg must define one of boolean, integer, string")
749         }
750         var sval chainjson.HexBytes
751         err = json.Unmarshal(r, &sval)
752         if err != nil {
753                 return err
754         }
755         a.S = &sval
756         return nil
757 }