OSDN Git Service

add value calculate with lock statement (#2)
authoroysheng <33340252+oysheng@users.noreply.github.com>
Thu, 23 Aug 2018 14:44:50 +0000 (22:44 +0800)
committerPaladz <yzhu101@uottawa.ca>
Thu, 23 Aug 2018 14:44:50 +0000 (22:44 +0800)
* add value calculate with lock statement

* remove redundant require code

* optimise style

* delete redundant function

compiler/ast.go
compiler/builder.go
compiler/checks.go
compiler/compile.go
compiler/parse.go
compiler/types.go

index 8eea5a0..6557c29 100644 (file)
@@ -21,7 +21,7 @@ type Contract struct {
        Clauses []*Clause `json:"clauses"`
 
        // Value is the name of the value locked by the contract.
-       Value string `json:"value"`
+       Value ValueInfo `json:"value"`
 
        // Body is the optimized bytecode of the contract body. This is not
        // a complete program!  Use instantiate to turn this (plus some
@@ -61,10 +61,6 @@ type Clause struct {
        // Params is the list of clause parameters.
        Params []*Param `json:"params,omitempty"`
 
-       // Reqs is the list of requirements (from the clause's "requires"
-       // section).
-       Reqs []*ClauseReq `json:"reqs,omitempty"`
-
        statements []statement
 
        // BlockHeight is the list of expressions passed to greater()/less() in this
@@ -121,24 +117,28 @@ func (s verifyStatement) countVarRefs(counts map[string]int) {
 }
 
 type lockStatement struct {
-       locked  expression
-       program expression
+       lockedAmount expression
+       lockedAsset  expression
+       program      expression
 
        // Added as a decoration, used by CHECKOUTPUT
        index int64
 }
 
 func (s lockStatement) countVarRefs(counts map[string]int) {
-       s.locked.countVarRefs(counts)
+       s.lockedAmount.countVarRefs(counts)
+       s.lockedAsset.countVarRefs(counts)
        s.program.countVarRefs(counts)
 }
 
 type unlockStatement struct {
-       expr expression
+       unlockedAmount expression
+       unlockedAsset  expression
 }
 
 func (s unlockStatement) countVarRefs(counts map[string]int) {
-       s.expr.countVarRefs(counts)
+       s.unlockedAmount.countVarRefs(counts)
+       s.unlockedAsset.countVarRefs(counts)
 }
 
 type expression interface {
index 757f9b6..13b1faf 100644 (file)
@@ -85,12 +85,12 @@ func (b *builder) addData(stk stack, data []byte) stack {
        return b.add(s, stk.add(s))
 }
 
-func (b *builder) addAmount(stk stack) stack {
-       return b.add("AMOUNT", stk.add("<amount>"))
+func (b *builder) addAmount(stk stack, desc string) stack {
+       return b.add("AMOUNT", stk.add(desc))
 }
 
-func (b *builder) addAsset(stk stack) stack {
-       return b.add("ASSET", stk.add("<asset>"))
+func (b *builder) addAsset(stk stack, desc string) stack {
+       return b.add("ASSET", stk.add(desc))
 }
 
 func (b *builder) addCheckOutput(stk stack, desc string) stack {
index d1a1c1a..15879e2 100644 (file)
@@ -26,22 +26,6 @@ func prohibitSigParams(contract *Contract) error {
        return nil
 }
 
-func prohibitValueParams(contract *Contract) error {
-       for _, p := range contract.Params {
-               if p.Type == valueType {
-                       return fmt.Errorf("Value-typed contract parameter \"%s\" must appear in a \"locks\" clause", p.Name)
-               }
-       }
-       for _, c := range contract.Clauses {
-               for _, p := range c.Params {
-                       if p.Type == valueType {
-                               return fmt.Errorf("Value-typed parameter \"%s\" of clause \"%s\" must appear in a \"requires\" clause", p.Name, c.Name)
-                       }
-               }
-       }
-       return nil
-}
-
 func requireAllParamsUsedInClauses(params []*Param, clauses []*Clause) error {
        for _, p := range params {
                used := false
@@ -67,22 +51,15 @@ func requireAllParamsUsedInClause(params []*Param, clause *Clause) error {
                        case *verifyStatement:
                                used = references(s.expr, p.Name)
                        case *lockStatement:
-                               used = references(s.locked, p.Name) || references(s.program, p.Name)
+                               used = references(s.lockedAmount, p.Name) || references(s.lockedAsset, p.Name) || references(s.program, p.Name)
                        case *unlockStatement:
-                               used = references(s.expr, p.Name)
+                               used = references(s.unlockedAmount, p.Name) || references(s.unlockedAsset, p.Name)
                        }
                        if used {
                                break
                        }
                }
-               if !used {
-                       for _, r := range clause.Reqs {
-                               if references(r.amountExpr, p.Name) || references(r.assetExpr, p.Name) {
-                                       used = true
-                                       break
-                               }
-                       }
-               }
+
                if !used {
                        return fmt.Errorf("parameter \"%s\" is unused in clause \"%s\"", p.Name, clause.Name)
                }
@@ -124,36 +101,30 @@ func requireAllValuesDisposedOnce(contract *Contract, clause *Clause) error {
        if err != nil {
                return err
        }
-       for _, req := range clause.Reqs {
-               err = valueDisposedOnce(req.Name, clause)
-               if err != nil {
-                       return err
-               }
-       }
        return nil
 }
 
-func valueDisposedOnce(name string, clause *Clause) error {
+func valueDisposedOnce(value ValueInfo, clause *Clause) error {
        var count int
        for _, s := range clause.statements {
                switch stmt := s.(type) {
                case *unlockStatement:
-                       if references(stmt.expr, name) {
+                       if references(stmt.unlockedAmount, value.Amount) && references(stmt.unlockedAsset, value.Asset) {
                                count++
                        }
                case *lockStatement:
-                       if references(stmt.locked, name) {
+                       if references(stmt.lockedAmount, value.Amount) && references(stmt.lockedAsset, value.Asset) {
                                count++
                        }
                }
        }
        switch count {
        case 0:
-               return fmt.Errorf("value \"%s\" not disposed in clause \"%s\"", name, clause.Name)
+               return fmt.Errorf("valueAmount \"%s\" or valueAsset \"%s\" not disposed in clause \"%s\"", value.Amount, value.Asset, clause.Name)
        case 1:
                return nil
        default:
-               return fmt.Errorf("value \"%s\" disposed multiple times in clause \"%s\"", name, clause.Name)
+               return fmt.Errorf("valueAmount \"%s\" or valueAsset \"%s\" disposed multiple times in clause \"%s\"", value.Amount, value.Asset, clause.Name)
        }
 }
 
@@ -191,19 +162,26 @@ func typeCheckClause(contract *Contract, clause *Clause, env *environ) error {
                        }
 
                case *lockStatement:
-                       if t := stmt.locked.typ(env); t != valueType {
-                               return fmt.Errorf("expression in lock statement in clause \"%s\" has type \"%s\", must be Value", clause.Name, t)
+                       if t := stmt.lockedAmount.typ(env); !(t == intType || t == amountType) {
+                               return fmt.Errorf("lockedAmount expression \"%s\" in lock statement in clause \"%s\" has type \"%s\", must be Integer", stmt.lockedAmount, clause.Name, t)
+                       }
+                       if t := stmt.lockedAsset.typ(env); t != assetType {
+                               return fmt.Errorf("lockedAsset expression \"%s\" in lock statement in clause \"%s\" has type \"%s\", must be Asset", stmt.lockedAsset, clause.Name, t)
                        }
                        if t := stmt.program.typ(env); t != progType {
                                return fmt.Errorf("program in lock statement in clause \"%s\" has type \"%s\", must be Program", clause.Name, t)
                        }
 
                case *unlockStatement:
-                       if t := stmt.expr.typ(env); t != valueType {
-                               return fmt.Errorf("expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Value", stmt.expr, clause.Name, t)
+                       if t := stmt.unlockedAmount.typ(env); !(t == intType || t == amountType) {
+                               return fmt.Errorf("unlockedAmount expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Integer", stmt.unlockedAmount, clause.Name, t)
+                       }
+                       if t := stmt.unlockedAsset.typ(env); t != assetType {
+                               return fmt.Errorf("unlockedAsset expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Asset", stmt.unlockedAsset, clause.Name, t)
                        }
-                       if stmt.expr.String() != contract.Value {
-                               return fmt.Errorf("expression in unlock statement of clause \"%s\" must be the contract value", clause.Name)
+                       if stmt.unlockedAmount.String() != contract.Value.Amount || stmt.unlockedAsset.String() != contract.Value.Asset {
+                               return fmt.Errorf("amount \"%s\" of asset \"%s\" expression in unlock statement of clause \"%s\" must be the contract valueAmount \"%s\" of valueAsset \"%s\"",
+                                       stmt.unlockedAmount.String(), stmt.unlockedAsset.String(), clause.Name, contract.Value.Amount, contract.Value.Asset)
                        }
                }
        }
index 99fe605..b543650 100644 (file)
@@ -5,6 +5,7 @@ import (
        "fmt"
        "io"
        "io/ioutil"
+       "strings"
 
        chainjson "github.com/bytom/encoding/json"
        "github.com/bytom/errors"
@@ -89,21 +90,17 @@ func Compile(r io.Reader) ([]*Contract, error) {
                                switch s := stmt.(type) {
                                case *lockStatement:
                                        valueInfo := ValueInfo{
-                                               Name:    s.locked.String(),
+                                               Amount:  s.lockedAmount.String(),
+                                               Asset:   s.lockedAsset.String(),
                                                Program: s.program.String(),
                                        }
-                                       if s.locked.String() != contract.Value {
-                                               for _, r := range clause.Reqs {
-                                                       if s.locked.String() == r.Name {
-                                                               valueInfo.Asset = r.assetExpr.String()
-                                                               valueInfo.Amount = r.amountExpr.String()
-                                                               break
-                                                       }
-                                               }
-                                       }
+
                                        clause.Values = append(clause.Values, valueInfo)
                                case *unlockStatement:
-                                       valueInfo := ValueInfo{Name: contract.Value}
+                                       valueInfo := ValueInfo{
+                                               Amount: contract.Value.Amount,
+                                               Asset:  contract.Value.Asset,
+                                       }
                                        clause.Values = append(clause.Values, valueInfo)
                                }
                        }
@@ -182,10 +179,15 @@ func compileContract(contract *Contract, globalEnv *environ) error {
                        return err
                }
        }
-       err = env.add(contract.Value, valueType, roleContractValue)
-       if err != nil {
+
+       // value is spilt with valueAmount and valueAsset
+       if err = env.add(contract.Value.Amount, amountType, roleContractValue); err != nil {
+               return err
+       }
+       if err = env.add(contract.Value.Asset, assetType, roleContractValue); err != nil {
                return err
        }
+
        for _, c := range contract.Clauses {
                err = env.add(c.Name, nilType, roleClause)
                if err != nil {
@@ -193,10 +195,6 @@ func compileContract(contract *Contract, globalEnv *environ) error {
                }
        }
 
-       err = prohibitValueParams(contract)
-       if err != nil {
-               return err
-       }
        err = prohibitSigParams(contract)
        if err != nil {
                return err
@@ -305,15 +303,6 @@ func compileClause(b *builder, contractStk stack, contract *Contract, env *envir
                        return err
                }
        }
-       for _, req := range clause.Reqs {
-               err = env.add(req.Name, valueType, roleClauseValue)
-               if err != nil {
-                       return err
-               }
-               req.Asset = req.assetExpr.String()
-               req.Amount = req.amountExpr.String()
-       }
-
        assignIndexes(clause)
 
        var stk stack
@@ -327,10 +316,6 @@ func compileClause(b *builder, contractStk stack, contract *Contract, env *envir
 
        // a count of the number of times each variable is referenced
        counts := make(map[string]int)
-       for _, req := range clause.Reqs {
-               req.assetExpr.countVarRefs(counts)
-               req.amountExpr.countVarRefs(counts)
-       }
        for _, s := range clause.statements {
                s.countVarRefs(counts)
        }
@@ -363,29 +348,26 @@ func compileClause(b *builder, contractStk stack, contract *Contract, env *envir
                        // TODO: permit more complex expressions for locked,
                        // like "lock x+y with foo" (?)
 
-                       if stmt.locked.String() == contract.Value {
-                               stk = b.addAmount(stk)
-                               stk = b.addAsset(stk)
+                       if stmt.lockedAmount.String() == contract.Value.Amount && stmt.lockedAsset.String() == contract.Value.Asset {
+                               stk = b.addAmount(stk, contract.Value.Amount)
+                               stk = b.addAsset(stk, contract.Value.Asset)
                        } else {
-                               var req *ClauseReq
-                               for _, r := range clause.Reqs {
-                                       if stmt.locked.String() == r.Name {
-                                               req = r
-                                               break
-                                       }
+                               if strings.Contains(stmt.lockedAmount.String(), contract.Value.Amount) {
+                                       stk = b.addAmount(stk, contract.Value.Amount)
                                }
-                               if req == nil {
-                                       return fmt.Errorf("unknown value \"%s\" in lock statement in clause \"%s\"", stmt.locked, clause.Name)
+
+                               if strings.Contains(stmt.lockedAsset.String(), contract.Value.Asset) {
+                                       stk = b.addAsset(stk, contract.Value.Asset)
                                }
 
                                // amount
-                               stk, err = compileExpr(b, stk, contract, clause, env, counts, req.amountExpr)
+                               stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAmount)
                                if err != nil {
                                        return errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
                                }
 
                                // asset
-                               stk, err = compileExpr(b, stk, contract, clause, env, counts, req.assetExpr)
+                               stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.lockedAsset)
                                if err != nil {
                                        return errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
                                }
@@ -400,7 +382,8 @@ func compileClause(b *builder, contractStk stack, contract *Contract, env *envir
                                return errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name)
                        }
 
-                       stk = b.addCheckOutput(stk, fmt.Sprintf("checkOutput(%s, %s)", stmt.locked, stmt.program))
+                       stk = b.addCheckOutput(stk, fmt.Sprintf("checkOutput(%s, %s, %s)",
+                               stmt.lockedAmount.String(), stmt.lockedAsset.String(), stmt.program))
                        stk = b.addVerify(stk)
 
                case *unlockStatement:
@@ -447,12 +430,12 @@ func compileExpr(b *builder, stk stack, contract *Contract, clause *Clause, env
                }
 
                lType := e.left.typ(env)
-               if e.op.left != "" && lType != e.op.left {
+               if e.op.left != "" && !(lType == e.op.left || lType == amountType) {
                        return stk, fmt.Errorf("in \"%s\", left operand has type \"%s\", must be \"%s\"", e, lType, e.op.left)
                }
 
                rType := e.right.typ(env)
-               if e.op.right != "" && rType != e.op.right {
+               if e.op.right != "" && !(rType == e.op.right || rType == amountType) {
                        return stk, fmt.Errorf("in \"%s\", right operand has type \"%s\", must be \"%s\"", e, rType, e.op.right)
                }
 
index 61a4765..5fd0d37 100644 (file)
@@ -60,8 +60,12 @@ func parseContract(p *parser) *Contract {
        consumeKeyword(p, "contract")
        name := consumeIdentifier(p)
        params := parseParams(p)
+       // locks amount of asset
        consumeKeyword(p, "locks")
-       value := consumeIdentifier(p)
+       value := ValueInfo{}
+       value.Amount = consumeIdentifier(p)
+       consumeKeyword(p, "of")
+       value.Asset = consumeIdentifier(p)
        consumeTok(p, "{")
        clauses := parseClauses(p)
        consumeTok(p, "}")
@@ -120,38 +124,12 @@ func parseClause(p *parser) *Clause {
        consumeKeyword(p, "clause")
        c.Name = consumeIdentifier(p)
        c.Params = parseParams(p)
-       if peekKeyword(p) == "requires" {
-               consumeKeyword(p, "requires")
-               c.Reqs = parseClauseRequirements(p)
-       }
        consumeTok(p, "{")
        c.statements = parseStatements(p)
        consumeTok(p, "}")
        return &c
 }
 
-func parseClauseRequirements(p *parser) []*ClauseReq {
-       var result []*ClauseReq
-       first := true
-       for {
-               switch {
-               case first:
-                       first = false
-               case peekTok(p, ","):
-                       consumeTok(p, ",")
-               default:
-                       return result
-               }
-               var req ClauseReq
-               req.Name = consumeIdentifier(p)
-               consumeTok(p, ":")
-               req.amountExpr = parseExpr(p)
-               consumeKeyword(p, "of")
-               req.assetExpr = parseExpr(p)
-               result = append(result, &req)
-       }
-}
-
 func parseStatements(p *parser) []statement {
        var statements []statement
        for !peekTok(p, "}") {
@@ -181,16 +159,20 @@ func parseVerifyStmt(p *parser) *verifyStatement {
 
 func parseLockStmt(p *parser) *lockStatement {
        consumeKeyword(p, "lock")
-       locked := parseExpr(p)
+       lockedAmount := parseExpr(p)
+       consumeKeyword(p, "of")
+       lockedAsset := parseExpr(p)
        consumeKeyword(p, "with")
        program := parseExpr(p)
-       return &lockStatement{locked: locked, program: program}
+       return &lockStatement{lockedAmount: lockedAmount, lockedAsset: lockedAsset, program: program}
 }
 
 func parseUnlockStmt(p *parser) *unlockStatement {
        consumeKeyword(p, "unlock")
-       expr := parseExpr(p)
-       return &unlockStatement{expr}
+       unlockedAmount := parseExpr(p)
+       consumeKeyword(p, "of")
+       unlockedAsset := parseExpr(p)
+       return &unlockStatement{unlockedAmount: unlockedAmount, unlockedAsset: unlockedAsset}
 }
 
 func parseExpr(p *parser) expression {
index d8b5b2c..1252de3 100644 (file)
@@ -16,7 +16,6 @@ var (
        pubkeyType   = typeDesc("PublicKey")
        sigType      = typeDesc("Signature")
        strType      = typeDesc("String")
-       valueType    = typeDesc("Value")
 
        sha3StrType      = typeDesc("Sha3(String)")
        sha3PubkeyType   = typeDesc("Sha3(PublicKey)")
@@ -37,7 +36,6 @@ var types = map[string]typeDesc{
        string(pubkeyType): pubkeyType,
        string(sigType):    sigType,
        string(strType):    strType,
-       string(valueType):  valueType,
 
        string(sha3StrType):      sha3StrType,
        string(sha3PubkeyType):   sha3PubkeyType,