// Values is the list of values unlocked or relocked in this clause.
Values []ValueInfo `json:"values"`
+ // Conditions is the list of condition for if-else statements which body contains
+ // the lock or unlock statement in this clause.
+ Conditions map[string]Condition `json:"conditions"`
+
+ // CondValues is the map of values unlocked or relocked in this clause's
+ // if-else statements which body contains the lock or unlock statement.
+ CondValues map[string][]ValueInfo `json:"cond_values"`
+
// Contracts is the list of contracts called by this clause.
Contracts []string `json:"contracts,omitempty"`
}
+// ValueInfo describes how a blockchain value is used in a contract clause.
+type ValueInfo struct {
+ // Name is the clause's name for this value.
+ Name string `json:"name"`
+
+ // Program is the program expression used to the lock the value, if
+ // the value is locked with "lock." If it's unlocked with "unlock"
+ // instead, this is empty.
+ Program string `json:"program,omitempty"`
+
+ // Asset is the expression describing the asset type the value must
+ // have, as it appears in a clause's "requires" section. If this is
+ // the contract value instead, this is empty.
+ Asset string `json:"asset,omitempty"`
+
+ // Amount is the expression describing the amount the value must
+ // have, as it appears in a clause's "requires" section. If this is
+ // the contract value instead, this is empty.
+ Amount string `json:"amount,omitempty"`
+
+ // Params is the list of parameters for amount expression. If the value
+ // of amount is a variable, this is empty.
+ Params []*Param `json:"params,omitempty"`
+}
+
// HashCall describes a call to a hash function.
type HashCall struct {
// HashType is "sha3" or "sha256".
ArgType string `json:"arg_type"`
}
-// IfBody describes a if ... else ... struct
-type IfStatmentBody struct {
- // if statements body
- trueBody []statement
+// Condition describes a condition expression.
+type Condition struct {
+ // Source is the string format of condition expression.
+ Source string `json:"source"`
- // else statements body
- falseBody []statement
+ // Params is the list of parameters for condition expression.
+ Params []*Param `json:"params,omitempty"`
+}
+
+// ContractArg is an argument with which to instantiate a contract as
+// a program. Exactly one of B, I, and S should be supplied.
+type ContractArg struct {
+ B *bool `json:"boolean,omitempty"`
+ I *int64 `json:"integer,omitempty"`
+ S *chainjson.HexBytes `json:"string,omitempty"`
}
type statement interface {
s.expr.countVarRefs(counts)
}
+// IfStatmentBody describes the content of if-else structure
+type IfStatmentBody struct {
+ // if body statements
+ trueBody []statement
+
+ // else body statements
+ falseBody []statement
+}
+
type ifStatement struct {
condition expression
body *IfStatmentBody
return string(v)
}
-func (e varRef) typ(env *environ) typeDesc {
- if entry := env.lookup(string(e)); entry != nil {
+func (v varRef) typ(env *environ) typeDesc {
+ if entry := env.lookup(string(v)); entry != nil {
return entry.t
}
return nilType
}
-func (e varRef) countVarRefs(counts map[string]int) {
- counts[string(e)]++
+func (v varRef) countVarRefs(counts map[string]int) {
+ counts[string(v)]++
}
type bytesLiteral []byte
return false
}
+func calClauseValues(contract *Contract, env *environ, stmt statement, conditions map[string]Condition, condValues map[string][]ValueInfo, index *int) (valueInfo *ValueInfo) {
+ switch s := stmt.(type) {
+ case *ifStatement:
+ *index++
+ strIndex := fmt.Sprintf("%d", *index)
+
+ conditionCounts := make(map[string]int)
+ s.condition.countVarRefs(conditionCounts)
+
+ params := []*Param{}
+ for v := range conditionCounts {
+ if entry := env.lookup(v); entry != nil && (entry.r == roleContractParam || entry.r == roleContractValue || entry.r == roleClauseParam || entry.r == roleClauseVariable) {
+ params = append(params, &Param{Name: v, Type: entry.t})
+ }
+ }
+
+ condition := Condition{Source: s.condition.String(), Params: params}
+ conditions["condition_"+strIndex] = condition
+
+ trueValues := []ValueInfo{}
+ for _, trueStmt := range s.body.trueBody {
+ trueValue := calClauseValues(contract, env, trueStmt, conditions, condValues, index)
+ if trueValue != nil {
+ trueValues = append(trueValues, *trueValue)
+ }
+ }
+ condValues["truebody_"+strIndex] = trueValues
+
+ if len(s.body.falseBody) != 0 {
+ falseValues := []ValueInfo{}
+ for _, falseStmt := range s.body.falseBody {
+ falseValue := calClauseValues(contract, env, falseStmt, conditions, condValues, index)
+ if falseValue != nil {
+ falseValues = append(falseValues, *falseValue)
+ }
+ }
+ condValues["falsebody_"+strIndex] = falseValues
+ }
+
+ case *lockStatement:
+ valueInfo = &ValueInfo{
+ Amount: s.lockedAmount.String(),
+ Asset: s.lockedAsset.String(),
+ Program: s.program.String(),
+ }
+
+ lockCounts := make(map[string]int)
+ s.lockedAmount.countVarRefs(lockCounts)
+ if _, ok := lockCounts[s.lockedAmount.String()]; !ok {
+ params := []*Param{}
+ for v := range lockCounts {
+ if entry := env.lookup(v); entry != nil && (entry.r == roleContractParam || entry.r == roleContractValue || entry.r == roleClauseParam || entry.r == roleClauseVariable) {
+ params = append(params, &Param{Name: v, Type: entry.t})
+ }
+ }
+ valueInfo.Params = params
+ }
+
+ case *unlockStatement:
+ valueInfo = &ValueInfo{
+ Amount: contract.Value.Amount,
+ Asset: contract.Value.Asset,
+ }
+ }
+
+ return valueInfo
+}
+
func prohibitSigParams(contract *Contract) error {
for _, p := range contract.Params {
if p.Type == sigType {
"github.com/bytom/protocol/vm/vmutil"
)
-// ValueInfo describes how a blockchain value is used in a contract
-// clause.
-type ValueInfo struct {
- // Name is the clause's name for this value.
- Name string `json:"name"`
-
- // Program is the program expression used to the lock the value, if
- // the value is locked with "lock." If it's unlocked with "unlock"
- // instead, this is empty.
- Program string `json:"program,omitempty"`
-
- // Asset is the expression describing the asset type the value must
- // have, as it appears in a clause's "requires" section. If this is
- // the contract value instead, this is empty.
- Asset string `json:"asset,omitempty"`
-
- // Amount is the expression describing the amount the value must
- // have, as it appears in a clause's "requires" section. If this is
- // the contract value instead, this is empty.
- Amount string `json:"amount,omitempty"`
-}
-
-// ContractArg is an argument with which to instantiate a contract as
-// a program. Exactly one of B, I, and S should be supplied.
-type ContractArg struct {
- B *bool `json:"boolean,omitempty"`
- I *int64 `json:"integer,omitempty"`
- S *chainjson.HexBytes `json:"string,omitempty"`
-}
-
// Compile parses a sequence of Equity contracts from the supplied reader
// and produces Contract objects containing the compiled bytecode and
// other analysis. If argMap is non-nil, it maps contract names to
if err != nil {
return nil, errors.Wrap(err, "compiling contract")
}
- for _, clause := range contract.Clauses {
- for _, stmt := range clause.statements {
- switch s := stmt.(type) {
- case *lockStatement:
- valueInfo := ValueInfo{
- Amount: s.lockedAmount.String(),
- Asset: s.lockedAsset.String(),
- Program: s.program.String(),
- }
-
- clause.Values = append(clause.Values, valueInfo)
- case *unlockStatement:
- valueInfo := ValueInfo{
- Amount: contract.Value.Amount,
- Asset: contract.Value.Asset,
- }
- clause.Values = append(clause.Values, valueInfo)
- }
- }
- }
}
return contracts, nil
}
}
+ conditions := make(map[string]Condition)
+ condValues := make(map[string][]ValueInfo)
+ index := 0
+ for _, stmt := range clause.statements {
+ valueInfo := calClauseValues(contract, env, stmt, conditions, condValues, &index)
+ if valueInfo != nil {
+ clause.Values = append(clause.Values, *valueInfo)
+ } else {
+ clause.Conditions = conditions
+ clause.CondValues = condValues
+ }
+ }
+
err = typeCheckClause(contract, clause, env)
if err != nil {
return err
roleContractValue
roleClause
roleClauseParam
- roleClauseValue
roleClauseVariable
)
roleContractValue: "contract value",
roleClause: "clause",
roleClauseParam: "clause parameter",
- roleClauseValue: "clause value",
roleClauseVariable: "clause variable",
}