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
// 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
}
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 {
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 {
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
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)
}
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)
}
}
}
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)
}
}
}
"fmt"
"io"
"io/ioutil"
+ "strings"
chainjson "github.com/bytom/encoding/json"
"github.com/bytom/errors"
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)
}
}
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 {
}
}
- err = prohibitValueParams(contract)
- if err != nil {
- return err
- }
err = prohibitSigParams(contract)
if err != nil {
return err
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
// 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)
}
// 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)
}
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:
}
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)
}
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, "}")
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, "}") {
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 {
pubkeyType = typeDesc("PublicKey")
sigType = typeDesc("Signature")
strType = typeDesc("String")
- valueType = typeDesc("Value")
sha3StrType = typeDesc("Sha3(String)")
sha3PubkeyType = typeDesc("Sha3(PublicKey)")
string(pubkeyType): pubkeyType,
string(sigType): sigType,
string(strType): strType,
- string(valueType): valueType,
string(sha3StrType): sha3StrType,
string(sha3PubkeyType): sha3PubkeyType,