OSDN Git Service

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