5 func checkRecursive(contract *Contract) bool {
6 for _, clause := range contract.Clauses {
7 for _, stmt := range clause.statements {
8 if result := checkStatRecursive(stmt, contract.Name); result {
16 func checkStatRecursive(stmt statement, contractName string) bool {
17 switch s := stmt.(type) {
19 for _, trueStmt := range s.body.trueBody {
20 if result := checkStatRecursive(trueStmt, contractName); result {
25 for _, falseStmt := range s.body.falseBody {
26 if result := checkStatRecursive(falseStmt, contractName); result {
32 if c, ok := s.program.(*callExpr); ok {
33 if references(c.fn, contractName) {
42 func prohibitSigParams(contract *Contract) error {
43 for _, p := range contract.Params {
44 if p.Type == sigType {
45 return fmt.Errorf("contract parameter \"%s\" has type Signature, but contract parameters cannot have type Signature", p.Name)
51 func requireAllParamsUsedInClauses(params []*Param, clauses []*Clause) error {
52 for _, p := range params {
54 for _, c := range clauses {
55 err := requireAllParamsUsedInClause([]*Param{p}, c)
63 return fmt.Errorf("parameter \"%s\" is unused", p.Name)
69 func requireAllParamsUsedInClause(params []*Param, clause *Clause) error {
70 for _, p := range params {
72 for _, stmt := range clause.statements {
73 if used = checkParamUsedInStatement(p, stmt); used {
79 return fmt.Errorf("parameter \"%s\" is unused in clause \"%s\"", p.Name, clause.Name)
85 func checkParamUsedInStatement(param *Param, stmt statement) (used bool) {
86 switch s := stmt.(type) {
88 if used = references(s.condition, param.Name); used {
92 for _, st := range s.body.trueBody {
93 if used = checkParamUsedInStatement(param, st); used {
99 for _, st := range s.body.falseBody {
100 if used = checkParamUsedInStatement(param, st); used {
106 case *defineStatement:
107 used = references(s.expr, param.Name)
108 case *assignStatement:
109 used = references(s.expr, param.Name)
110 case *verifyStatement:
111 used = references(s.expr, param.Name)
113 used = references(s.lockedAmount, param.Name) || references(s.lockedAsset, param.Name) || references(s.program, param.Name)
114 case *unlockStatement:
115 used = references(s.unlockedAmount, param.Name) || references(s.unlockedAsset, param.Name)
121 func references(expr expression, name string) bool {
122 switch e := expr.(type) {
124 return references(e.left, name) || references(e.right, name)
126 return references(e.expr, name)
128 if references(e.fn, name) {
131 for _, a := range e.args {
132 if references(a, name) {
138 return string(e) == name
140 for _, elt := range []expression(e) {
141 if references(elt, name) {
150 func referencedBuiltin(expr expression) *builtin {
151 if v, ok := expr.(varRef); ok {
152 for _, b := range builtins {
153 if string(v) == b.name {
161 func countsVarRef(stat statement, counts map[string]int) map[string]int {
162 if stmt, ok := stat.(*defineStatement); ok && stmt.expr == nil {
166 if _, ok := stat.(*unlockStatement); ok {
170 stat.countVarRefs(counts)
171 if stmt, ok := stat.(*ifStatement); ok {
172 for _, trueStmt := range stmt.body.trueBody {
173 counts = countsVarRef(trueStmt, counts)
176 for _, falseStmt := range stmt.body.falseBody {
177 counts = countsVarRef(falseStmt, counts)
184 func assignIndexes(clause *Clause) error {
186 for i, stmt := range clause.statements {
187 if nextIndex = assignStatIndexes(stmt, nextIndex, i != len(clause.statements)-1); nextIndex < 0 {
188 return fmt.Errorf("Not support that the number of lock/unlock statement is not equal between ifbody and elsebody when the if-else is not the last statement in clause \"%s\"", clause.Name)
195 func assignStatIndexes(stat statement, nextIndex int64, nonFinalFlag bool) int64 {
196 switch stmt := stat.(type) {
198 trueIndex := nextIndex
199 falseIndex := nextIndex
200 for _, trueStmt := range stmt.body.trueBody {
201 trueIndex = assignStatIndexes(trueStmt, trueIndex, nonFinalFlag)
204 for _, falseStmt := range stmt.body.falseBody {
205 falseIndex = assignStatIndexes(falseStmt, falseIndex, nonFinalFlag)
208 if trueIndex != falseIndex && nonFinalFlag {
210 } else if trueIndex == falseIndex {
211 nextIndex = trueIndex
215 stmt.index = nextIndex
218 case *unlockStatement:
225 func typeCheckClause(contract *Contract, clause *Clause, env *environ) error {
226 for _, s := range clause.statements {
227 if err := typeCheckStatement(s, contract.Value, clause.Name, env); err != nil {
234 func typeCheckStatement(stat statement, contractValue ValueInfo, clauseName string, env *environ) error {
235 switch stmt := stat.(type) {
237 for _, trueStmt := range stmt.body.trueBody {
238 if err := typeCheckStatement(trueStmt, contractValue, clauseName, env); err != nil {
243 for _, falseStmt := range stmt.body.falseBody {
244 if err := typeCheckStatement(falseStmt, contractValue, clauseName, env); err != nil {
249 case *defineStatement:
250 if stmt.expr != nil && stmt.expr.typ(env) != stmt.variable.Type && !isHashSubtype(stmt.expr.typ(env)) {
251 return fmt.Errorf("expression in define statement in clause \"%s\" has type \"%s\", must be \"%s\"",
252 clauseName, stmt.expr.typ(env), stmt.variable.Type)
255 case *assignStatement:
256 if stmt.expr.typ(env) != stmt.variable.Type && !isHashSubtype(stmt.expr.typ(env)) {
257 return fmt.Errorf("expression in assign statement in clause \"%s\" has type \"%s\", must be \"%s\"",
258 clauseName, stmt.expr.typ(env), stmt.variable.Type)
261 case *verifyStatement:
262 if t := stmt.expr.typ(env); t != boolType {
263 return fmt.Errorf("expression in verify statement in clause \"%s\" has type \"%s\", must be Boolean", clauseName, t)
267 if t := stmt.lockedAmount.typ(env); !(t == intType || t == amountType) {
268 return fmt.Errorf("lockedAmount expression \"%s\" in lock statement in clause \"%s\" has type \"%s\", must be Integer", stmt.lockedAmount, clauseName, t)
270 if t := stmt.lockedAsset.typ(env); t != assetType {
271 return fmt.Errorf("lockedAsset expression \"%s\" in lock statement in clause \"%s\" has type \"%s\", must be Asset", stmt.lockedAsset, clauseName, t)
273 if t := stmt.program.typ(env); t != progType {
274 return fmt.Errorf("program in lock statement in clause \"%s\" has type \"%s\", must be Program", clauseName, t)
277 case *unlockStatement:
278 if t := stmt.unlockedAmount.typ(env); !(t == intType || t == amountType) {
279 return fmt.Errorf("unlockedAmount expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Integer", stmt.unlockedAmount, clauseName, t)
281 if t := stmt.unlockedAsset.typ(env); t != assetType {
282 return fmt.Errorf("unlockedAsset expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Asset", stmt.unlockedAsset, clauseName, t)
284 if stmt.unlockedAmount.String() != contractValue.Amount || stmt.unlockedAsset.String() != contractValue.Asset {
285 return fmt.Errorf("amount \"%s\" of asset \"%s\" expression in unlock statement of clause \"%s\" must be the contract valueAmount \"%s\" of valueAsset \"%s\"",
286 stmt.unlockedAmount.String(), stmt.unlockedAsset.String(), clauseName, contractValue.Amount, contractValue.Asset)