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 calClauseValues(contract *Contract, env *environ, stmt statement, conditions map[string]Condition, condValues map[string][]ValueInfo, index *int) (valueInfo *ValueInfo) {
43 switch s := stmt.(type) {
46 strIndex := fmt.Sprintf("%d", *index)
48 conditionCounts := make(map[string]int)
49 s.condition.countVarRefs(conditionCounts)
52 for v := range conditionCounts {
53 if entry := env.lookup(v); entry != nil && (entry.r == roleContractParam || entry.r == roleContractValue || entry.r == roleClauseParam || entry.r == roleClauseVariable) {
54 params = append(params, &Param{Name: v, Type: entry.t})
58 condition := Condition{Source: s.condition.String(), Params: params}
59 conditions["condition_"+strIndex] = condition
61 trueValues := []ValueInfo{}
62 for _, trueStmt := range s.body.trueBody {
63 trueValue := calClauseValues(contract, env, trueStmt, conditions, condValues, index)
65 trueValues = append(trueValues, *trueValue)
68 condValues["truebody_"+strIndex] = trueValues
70 if len(s.body.falseBody) != 0 {
71 falseValues := []ValueInfo{}
72 for _, falseStmt := range s.body.falseBody {
73 falseValue := calClauseValues(contract, env, falseStmt, conditions, condValues, index)
74 if falseValue != nil {
75 falseValues = append(falseValues, *falseValue)
78 condValues["falsebody_"+strIndex] = falseValues
82 valueInfo = &ValueInfo{
83 Amount: s.lockedAmount.String(),
84 Asset: s.lockedAsset.String(),
85 Program: s.program.String(),
88 lockCounts := make(map[string]int)
89 s.lockedAmount.countVarRefs(lockCounts)
90 if _, ok := lockCounts[s.lockedAmount.String()]; !ok {
92 for v := range lockCounts {
93 if entry := env.lookup(v); entry != nil && (entry.r == roleContractParam || entry.r == roleContractValue || entry.r == roleClauseParam || entry.r == roleClauseVariable) {
94 params = append(params, &Param{Name: v, Type: entry.t})
97 valueInfo.Params = params
100 case *unlockStatement:
101 valueInfo = &ValueInfo{
102 Amount: contract.Value.Amount,
103 Asset: contract.Value.Asset,
110 func prohibitSigParams(contract *Contract) error {
111 for _, p := range contract.Params {
112 if p.Type == sigType {
113 return fmt.Errorf("contract parameter \"%s\" has type Signature, but contract parameters cannot have type Signature", p.Name)
119 func requireAllParamsUsedInClauses(params []*Param, clauses []*Clause) error {
120 for _, p := range params {
122 for _, c := range clauses {
123 err := requireAllParamsUsedInClause([]*Param{p}, c)
131 return fmt.Errorf("parameter \"%s\" is unused", p.Name)
137 func requireAllParamsUsedInClause(params []*Param, clause *Clause) error {
138 for _, p := range params {
140 for _, stmt := range clause.statements {
141 if used = checkParamUsedInStatement(p, stmt); used {
147 return fmt.Errorf("parameter \"%s\" is unused in clause \"%s\"", p.Name, clause.Name)
153 func checkParamUsedInStatement(param *Param, stmt statement) (used bool) {
154 switch s := stmt.(type) {
156 if used = references(s.condition, param.Name); used {
160 for _, st := range s.body.trueBody {
161 if used = checkParamUsedInStatement(param, st); used {
167 for _, st := range s.body.falseBody {
168 if used = checkParamUsedInStatement(param, st); used {
174 case *defineStatement:
175 used = references(s.expr, param.Name)
176 case *assignStatement:
177 used = references(s.expr, param.Name)
178 case *verifyStatement:
179 used = references(s.expr, param.Name)
181 used = references(s.lockedAmount, param.Name) || references(s.lockedAsset, param.Name) || references(s.program, param.Name)
182 case *unlockStatement:
183 used = references(s.unlockedAmount, param.Name) || references(s.unlockedAsset, param.Name)
189 func references(expr expression, name string) bool {
190 switch e := expr.(type) {
192 return references(e.left, name) || references(e.right, name)
194 return references(e.expr, name)
196 if references(e.fn, name) {
199 for _, a := range e.args {
200 if references(a, name) {
206 return string(e) == name
208 for _, elt := range []expression(e) {
209 if references(elt, name) {
218 func referencedBuiltin(expr expression) *builtin {
219 if v, ok := expr.(varRef); ok {
220 for _, b := range builtins {
221 if string(v) == b.name {
229 func countsVarRef(stat statement, counts map[string]int) map[string]int {
230 if stmt, ok := stat.(*defineStatement); ok && stmt.expr == nil {
234 if _, ok := stat.(*unlockStatement); ok {
238 stat.countVarRefs(counts)
239 if stmt, ok := stat.(*ifStatement); ok {
240 for _, trueStmt := range stmt.body.trueBody {
241 counts = countsVarRef(trueStmt, counts)
244 for _, falseStmt := range stmt.body.falseBody {
245 counts = countsVarRef(falseStmt, counts)
252 func assignIndexes(clause *Clause) error {
254 for i, stmt := range clause.statements {
255 if nextIndex = assignStatIndexes(stmt, nextIndex, i != len(clause.statements)-1); nextIndex < 0 {
256 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)
263 func assignStatIndexes(stat statement, nextIndex int64, nonFinalFlag bool) int64 {
264 switch stmt := stat.(type) {
266 trueIndex := nextIndex
267 falseIndex := nextIndex
268 for _, trueStmt := range stmt.body.trueBody {
269 trueIndex = assignStatIndexes(trueStmt, trueIndex, nonFinalFlag)
272 for _, falseStmt := range stmt.body.falseBody {
273 falseIndex = assignStatIndexes(falseStmt, falseIndex, nonFinalFlag)
276 if trueIndex != falseIndex && nonFinalFlag {
278 } else if trueIndex == falseIndex {
279 nextIndex = trueIndex
283 stmt.index = nextIndex
286 case *unlockStatement:
293 func typeCheckClause(contract *Contract, clause *Clause, env *environ) error {
294 for _, s := range clause.statements {
295 if err := typeCheckStatement(s, contract.Value, clause.Name, env); err != nil {
302 func typeCheckStatement(stat statement, contractValue ValueInfo, clauseName string, env *environ) error {
303 switch stmt := stat.(type) {
305 for _, trueStmt := range stmt.body.trueBody {
306 if err := typeCheckStatement(trueStmt, contractValue, clauseName, env); err != nil {
311 for _, falseStmt := range stmt.body.falseBody {
312 if err := typeCheckStatement(falseStmt, contractValue, clauseName, env); err != nil {
317 case *defineStatement:
318 if stmt.expr != nil && stmt.expr.typ(env) != stmt.variable.Type && !(stmt.variable.Type == hashType && isHashSubtype(stmt.expr.typ(env))) {
319 return fmt.Errorf("expression in define statement in clause \"%s\" has type \"%s\", must be \"%s\"",
320 clauseName, stmt.expr.typ(env), stmt.variable.Type)
323 case *assignStatement:
324 if stmt.expr.typ(env) != stmt.variable.Type && !(stmt.variable.Type == hashType && isHashSubtype(stmt.expr.typ(env))) {
325 return fmt.Errorf("expression in assign statement in clause \"%s\" has type \"%s\", must be \"%s\"",
326 clauseName, stmt.expr.typ(env), stmt.variable.Type)
329 case *verifyStatement:
330 if t := stmt.expr.typ(env); t != boolType {
331 return fmt.Errorf("expression in verify statement in clause \"%s\" has type \"%s\", must be Boolean", clauseName, t)
335 if t := stmt.lockedAmount.typ(env); !(t == intType || t == amountType) {
336 return fmt.Errorf("lockedAmount expression \"%s\" in lock statement in clause \"%s\" has type \"%s\", must be Integer", stmt.lockedAmount, clauseName, t)
338 if t := stmt.lockedAsset.typ(env); t != assetType {
339 return fmt.Errorf("lockedAsset expression \"%s\" in lock statement in clause \"%s\" has type \"%s\", must be Asset", stmt.lockedAsset, clauseName, t)
341 if t := stmt.program.typ(env); t != progType {
342 return fmt.Errorf("program in lock statement in clause \"%s\" has type \"%s\", must be Program", clauseName, t)
345 case *unlockStatement:
346 if t := stmt.unlockedAmount.typ(env); !(t == intType || t == amountType) {
347 return fmt.Errorf("unlockedAmount expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Integer", stmt.unlockedAmount, clauseName, t)
349 if t := stmt.unlockedAsset.typ(env); t != assetType {
350 return fmt.Errorf("unlockedAsset expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Asset", stmt.unlockedAsset, clauseName, t)
352 if stmt.unlockedAmount.String() != contractValue.Amount || stmt.unlockedAsset.String() != contractValue.Asset {
353 return fmt.Errorf("amount \"%s\" of asset \"%s\" expression in unlock statement of clause \"%s\" must be the contract valueAmount \"%s\" of valueAsset \"%s\"",
354 stmt.unlockedAmount.String(), stmt.unlockedAsset.String(), clauseName, contractValue.Amount, contractValue.Asset)