From: Paladz Date: Wed, 29 Nov 2017 03:19:15 +0000 (+0800) Subject: remove exp (#143) X-Git-Tag: v1.0.5-alpha~609 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=04aef9a4ba5dd40986e1f83c473191bb915fc12d;p=bytom%2Fbytom-spv.git remove exp (#143) --- diff --git a/exp/Readme.md b/exp/Readme.md deleted file mode 100644 index c638c375..00000000 --- a/exp/Readme.md +++ /dev/null @@ -1,6 +0,0 @@ -Experimental tree -================= - -Code in this tree may be committed to `main` with minimal review, but may not be imported by production code. - -A thorough code review is required to move code out of this tree and into Chain Core proper. diff --git a/exp/ivy/compiler/ast.go b/exp/ivy/compiler/ast.go deleted file mode 100644 index 22e6e41e..00000000 --- a/exp/ivy/compiler/ast.go +++ /dev/null @@ -1,318 +0,0 @@ -package compiler - -import ( - "encoding/hex" - "fmt" - "strconv" - "strings" - - chainjson "github.com/bytom/encoding/json" -) - -// Contract is a compiled Ivy contract. -type Contract struct { - // Name is the contract name. - Name string `json:"name"` - - // Params is the list of contract parameters. - Params []*Param `json:"params,omitempty"` - - // Clauses is the list of contract clauses. - Clauses []*Clause `json:"clauses"` - - // Value is the name of the value locked by the contract. - Value string `json:"value"` - - // Body is the optimized bytecode of the contract body. This is not - // a complete program! Use instantiate to turn this (plus some - // arguments) into a program. - Body chainjson.HexBytes `json:"body_bytecode"` - - // Opcodes is the human-readable string of opcodes corresponding to - // Body. - Opcodes string `json:"body_opcodes,omitempty"` - - // Recursive tells whether this contract calls itself. (This is - // used to select between two possible instantiation options.) - Recursive bool `json:"recursive"` - - // Pre-optimized list of instruction steps, with stack snapshots. - Steps []Step `json:"-"` -} - -// Param is a contract or clause parameter. -type Param struct { - // Name is the parameter name. - Name string `json:"name"` - - // Type is the declared parameter type. - Type typeDesc `json:"declared_type"` - - // InferredType, if available, is a more-specific type than Type, - // inferred from the logic of the contract. - InferredType typeDesc `json:"inferred_type,omitempty"` -} - -// Clause is a compiled contract clause. -type Clause struct { - // Name is the clause name. - Name string `json:"name"` - - // 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 - - // MinTimes is the list of expressions passed to after() in this - // clause. - MinTimes []string `json:"mintimes,omitempty"` - - // MaxTimes is the list of expressions passed to before() in this - // clause. - MaxTimes []string `json:"maxtimes,omitempty"` - - // HashCalls is the list of hash functions and their arguments used - // in this clause. - HashCalls []HashCall `json:"hash_calls,omitempty"` - - // Values is the list of values unlocked or relocked in this clause. - Values []ValueInfo `json:"values"` - - // Contracts is the list of contracts called by this clause. - Contracts []string `json:"contracts,omitempty"` -} - -// HashCall describes a call to a hash function. -type HashCall struct { - // HashType is "sha3" or "sha256". - HashType string `json:"hash_type"` - - // Arg is the expression passed to the hash function. - Arg string `json:"arg"` - - // ArgType is the type of Arg. - ArgType string `json:"arg_type"` -} - -// ClauseReq describes a payment requirement of a clause (one of the -// things after the "requires" keyword). -type ClauseReq struct { - Name string `json:"name"` - - assetExpr, amountExpr expression - - // Asset is the expression describing the required asset. - Asset string `json:"asset"` - - // Amount is the expression describing the required amount. - Amount string `json:"amount"` -} - -type statement interface { - countVarRefs(map[string]int) -} - -type verifyStatement struct { - expr expression -} - -func (s verifyStatement) countVarRefs(counts map[string]int) { - s.expr.countVarRefs(counts) -} - -type lockStatement struct { - locked 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.program.countVarRefs(counts) -} - -type unlockStatement struct { - expr expression -} - -func (s unlockStatement) countVarRefs(counts map[string]int) { - s.expr.countVarRefs(counts) -} - -type expression interface { - String() string - typ(*environ) typeDesc - countVarRefs(map[string]int) -} - -type binaryExpr struct { - left, right expression - op *binaryOp -} - -func (e binaryExpr) String() string { - return fmt.Sprintf("(%s %s %s)", e.left, e.op.op, e.right) -} - -func (e binaryExpr) typ(*environ) typeDesc { - return e.op.result -} - -func (e binaryExpr) countVarRefs(counts map[string]int) { - e.left.countVarRefs(counts) - e.right.countVarRefs(counts) -} - -type unaryExpr struct { - op *unaryOp - expr expression -} - -func (e unaryExpr) String() string { - return fmt.Sprintf("%s%s", e.op.op, e.expr) -} - -func (e unaryExpr) typ(*environ) typeDesc { - return e.op.result -} - -func (e unaryExpr) countVarRefs(counts map[string]int) { - e.expr.countVarRefs(counts) -} - -type callExpr struct { - fn expression - args []expression -} - -func (e callExpr) String() string { - var argStrs []string - for _, a := range e.args { - argStrs = append(argStrs, a.String()) - } - return fmt.Sprintf("%s(%s)", e.fn, strings.Join(argStrs, ", ")) -} - -func (e callExpr) typ(env *environ) typeDesc { - if b := referencedBuiltin(e.fn); b != nil { - switch b.name { - case "sha3": - if len(e.args) == 1 { - switch e.args[0].typ(env) { - case strType: - return sha3StrType - case pubkeyType: - return sha3PubkeyType - } - } - - case "sha256": - if len(e.args) == 1 { - switch e.args[0].typ(env) { - case strType: - return sha256StrType - case pubkeyType: - return sha256PubkeyType - } - } - } - - return b.result - } - if e.fn.typ(env) == predType { - return boolType - } - if e.fn.typ(env) == contractType { - return progType - } - return nilType -} - -func (e callExpr) countVarRefs(counts map[string]int) { - e.fn.countVarRefs(counts) - for _, a := range e.args { - a.countVarRefs(counts) - } -} - -type varRef string - -func (v varRef) String() string { - return string(v) -} - -func (e varRef) typ(env *environ) typeDesc { - if entry := env.lookup(string(e)); entry != nil { - return entry.t - } - return nilType -} - -func (e varRef) countVarRefs(counts map[string]int) { - counts[string(e)]++ -} - -type bytesLiteral []byte - -func (e bytesLiteral) String() string { - return "0x" + hex.EncodeToString([]byte(e)) -} - -func (bytesLiteral) typ(*environ) typeDesc { - return "String" -} - -func (bytesLiteral) countVarRefs(map[string]int) {} - -type integerLiteral int64 - -func (e integerLiteral) String() string { - return strconv.FormatInt(int64(e), 10) -} - -func (integerLiteral) typ(*environ) typeDesc { - return "Integer" -} - -func (integerLiteral) countVarRefs(map[string]int) {} - -type booleanLiteral bool - -func (e booleanLiteral) String() string { - if e { - return "true" - } - return "false" -} - -func (booleanLiteral) typ(*environ) typeDesc { - return "Boolean" -} - -func (booleanLiteral) countVarRefs(map[string]int) {} - -type listExpr []expression - -func (e listExpr) String() string { - var elts []string - for _, elt := range e { - elts = append(elts, elt.String()) - } - return fmt.Sprintf("[%s]", strings.Join(elts, ", ")) -} - -func (listExpr) typ(*environ) typeDesc { - return "List" -} - -func (e listExpr) countVarRefs(counts map[string]int) { - for _, elt := range e { - elt.countVarRefs(counts) - } -} diff --git a/exp/ivy/compiler/builder.go b/exp/ivy/compiler/builder.go deleted file mode 100644 index 180896f2..00000000 --- a/exp/ivy/compiler/builder.go +++ /dev/null @@ -1,210 +0,0 @@ -package compiler - -import ( - "fmt" - "strconv" - "strings" -) - -type builder struct { - items []*builderItem - pendingVerify *builderItem -} - -type builderItem struct { - opcodes string - stk stack -} - -func (b *builder) add(opcodes string, newstack stack) stack { - if b.pendingVerify != nil { - b.items = append(b.items, b.pendingVerify) - b.pendingVerify = nil - } - item := &builderItem{opcodes: opcodes, stk: newstack} - if opcodes == "VERIFY" { - b.pendingVerify = item - } else { - b.items = append(b.items, item) - } - return newstack -} - -func (b *builder) addRoll(stk stack, n int) stack { - b.addInt64(stk, int64(n)) - return b.add("ROLL", stk.roll(n)) -} - -func (b *builder) addDup(stk stack) stack { - return b.add("DUP", stk.dup()) -} - -func (b *builder) addInt64(stk stack, n int64) stack { - s := strconv.FormatInt(n, 10) - return b.add(s, stk.add(s)) -} - -func (b *builder) addNumEqual(stk stack, desc string) stack { - return b.add("NUMEQUAL", stk.dropN(2).add(desc)) -} - -func (b *builder) addJumpIf(stk stack, label string) stack { - return b.add(fmt.Sprintf("JUMPIF:$%s", label), stk.drop()) -} - -func (b *builder) addJumpTarget(stk stack, label string) stack { - return b.add("$"+label, stk) -} - -func (b *builder) addDrop(stk stack) stack { - return b.add("DROP", stk.drop()) -} - -func (b *builder) forgetPendingVerify() { - b.pendingVerify = nil -} - -func (b *builder) addJump(stk stack, label string) stack { - return b.add(fmt.Sprintf("JUMP:$%s", label), stk) -} - -func (b *builder) addVerify(stk stack) stack { - return b.add("VERIFY", stk.drop()) -} - -func (b *builder) addData(stk stack, data []byte) stack { - var s string - switch len(data) { - case 0: - s = "0" - case 1: - s = strconv.FormatInt(int64(data[0]), 10) - default: - s = fmt.Sprintf("0x%x", data) - } - return b.add(s, stk.add(s)) -} - -func (b *builder) addAmount(stk stack) stack { - return b.add("AMOUNT", stk.add("")) -} - -func (b *builder) addAsset(stk stack) stack { - return b.add("ASSET", stk.add("")) -} - -func (b *builder) addCheckOutput(stk stack, desc string) stack { - return b.add("CHECKOUTPUT", stk.dropN(6).add(desc)) -} - -func (b *builder) addBoolean(stk stack, val bool) stack { - if val { - return b.add("TRUE", stk.add("true")) - } - return b.add("FALSE", stk.add("false")) -} - -func (b *builder) addOps(stk stack, ops string, desc string) stack { - return b.add(ops, stk.add(desc)) -} - -func (b *builder) addToAltStack(stk stack) (stack, string) { - t := stk.top() - return b.add("TOALTSTACK", stk.drop()), t -} - -func (b *builder) addTxSigHash(stk stack) stack { - return b.add("TXSIGHASH", stk.add("")) -} - -func (b *builder) addFromAltStack(stk stack, alt string) stack { - return b.add("FROMALTSTACK", stk.add(alt)) -} - -func (b *builder) addSwap(stk stack) stack { - return b.add("SWAP", stk.swap()) -} - -func (b *builder) addCheckMultisig(stk stack, n int, desc string) stack { - return b.add("CHECKMULTISIG", stk.dropN(n).add(desc)) -} - -func (b *builder) addOver(stk stack) stack { - return b.add("OVER", stk.over()) -} - -func (b *builder) addPick(stk stack, n int) stack { - b.addInt64(stk, int64(n)) - return b.add("PICK", stk.pick(n)) -} - -func (b *builder) addCatPushdata(stk stack, desc string) stack { - return b.add("CATPUSHDATA", stk.dropN(2).add(desc)) -} - -func (b *builder) addCat(stk stack, desc string) stack { - return b.add("CAT", stk.dropN(2).add(desc)) -} - -func (b *builder) opcodes() string { - var ops []string - for _, item := range b.items { - ops = append(ops, item.opcodes) - } - return strings.Join(ops, " ") -} - -// This is for producing listings like: -// 5 | [... borrower lender deadline balanceAmount balanceAsset 5] -// ROLL | [... borrower lender deadline balanceAmount balanceAsset ] -// JUMPIF:$default | [... borrower lender deadline balanceAmount balanceAsset] -// $repay | [... borrower lender deadline balanceAmount balanceAsset] -// 0 | [... borrower lender deadline balanceAmount balanceAsset 0] -// 0 | [... borrower lender deadline balanceAmount balanceAsset 0 0] -// 3 | [... borrower lender deadline balanceAmount balanceAsset 0 0 3] -// ROLL | [... borrower lender deadline balanceAsset 0 0 balanceAmount] -// 3 | [... borrower lender deadline balanceAsset 0 0 balanceAmount 3] -// ROLL | [... borrower lender deadline 0 0 balanceAmount balanceAsset] -// 1 | [... borrower lender deadline 0 0 balanceAmount balanceAsset 1] -// 6 | [... borrower lender deadline 0 0 balanceAmount balanceAsset 1 6] -// ROLL | [... borrower deadline 0 0 balanceAmount balanceAsset 1 lender] -// CHECKOUTPUT | [... borrower deadline checkOutput(payment, lender)] -// VERIFY | [... borrower deadline] -// 1 | [... borrower deadline 1] -// 0 | [... borrower deadline 1 0] -// AMOUNT | [... borrower deadline 1 0 ] -// ASSET | [... borrower deadline 1 0 ] -// 1 | [... borrower deadline 1 0 1] -// 6 | [... borrower deadline 1 0 1 6] -// ROLL | [... deadline 1 0 1 borrower] -// CHECKOUTPUT | [... deadline checkOutput(collateral, borrower)] -// JUMP:$_end | [... borrower lender deadline balanceAmount balanceAsset] -// $default | [... borrower lender deadline balanceAmount balanceAsset] -// 2 | [... borrower lender deadline balanceAmount balanceAsset 2] -// ROLL | [... borrower lender balanceAmount balanceAsset deadline] -// MINTIME LESSTHAN | [... borrower lender balanceAmount balanceAsset after(deadline)] -// VERIFY | [... borrower lender balanceAmount balanceAsset] -// 0 | [... borrower lender balanceAmount balanceAsset 0] -// 0 | [... borrower lender balanceAmount balanceAsset 0 0] -// AMOUNT | [... borrower lender balanceAmount balanceAsset 0 0 ] -// ASSET | [... borrower lender balanceAmount balanceAsset 0 0 ] -// 1 | [... borrower lender balanceAmount balanceAsset 0 0 1] -// 7 | [... borrower lender balanceAmount balanceAsset 0 0 1 7] -// ROLL | [... borrower balanceAmount balanceAsset 0 0 1 lender] -// CHECKOUTPUT | [... borrower balanceAmount balanceAsset checkOutput(collateral, lender)] -// $_end | [... borrower lender deadline balanceAmount balanceAsset] - -type ( - Step struct { - Opcodes string `json:"opcodes"` - Stack string `json:"stack"` - } -) - -func (b *builder) steps() []Step { - var result []Step - for _, item := range b.items { - result = append(result, Step{item.opcodes, item.stk.String()}) - } - return result -} diff --git a/exp/ivy/compiler/builder_test.go b/exp/ivy/compiler/builder_test.go deleted file mode 100644 index 63ec6b1d..00000000 --- a/exp/ivy/compiler/builder_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package compiler - -// func TestBuilder(t *testing.T) { -// cases := []struct { -// name string -// f func(*builder) -// wantHex string -// }{ -// { -// "single pushdata", -// func(b *builder) { -// b.addInt64(1) -// }, -// "51", -// }, -// { -// "pushdata and verify", -// func(b *builder) { -// b.addInt64(1) -// b.addOp(vm.OP_VERIFY) -// }, -// "51", -// }, -// { -// "pushdata, verify, second pushdata", -// func(b *builder) { -// b.addInt64(1) -// b.addOp(vm.OP_VERIFY) -// b.addInt64(2) -// }, -// "516952", -// }, -// } -// for _, c := range cases { -// t.Run(c.name, func(t *testing.T) { -// b := newBuilder() -// c.f(b) -// got, err := b.build() -// if err != nil { -// t.Fatal(err) -// } -// want, err := hex.DecodeString(c.wantHex) -// if err != nil { -// t.Fatal(err) -// } -// if !bytes.Equal(got, want) { -// t.Errorf("got %x, want %x", got, want) -// } -// }) -// } -// } diff --git a/exp/ivy/compiler/builtins.go b/exp/ivy/compiler/builtins.go deleted file mode 100644 index d0b70e3d..00000000 --- a/exp/ivy/compiler/builtins.go +++ /dev/null @@ -1,79 +0,0 @@ -package compiler - -type builtin struct { - name string - opcodes string - args []typeDesc - result typeDesc -} - -var builtins = []builtin{ - {"sha3", "SHA3", []typeDesc{nilType}, hashType}, - {"sha256", "SHA256", []typeDesc{nilType}, hashType}, - {"size", "SIZE SWAP DROP", []typeDesc{nilType}, intType}, - {"abs", "ABS", []typeDesc{intType}, intType}, - {"min", "MIN", []typeDesc{intType, intType}, intType}, - {"max", "MAX", []typeDesc{intType, intType}, intType}, - {"checkTxSig", "TXSIGHASH SWAP CHECKSIG", []typeDesc{pubkeyType, sigType}, boolType}, - {"concat", "CAT", []typeDesc{nilType, nilType}, strType}, - {"concatpush", "CATPUSHDATA", []typeDesc{nilType, nilType}, strType}, - {"before", "MAXTIME GREATERTHAN", []typeDesc{timeType}, boolType}, - {"after", "MINTIME LESSTHAN", []typeDesc{timeType}, boolType}, - {"checkTxMultiSig", "", []typeDesc{listType, listType}, boolType}, // WARNING WARNING WOOP WOOP special case -} - -type binaryOp struct { - op string - precedence int - opcodes string - - left, right, result typeDesc -} - -var binaryOps = []binaryOp{ - // disjunctions disallowed (for now?) - // {"||", 1, "BOOLOR", "Boolean", "Boolean", "Boolean"}, - - // and disallow this too - // {"&&", 2, "BOOLAND", "Boolean", "Boolean", "Boolean"}, - - {">", 3, "GREATERTHAN", "Integer", "Integer", "Boolean"}, - {"<", 3, "LESSTHAN", "Integer", "Integer", "Boolean"}, - {">=", 3, "GREATERTHANOREQUAL", "Integer", "Integer", "Boolean"}, - {"<=", 3, "LESSTHANOREQUAL", "Integer", "Integer", "Boolean"}, - - {"==", 3, "EQUAL", "", "", "Boolean"}, - {"!=", 3, "EQUAL NOT", "", "", "Boolean"}, - - {"^", 4, "XOR", "", "", ""}, - {"|", 4, "OR", "", "", ""}, - - {"+", 4, "ADD", "Integer", "Integer", "Integer"}, - {"-", 4, "SUB", "Integer", "Integer", "Integer"}, - - // {"&^", 5, "INVERT AND", "", "", ""}, - {"&", 5, "AND", "", "", ""}, - - {"<<", 5, "LSHIFT", "Integer", "Integer", "Integer"}, - {">>", 5, "RSHIFT", "Integer", "Integer", "Integer"}, - - {"%", 5, "MOD", "Integer", "Integer", "Integer"}, - {"*", 5, "MUL", "Integer", "Integer", "Integer"}, - {"/", 5, "DIV", "Integer", "Integer", "Integer"}, -} - -type unaryOp struct { - op string - opcodes string - - operand, result typeDesc -} - -var unaryOps = []unaryOp{ - {"-", "NEGATE", "Integer", "Integer"}, - - // not not allowed (for now?) - // {"!", "NOT", "Boolean", "Boolean"}, - - {"~", "INVERT", "", ""}, -} diff --git a/exp/ivy/compiler/checks.go b/exp/ivy/compiler/checks.go deleted file mode 100644 index d1a1c1ab..00000000 --- a/exp/ivy/compiler/checks.go +++ /dev/null @@ -1,211 +0,0 @@ -package compiler - -import "fmt" - -func checkRecursive(contract *Contract) bool { - for _, clause := range contract.Clauses { - for _, stmt := range clause.statements { - if l, ok := stmt.(*lockStatement); ok { - if c, ok := l.program.(*callExpr); ok { - if references(c.fn, contract.Name) { - return true - } - } - } - } - } - return false -} - -func prohibitSigParams(contract *Contract) error { - for _, p := range contract.Params { - if p.Type == sigType { - return fmt.Errorf("contract parameter \"%s\" has type Signature, but contract parameters cannot have type Signature", p.Name) - } - } - 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 - for _, c := range clauses { - err := requireAllParamsUsedInClause([]*Param{p}, c) - if err == nil { - used = true - break - } - } - if !used { - return fmt.Errorf("parameter \"%s\" is unused", p.Name) - } - } - return nil -} - -func requireAllParamsUsedInClause(params []*Param, clause *Clause) error { - for _, p := range params { - used := false - for _, stmt := range clause.statements { - switch s := stmt.(type) { - case *verifyStatement: - used = references(s.expr, p.Name) - case *lockStatement: - used = references(s.locked, p.Name) || references(s.program, p.Name) - case *unlockStatement: - used = references(s.expr, 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) - } - } - return nil -} - -func references(expr expression, name string) bool { - switch e := expr.(type) { - case *binaryExpr: - return references(e.left, name) || references(e.right, name) - case *unaryExpr: - return references(e.expr, name) - case *callExpr: - if references(e.fn, name) { - return true - } - for _, a := range e.args { - if references(a, name) { - return true - } - } - return false - case varRef: - return string(e) == name - case listExpr: - for _, elt := range []expression(e) { - if references(elt, name) { - return true - } - } - return false - } - return false -} - -func requireAllValuesDisposedOnce(contract *Contract, clause *Clause) error { - err := valueDisposedOnce(contract.Value, clause) - 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 { - var count int - for _, s := range clause.statements { - switch stmt := s.(type) { - case *unlockStatement: - if references(stmt.expr, name) { - count++ - } - case *lockStatement: - if references(stmt.locked, name) { - count++ - } - } - } - switch count { - case 0: - return fmt.Errorf("value \"%s\" not disposed in clause \"%s\"", name, clause.Name) - case 1: - return nil - default: - return fmt.Errorf("value \"%s\" disposed multiple times in clause \"%s\"", name, clause.Name) - } -} - -func referencedBuiltin(expr expression) *builtin { - if v, ok := expr.(varRef); ok { - for _, b := range builtins { - if string(v) == b.name { - return &b - } - } - } - return nil -} - -func assignIndexes(clause *Clause) { - var nextIndex int64 - for _, s := range clause.statements { - switch stmt := s.(type) { - case *lockStatement: - stmt.index = nextIndex - nextIndex++ - - case *unlockStatement: - nextIndex++ - } - } -} - -func typeCheckClause(contract *Contract, clause *Clause, env *environ) error { - for _, s := range clause.statements { - switch stmt := s.(type) { - case *verifyStatement: - if t := stmt.expr.typ(env); t != boolType { - return fmt.Errorf("expression in verify statement in clause \"%s\" has type \"%s\", must be Boolean", clause.Name, t) - } - - 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.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 stmt.expr.String() != contract.Value { - return fmt.Errorf("expression in unlock statement of clause \"%s\" must be the contract value", clause.Name) - } - } - } - return nil -} diff --git a/exp/ivy/compiler/checks_test.go b/exp/ivy/compiler/checks_test.go deleted file mode 100644 index 79e92827..00000000 --- a/exp/ivy/compiler/checks_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package compiler - -import "testing" - -func TestRequireAllParamsUsedInClauses(t *testing.T) { - clauses := []*Clause{ - &Clause{ - statements: []statement{ - &verifyStatement{expr: varRef("foo")}, - &verifyStatement{ - expr: &binaryExpr{ - left: varRef("foo"), - right: varRef("bar"), - }, - }, - &lockStatement{ - locked: varRef("baz"), - program: varRef("foo"), - }, - }, - }, - &Clause{ - statements: []statement{ - &verifyStatement{expr: varRef("foo")}, - &verifyStatement{ - expr: &binaryExpr{ - left: varRef("foo"), - right: varRef("plugh"), - }, - }, - &lockStatement{ - locked: varRef("xyzzy"), - program: varRef("foo"), - }, - }, - }, - } - - cases := []struct { - name string - params []string - want string - }{ - { - name: "contract param used in both clauses", - params: []string{"foo"}, - }, - { - name: "contract param used in one clause", - params: []string{"bar"}, - }, - { - name: "contract param used in no clauses", - params: []string{"y2"}, - want: "parameter \"y2\" is unused", - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - var params []*Param - for _, p := range c.params { - params = append(params, &Param{Name: p}) - } - err := requireAllParamsUsedInClauses(params, clauses) - if err == nil && c.want == "" { - return - } - if err == nil { - t.Errorf("got err==nil, want %s", c.want) - return - } - if err.Error() != c.want { - t.Errorf("got %s, want %s", err, c.want) - } - }) - } -} diff --git a/exp/ivy/compiler/cmd/ivyc/ivyc.go b/exp/ivy/compiler/cmd/ivyc/ivyc.go deleted file mode 100644 index 92b2e2aa..00000000 --- a/exp/ivy/compiler/cmd/ivyc/ivyc.go +++ /dev/null @@ -1,216 +0,0 @@ -package main - -import ( - "bytes" - "flag" - "fmt" - "log" - "os" - "strings" - - "github.com/bytom/exp/ivy/compiler" -) - -func main() { - packageName := flag.String("package", "main", "Go package name for generated file") - flag.Parse() - - contracts, err := compiler.Compile(os.Stdin) - if err != nil { - log.Fatal(err) - } - - fmt.Printf("package %s\n\n", *packageName) - - imports := map[string]bool{ - "bytes": true, - "encoding/hex": true, - "fmt": true, - "chain/exp/ivy/compiler": true, - "chain/protocol/vm": true, - } - - buf := new(bytes.Buffer) - - if len(contracts) == 1 { - fmt.Fprintf(buf, "var %s_body_bytes []byte\n\n", contracts[0].Name) - } else { - fmt.Fprintf(buf, "var (\n") - for _, contract := range contracts { - fmt.Fprintf(buf, "\t%s_body_bytes []byte\n", contract.Name) - } - fmt.Fprintf(buf, ")\n\n") - } - - fmt.Fprintf(buf, "func init() {\n") - for _, contract := range contracts { - fmt.Fprintf(buf, "\t%s_body_bytes, _ = hex.DecodeString(\"%x\")\n", contract.Name, contract.Body) - } - fmt.Fprintf(buf, "}\n\n") - - for _, contract := range contracts { - fmt.Fprintf(buf, "// contract %s(%s) locks %s\n", contract.Name, paramsStr(contract.Params), contract.Value) - fmt.Fprintf(buf, "//\n") - maxWidth := 0 - for _, step := range contract.Steps { - if len(step.Opcodes) > maxWidth { - maxWidth = len(step.Opcodes) - } - } - format := fmt.Sprintf("// %%-%d.%ds %%s\n", maxWidth, maxWidth) - for _, step := range contract.Steps { - fmt.Fprintf(buf, format, step.Opcodes, step.Stack) - } - fmt.Fprintf(buf, "\n") - - fmt.Fprintf(buf, "// PayTo%s instantiates contract %s as a program with specific arguments.\n", contract.Name, contract.Name) - goParams, newImports := asGoParams(contract.Params) - for _, imp := range newImports { - imports[imp] = true - } - fmt.Fprintf(buf, "func PayTo%s(%s) ([]byte, error) {\n", contract.Name, goParams) - fmt.Fprintf(buf, "\t_contractParams := []compiler.Param{\n") - for _, param := range contract.Params { - fmt.Fprintf(buf, "\t\t{Name: \"%s\", Type: \"%s\"},\n", param.Name, param.Type) - } - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tvar _contractArgs []compiler.ContractArg\n") - for _, param := range contract.Params { - switch param.Type { - case "Amount": - fmt.Fprintf(buf, "\t_%s := int64(%s)\n", param.Name, param.Name) - fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{I: &_%s})\n", param.Name) - case "Asset": - fmt.Fprintf(buf, "\t_%s := %s[:]\n", param.Name, param.Name) - fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{S: &_%s})\n", param.Name) - case "Boolean", "Hash", "Program", "PublicKey", "Signature", "String": - fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{S: &%s})\n", param.Name) - case "Integer": - fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{I: &%s})\n", param.Name) - case "Time": - fmt.Fprintf(buf, "\t_%s := %s.UnixNano() / time.Millisecond\n", param.Name, param.Name) - fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{I: &_%s})\n", param.Name) - } - } - fmt.Fprintf(buf, "\treturn compiler.Instantiate(_contractParams, %s_body_bytes, %v, _contractArgs)\n", contract.Name, contract.Recursive) - fmt.Fprintf(buf, "}\n\n") - - fmt.Fprintf(buf, "// ParsePayTo%s parses the arguments out of an instantiation of contract %s.\n", contract.Name, contract.Name) - fmt.Fprintf(buf, "// If the input is not an instantiation of %s, returns an error.\n", contract.Name) - fmt.Fprintf(buf, "func ParsePayTo%s(prog []byte) ([][]byte, error) {\n", contract.Name) - fmt.Fprintf(buf, "\tvar result [][]byte\n") - fmt.Fprintf(buf, "\tinsts, err := vm.ParseProgram(prog)\n") - fmt.Fprintf(buf, "\tif err != nil {\n") - fmt.Fprintf(buf, "\t\treturn nil, err\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tfor i := 0; i < %d; i++ {\n", len(contract.Params)) - fmt.Fprintf(buf, "\t\tif len(insts) == 0 {\n") - fmt.Fprintf(buf, "\t\t\treturn nil, fmt.Errorf(\"program too short\")\n") - fmt.Fprintf(buf, "\t\t}\n") - fmt.Fprintf(buf, "\t\tif !insts[0].IsPushdata() {\n") - fmt.Fprintf(buf, "\t\t\treturn nil, fmt.Errorf(\"too few arguments\")\n") - fmt.Fprintf(buf, "\t\t}\n") - fmt.Fprintf(buf, "\t\tresult = append(result, insts[0].Data)\n") - fmt.Fprintf(buf, "\t\tinsts = insts[1:]\n") - fmt.Fprintf(buf, "\t}\n") - if contract.Recursive { - // args... body DEPTH OVER 0 CHECKPREDICATE - fmt.Fprintf(buf, "\tif len(insts) == 0 {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"program too short\")\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tif !insts[0].IsPushdata() {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"too few arguments\")\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tif !bytes.Equal(%s_body_bytes, insts[0].Data) {\n", contract.Name) - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"body bytes do not match %s\")\n", contract.Name) - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tinsts = insts[1:]\n") - } // else args ... DEPTH body 0 CHECKPREDICATE - fmt.Fprintf(buf, "\tif len(insts) != 4 {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"program too short\")\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tif insts[0].Op != vm.OP_DEPTH {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") - fmt.Fprintf(buf, "\t}\n") - if contract.Recursive { - fmt.Fprintf(buf, "\tif insts[1].Op != vm.OP_OVER {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") - fmt.Fprintf(buf, "\t}\n") - } else { - fmt.Fprintf(buf, "\tif !insts[1].IsPushdata() {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tif !bytes.Equal(%s_body_bytes, insts[1].Data) {\n", contract.Name) - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"body bytes do not match %s\")\n", contract.Name) - fmt.Fprintf(buf, "\t}\n") - } - fmt.Fprintf(buf, "\tif !insts[2].IsPushdata() {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tv, err := vm.AsInt64(insts[2].Data)\n") - fmt.Fprintf(buf, "\tif err != nil {\n") - fmt.Fprintf(buf, "\t\treturn nil, err\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tif v != 0 {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\tif insts[3].Op != vm.OP_CHECKPREDICATE {\n") - fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") - fmt.Fprintf(buf, "\t}\n") - fmt.Fprintf(buf, "\treturn result, nil\n") - fmt.Fprintf(buf, "}\n\n") - - // TODO(bobg): RedeemFoo_Bar functions for marshaling the args to - // the Bar clause of contract Foo. - } - - fmt.Printf("import (\n") - for imp := range imports { - fmt.Printf("\t\"%s\"\n", imp) - } - fmt.Printf(")\n\n") - - os.Stdout.Write(buf.Bytes()) -} - -func paramsStr(params []*compiler.Param) string { - var strs []string - for _, p := range params { - strs = append(strs, fmt.Sprintf("%s: %s", p.Name, p.Type)) - } - return strings.Join(strs, ", ") -} - -func asGoParams(params []*compiler.Param) (goParams string, imports []string) { - var strs []string - for _, p := range params { - var typ string - switch p.Type { - case "Amount": - typ = "uint64" - case "Asset": - typ = "bc.AssetId" - imports = append(imports, "chain/protocol/bc") - case "Boolean": - typ = "bool" - case "Hash": - typ = "[]byte" - case "Integer": - typ = "int64" - case "Program": - typ = "[]byte" - case "PublicKey": - typ = "ed25519.PublicKey" - imports = append(imports, "chain/crypto/ed25519") - case "Signature": - typ = "[]byte" - case "String": - typ = "[]byte" - case "Time": - typ = "time.Time" - imports = append(imports, "time") - } - strs = append(strs, fmt.Sprintf("%s %s", p.Name, typ)) - } - return strings.Join(strs, ", "), imports -} diff --git a/exp/ivy/compiler/compile.go b/exp/ivy/compiler/compile.go deleted file mode 100644 index f55ff722..00000000 --- a/exp/ivy/compiler/compile.go +++ /dev/null @@ -1,756 +0,0 @@ -package compiler - -import ( - "encoding/json" - "fmt" - "io" - "io/ioutil" - - chainjson "github.com/bytom/encoding/json" - "github.com/bytom/errors" - "github.com/bytom/protocol/vm" - "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 Ivy 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 -// lists of arguments with which to instantiate them as programs, with -// the results placed in the contract's Program field. A contract -// named in argMap but not found in the input is silently ignored. -func Compile(r io.Reader) ([]*Contract, error) { - inp, err := ioutil.ReadAll(r) - if err != nil { - return nil, errors.Wrap(err, "reading input") - } - contracts, err := parse(inp) - if err != nil { - return nil, errors.Wrap(err, "parse error") - } - - globalEnv := newEnviron(nil) - for _, k := range keywords { - globalEnv.add(k, nilType, roleKeyword) - } - for _, b := range builtins { - globalEnv.add(b.name, nilType, roleBuiltin) - } - - // All contracts must be checked for recursiveness before any are - // compiled. - for _, contract := range contracts { - contract.Recursive = checkRecursive(contract) - } - - for _, contract := range contracts { - err = globalEnv.addContract(contract) - if err != nil { - return nil, err - } - } - - for _, contract := range contracts { - err = compileContract(contract, globalEnv) - 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{ - Name: s.locked.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} - clause.Values = append(clause.Values, valueInfo) - } - } - } - } - - return contracts, nil -} - -func Instantiate(body []byte, params []*Param, recursive bool, args []ContractArg) ([]byte, error) { - if len(args) != len(params) { - return nil, fmt.Errorf("got %d argument(s), want %d", len(args), len(params)) - } - - // typecheck args against param types - for i, param := range params { - arg := args[i] - switch param.Type { - case amountType, intType, timeType: - if arg.I == nil { - return nil, fmt.Errorf("type mismatch in arg %d (want integer)", i) - } - case assetType, hashType, progType, pubkeyType, sigType, strType: - if arg.S == nil { - return nil, fmt.Errorf("type mismatch in arg %d (want string)", i) - } - case boolType: - if arg.B == nil { - return nil, fmt.Errorf("type mismatch in arg %d (want boolean)", i) - } - } - } - - b := vmutil.NewBuilder() - - for i := len(args) - 1; i >= 0; i-- { - a := args[i] - switch { - case a.B != nil: - var n int64 - if *a.B { - n = 1 - } - b.AddInt64(n) - case a.I != nil: - b.AddInt64(*a.I) - case a.S != nil: - b.AddData(*a.S) - } - } - - if recursive { - // ... DEPTH OVER 0 CHECKPREDICATE - b.AddData(body) - b.AddOp(vm.OP_DEPTH).AddOp(vm.OP_OVER) - } else { - // ... DEPTH 0 CHECKPREDICATE - b.AddOp(vm.OP_DEPTH) - b.AddData(body) - } - b.AddInt64(0) - b.AddOp(vm.OP_CHECKPREDICATE) - return b.Build() -} - -func compileContract(contract *Contract, globalEnv *environ) error { - var err error - - if len(contract.Clauses) == 0 { - return fmt.Errorf("empty contract") - } - env := newEnviron(globalEnv) - for _, p := range contract.Params { - err = env.add(p.Name, p.Type, roleContractParam) - if err != nil { - return err - } - } - err = env.add(contract.Value, valueType, roleContractValue) - if err != nil { - return err - } - for _, c := range contract.Clauses { - err = env.add(c.Name, nilType, roleClause) - if err != nil { - return err - } - } - - err = prohibitValueParams(contract) - if err != nil { - return err - } - err = prohibitSigParams(contract) - if err != nil { - return err - } - err = requireAllParamsUsedInClauses(contract.Params, contract.Clauses) - if err != nil { - return err - } - - var stk stack - - if len(contract.Clauses) > 1 { - stk = stk.add("") - } - - for i := len(contract.Params) - 1; i >= 0; i-- { - p := contract.Params[i] - stk = stk.add(p.Name) - } - - if contract.Recursive { - stk = stk.add(contract.Name) - } - - b := &builder{} - - if len(contract.Clauses) == 1 { - err = compileClause(b, stk, contract, env, contract.Clauses[0]) - if err != nil { - return err - } - } else { - if len(contract.Params) > 0 { - // A clause selector is at the bottom of the stack. Roll it to the - // top. - n := len(contract.Params) - if contract.Recursive { - n++ - } - stk = b.addRoll(stk, n) // stack: [ [] ] - } - - var stk2 stack - - // clauses 2..N-1 - for i := len(contract.Clauses) - 1; i >= 2; i-- { - stk = b.addDup(stk) // stack: [... ] - stk = b.addInt64(stk, int64(i)) // stack: [... ] - stk = b.addNumEqual(stk, fmt.Sprintf("( == %d)", i)) // stack: [... ] - stk = b.addJumpIf(stk, contract.Clauses[i].Name) // stack: [... ] - stk2 = stk // stack starts here for clauses 2 through N-1 - } - - // clause 1 - stk = b.addJumpIf(stk, contract.Clauses[1].Name) // consumes the clause selector - - // no jump needed for clause 0 - - for i, clause := range contract.Clauses { - if i > 1 { - // Clauses 0 and 1 have no clause selector on top of the - // stack. Clauses 2 and later do. - stk = stk2 - } - - b.addJumpTarget(stk, clause.Name) - - if i > 1 { - stk = b.addDrop(stk) - } - - err = compileClause(b, stk, contract, env, clause) - if err != nil { - return errors.Wrapf(err, "compiling clause \"%s\"", clause.Name) - } - b.forgetPendingVerify() - if i < len(contract.Clauses)-1 { - b.addJump(stk, "_end") - } - } - b.addJumpTarget(stk, "_end") - } - - opcodes := optimize(b.opcodes()) - prog, err := vm.Assemble(opcodes) - if err != nil { - return err - } - - contract.Body = prog - contract.Opcodes = opcodes - - contract.Steps = b.steps() - - return nil -} - -func compileClause(b *builder, contractStk stack, contract *Contract, env *environ, clause *Clause) error { - var err error - - // copy env to leave outerEnv unchanged - env = newEnviron(env) - for _, p := range clause.Params { - err = env.add(p.Name, p.Type, roleClauseParam) - if err != nil { - 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 - for _, p := range clause.Params { - // NOTE: the order of clause params is not reversed, unlike - // contract params (and also unlike the arguments to Ivy - // function-calls). - stk = stk.add(p.Name) - } - stk = stk.addFromStack(contractStk) - - // 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) - } - - for _, s := range clause.statements { - switch stmt := s.(type) { - case *verifyStatement: - stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.expr) - if err != nil { - return errors.Wrapf(err, "in verify statement in clause \"%s\"", clause.Name) - } - stk = b.addVerify(stk) - - // special-case reporting of certain function calls - if c, ok := stmt.expr.(*callExpr); ok && len(c.args) == 1 { - if b := referencedBuiltin(c.fn); b != nil { - switch b.name { - case "before": - clause.MaxTimes = append(clause.MaxTimes, c.args[0].String()) - case "after": - clause.MinTimes = append(clause.MinTimes, c.args[0].String()) - } - } - } - - case *lockStatement: - // index - stk = b.addInt64(stk, stmt.index) - - // refdatahash - stk = b.addData(stk, nil) - - // 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) - } else { - var req *ClauseReq - for _, r := range clause.Reqs { - if stmt.locked.String() == r.Name { - req = r - break - } - } - if req == nil { - return fmt.Errorf("unknown value \"%s\" in lock statement in clause \"%s\"", stmt.locked, clause.Name) - } - - // amount - stk, err = compileExpr(b, stk, contract, clause, env, counts, req.amountExpr) - 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) - if err != nil { - return errors.Wrapf(err, "in lock statement in clause \"%s\"", clause.Name) - } - } - - // version - stk = b.addInt64(stk, 1) - - // prog - stk, err = compileExpr(b, stk, contract, clause, env, counts, stmt.program) - if err != nil { - 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.addVerify(stk) - - case *unlockStatement: - if len(clause.statements) == 1 { - // This is the only statement in the clause, make sure TRUE is - // on the stack. - stk = b.addBoolean(stk, true) - } - } - } - - err = requireAllValuesDisposedOnce(contract, clause) - if err != nil { - return err - } - err = typeCheckClause(contract, clause, env) - if err != nil { - return err - } - err = requireAllParamsUsedInClause(clause.Params, clause) - if err != nil { - return err - } - - return nil -} - -func compileExpr(b *builder, stk stack, contract *Contract, clause *Clause, env *environ, counts map[string]int, expr expression) (stack, error) { - var err error - - switch e := expr.(type) { - case *binaryExpr: - // Do typechecking after compiling subexpressions (because other - // compilation errors are more interesting than type mismatch - // errors). - - stk, err = compileExpr(b, stk, contract, clause, env, counts, e.left) - if err != nil { - return stk, errors.Wrapf(err, "in left operand of \"%s\" expression", e.op.op) - } - stk, err = compileExpr(b, stk, contract, clause, env, counts, e.right) - if err != nil { - return stk, errors.Wrapf(err, "in right operand of \"%s\" expression", e.op.op) - } - - lType := e.left.typ(env) - if e.op.left != "" && lType != e.op.left { - 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 { - return stk, fmt.Errorf("in \"%s\", right operand has type \"%s\", must be \"%s\"", e, rType, e.op.right) - } - - switch e.op.op { - case "==", "!=": - if lType != rType { - // Maybe one is Hash and the other is (more-specific-Hash subtype). - // TODO(bobg): generalize this mechanism - if lType == hashType && isHashSubtype(rType) { - propagateType(contract, clause, env, rType, e.left) - } else if rType == hashType && isHashSubtype(lType) { - propagateType(contract, clause, env, lType, e.right) - } else { - return stk, fmt.Errorf("type mismatch in \"%s\": left operand has type \"%s\", right operand has type \"%s\"", e, lType, rType) - } - } - if lType == "Boolean" { - return stk, fmt.Errorf("in \"%s\": using \"%s\" on Boolean values not allowed", e, e.op.op) - } - } - - stk = b.addOps(stk.dropN(2), e.op.opcodes, e.String()) - - case *unaryExpr: - // Do typechecking after compiling subexpression (because other - // compilation errors are more interesting than type mismatch - // errors). - - var err error - stk, err = compileExpr(b, stk, contract, clause, env, counts, e.expr) - if err != nil { - return stk, errors.Wrapf(err, "in \"%s\" expression", e.op.op) - } - - if e.op.operand != "" && e.expr.typ(env) != e.op.operand { - return stk, fmt.Errorf("in \"%s\", operand has type \"%s\", must be \"%s\"", e, e.expr.typ(env), e.op.operand) - } - b.addOps(stk.drop(), e.op.opcodes, e.String()) - - case *callExpr: - bi := referencedBuiltin(e.fn) - if bi == nil { - if v, ok := e.fn.(varRef); ok { - if entry := env.lookup(string(v)); entry != nil && entry.t == contractType { - clause.Contracts = append(clause.Contracts, entry.c.Name) - - partialName := fmt.Sprintf("%s(...)", v) - stk = b.addData(stk, nil) - - if len(e.args) != len(entry.c.Params) { - return stk, fmt.Errorf("contract \"%s\" expects %d argument(s), got %d", entry.c.Name, len(entry.c.Params), len(e.args)) - } - - for i := len(e.args) - 1; i >= 0; i-- { - arg := e.args[i] - if entry.c.Params[i].Type != "" && arg.typ(env) != entry.c.Params[i].Type { - return stk, fmt.Errorf("argument %d to contract \"%s\" has type \"%s\", must be \"%s\"", i, entry.c.Name, arg.typ(env), entry.c.Params[i].Type) - } - stk, err = compileExpr(b, stk, contract, clause, env, counts, arg) - if err != nil { - return stk, err - } - stk = b.addCatPushdata(stk, partialName) - } - - switch { - case entry.c == contract: - // Recursive call - cannot use entry.c.Body - // ... DEPTH OVER 0 CHECKPREDICATE - stk, err = compileRef(b, stk, counts, varRef(contract.Name)) - if err != nil { - return stk, errors.Wrap(err, "compiling contract call") - } - stk = b.addCatPushdata(stk, partialName) - stk = b.addData(stk, []byte{byte(vm.OP_DEPTH), byte(vm.OP_OVER)}) - stk = b.addCat(stk, partialName) - - case entry.c.Recursive: - // Non-recursive call to a (different) recursive contract - // ... DEPTH OVER 0 CHECKPREDICATE - if len(entry.c.Body) == 0 { - // TODO(bobg): sort input contracts topologically to permit forward calling - return stk, fmt.Errorf("contract \"%s\" not defined", entry.c.Name) - } - stk = b.addData(stk, entry.c.Body) - stk = b.addCatPushdata(stk, partialName) - stk = b.addData(stk, []byte{byte(vm.OP_DEPTH), byte(vm.OP_OVER)}) - stk = b.addCat(stk, partialName) - - default: - // Non-recursive call to non-recursive contract - // ... DEPTH 0 CHECKPREDICATE - stk = b.addData(stk, []byte{byte(vm.OP_DEPTH)}) - stk = b.addCat(stk, partialName) - if len(entry.c.Body) == 0 { - // TODO(bobg): sort input contracts topologically to permit forward calling - return stk, fmt.Errorf("contract \"%s\" not defined", entry.c.Name) - } - stk = b.addData(stk, entry.c.Body) - stk = b.addCatPushdata(stk, partialName) - } - stk = b.addData(stk, vm.Int64Bytes(0)) - stk = b.addCatPushdata(stk, partialName) - stk = b.addData(stk, []byte{byte(vm.OP_CHECKPREDICATE)}) - stk = b.addCat(stk, e.String()) - - return stk, nil - } - } - return stk, fmt.Errorf("unknown function \"%s\"", e.fn) - } - - if len(e.args) != len(bi.args) { - return stk, fmt.Errorf("wrong number of args for \"%s\": have %d, want %d", bi.name, len(e.args), len(bi.args)) - } - - // WARNING WARNING WOOP WOOP - // special-case hack - // WARNING WARNING WOOP WOOP - if bi.name == "checkTxMultiSig" { - if _, ok := e.args[0].(listExpr); !ok { - return stk, fmt.Errorf("checkTxMultiSig expects list literals, got %T for argument 0", e.args[0]) - } - if _, ok := e.args[1].(listExpr); !ok { - return stk, fmt.Errorf("checkTxMultiSig expects list literals, got %T for argument 1", e.args[1]) - } - - var k1, k2 int - - stk, k1, err = compileArg(b, stk, contract, clause, env, counts, e.args[1]) - if err != nil { - return stk, err - } - - // stack: [... sigM ... sig1 M] - - var altEntry string - stk, altEntry = b.addToAltStack(stk) // stack: [... sigM ... sig1] - stk = b.addTxSigHash(stk) // stack: [... sigM ... sig1 txsighash] - - stk, k2, err = compileArg(b, stk, contract, clause, env, counts, e.args[0]) - if err != nil { - return stk, err - } - - // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 N] - - stk = b.addFromAltStack(stk, altEntry) // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 N M] - stk = b.addSwap(stk) // stack: [... sigM ... sig1 txsighash pubkeyN ... pubkey1 M N] - stk = b.addCheckMultisig(stk, k1+k2, e.String()) - - return stk, nil - } - - var k int - - for i := len(e.args) - 1; i >= 0; i-- { - a := e.args[i] - var k2 int - var err error - stk, k2, err = compileArg(b, stk, contract, clause, env, counts, a) - if err != nil { - return stk, errors.Wrapf(err, "compiling argument %d in call expression", i) - } - k += k2 - } - - // Do typechecking after compiling subexpressions (because other - // compilation errors are more interesting than type mismatch - // errors). - for i, actual := range e.args { - if bi.args[i] != "" && actual.typ(env) != bi.args[i] { - return stk, fmt.Errorf("argument %d to \"%s\" has type \"%s\", must be \"%s\"", i, bi.name, actual.typ(env), bi.args[i]) - } - } - - stk = b.addOps(stk.dropN(k), bi.opcodes, e.String()) - - // special-case reporting - switch bi.name { - case "sha3", "sha256": - clause.HashCalls = append(clause.HashCalls, HashCall{bi.name, e.args[0].String(), string(e.args[0].typ(env))}) - } - - case varRef: - return compileRef(b, stk, counts, e) - - case integerLiteral: - stk = b.addInt64(stk, int64(e)) - - case bytesLiteral: - stk = b.addData(stk, []byte(e)) - - case booleanLiteral: - stk = b.addBoolean(stk, bool(e)) - - case listExpr: - // Lists are excluded here because they disobey the invariant of - // this function: namely, that it increases the stack size by - // exactly one. (A list pushes its items and its length on the - // stack.) But they're OK as function-call arguments because the - // function (presumably) consumes all the stack items added. - return stk, fmt.Errorf("encountered list outside of function-call context") - } - return stk, nil -} - -func compileArg(b *builder, stk stack, contract *Contract, clause *Clause, env *environ, counts map[string]int, expr expression) (stack, int, error) { - var n int - if list, ok := expr.(listExpr); ok { - for i := 0; i < len(list); i++ { - elt := list[len(list)-i-1] - var err error - stk, err = compileExpr(b, stk, contract, clause, env, counts, elt) - if err != nil { - return stk, 0, err - } - n++ - } - stk = b.addInt64(stk, int64(len(list))) - n++ - return stk, n, nil - } - var err error - stk, err = compileExpr(b, stk, contract, clause, env, counts, expr) - return stk, 1, err -} - -func compileRef(b *builder, stk stack, counts map[string]int, ref varRef) (stack, error) { - depth := stk.find(string(ref)) - if depth < 0 { - return stk, fmt.Errorf("undefined reference: \"%s\"", ref) - } - - var isFinal bool - if count, ok := counts[string(ref)]; ok && count > 0 { - count-- - counts[string(ref)] = count - isFinal = count == 0 - } - - switch depth { - case 0: - if !isFinal { - stk = b.addDup(stk) - } - case 1: - if isFinal { - stk = b.addSwap(stk) - } else { - stk = b.addOver(stk) - } - default: - if isFinal { - stk = b.addRoll(stk, depth) - } else { - stk = b.addPick(stk, depth) - } - } - return stk, nil -} - -func (a *ContractArg) UnmarshalJSON(b []byte) error { - var m map[string]json.RawMessage - err := json.Unmarshal(b, &m) - if err != nil { - return err - } - if r, ok := m["boolean"]; ok { - var bval bool - err = json.Unmarshal(r, &bval) - if err != nil { - return err - } - a.B = &bval - return nil - } - if r, ok := m["integer"]; ok { - var ival int64 - err = json.Unmarshal(r, &ival) - if err != nil { - return err - } - a.I = &ival - return nil - } - r, ok := m["string"] - if !ok { - return fmt.Errorf("contract arg must define one of boolean, integer, string") - } - var sval chainjson.HexBytes - err = json.Unmarshal(r, &sval) - if err != nil { - return err - } - a.S = &sval - return nil -} diff --git a/exp/ivy/compiler/compile_test.go b/exp/ivy/compiler/compile_test.go deleted file mode 100644 index 6780df62..00000000 --- a/exp/ivy/compiler/compile_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package compiler - -import ( - "encoding/hex" - "encoding/json" - "strings" - "testing" - - "github.com/bytom/exp/ivy/compiler/ivytest" -) - -func TestCompile(t *testing.T) { - cases := []struct { - name string - contract string - wantJSON string - }{ - { - "TrivialLock", - ivytest.TrivialLock, - `[{"name":"TrivialLock","clauses":[{"name":"trivialUnlock","values":[{"name":"locked"}]}],"value":"locked","body_bytecode":"51","body_opcodes":"TRUE","recursive":false}]`, - }, - { - "LockWithPublicKey", - ivytest.LockWithPublicKey, - `[{"name":"LockWithPublicKey","params":[{"name":"publicKey","declared_type":"PublicKey"}],"clauses":[{"name":"unlockWithSig","params":[{"name":"sig","declared_type":"Signature"}],"values":[{"name":"locked"}]}],"value":"locked","body_bytecode":"ae7cac","body_opcodes":"TXSIGHASH SWAP CHECKSIG","recursive":false}]`, - }, - { - "LockWithPublicKeyHash", - ivytest.LockWithPKHash, - `[{"name":"LockWithPublicKeyHash","params":[{"name":"pubKeyHash","declared_type":"Hash","inferred_type":"Sha3(PublicKey)"}],"clauses":[{"name":"spend","params":[{"name":"pubKey","declared_type":"PublicKey"},{"name":"sig","declared_type":"Signature"}],"hash_calls":[{"hash_type":"sha3","arg":"pubKey","arg_type":"PublicKey"}],"values":[{"name":"value"}]}],"value":"value","body_bytecode":"5279aa887cae7cac","body_opcodes":"2 PICK SHA3 EQUALVERIFY SWAP TXSIGHASH SWAP CHECKSIG","recursive":false}]`, - }, - { - "LockWith2of3Keys", - ivytest.LockWith2of3Keys, - `[{"name":"LockWith3Keys","params":[{"name":"pubkey1","declared_type":"PublicKey"},{"name":"pubkey2","declared_type":"PublicKey"},{"name":"pubkey3","declared_type":"PublicKey"}],"clauses":[{"name":"unlockWith2Sigs","params":[{"name":"sig1","declared_type":"Signature"},{"name":"sig2","declared_type":"Signature"}],"values":[{"name":"locked"}]}],"value":"locked","body_bytecode":"537a547a526bae71557a536c7cad","body_opcodes":"3 ROLL 4 ROLL 2 TOALTSTACK TXSIGHASH 2ROT 5 ROLL 3 FROMALTSTACK SWAP CHECKMULTISIG","recursive":false}]`, - }, - { - "LockToOutput", - ivytest.LockToOutput, - `[{"name":"LockToOutput","params":[{"name":"address","declared_type":"Program"}],"clauses":[{"name":"relock","values":[{"name":"locked","program":"address"}]}],"value":"locked","body_bytecode":"0000c3c251557ac1","body_opcodes":"0 0 AMOUNT ASSET 1 5 ROLL CHECKOUTPUT","recursive":false}]`, - }, - { - "TradeOffer", - ivytest.TradeOffer, - `[{"name":"TradeOffer","params":[{"name":"requestedAsset","declared_type":"Asset"},{"name":"requestedAmount","declared_type":"Amount"},{"name":"sellerProgram","declared_type":"Program"},{"name":"sellerKey","declared_type":"PublicKey"}],"clauses":[{"name":"trade","reqs":[{"name":"payment","asset":"requestedAsset","amount":"requestedAmount"}],"values":[{"name":"payment","program":"sellerProgram","asset":"requestedAsset","amount":"requestedAmount"},{"name":"offered"}]},{"name":"cancel","params":[{"name":"sellerSig","declared_type":"Signature"}],"values":[{"name":"offered","program":"sellerProgram"}]}],"value":"offered","body_bytecode":"547a641300000000007251557ac16323000000547a547aae7cac690000c3c251577ac1","body_opcodes":"4 ROLL JUMPIF:$cancel $trade 0 0 2SWAP 1 5 ROLL CHECKOUTPUT JUMP:$_end $cancel 4 ROLL 4 ROLL TXSIGHASH SWAP CHECKSIG VERIFY 0 0 AMOUNT ASSET 1 7 ROLL CHECKOUTPUT $_end","recursive":false}]`, - }, - { - "EscrowedTransfer", - ivytest.EscrowedTransfer, - `[{"name":"EscrowedTransfer","params":[{"name":"agent","declared_type":"PublicKey"},{"name":"sender","declared_type":"Program"},{"name":"recipient","declared_type":"Program"}],"clauses":[{"name":"approve","params":[{"name":"sig","declared_type":"Signature"}],"values":[{"name":"value","program":"recipient"}]},{"name":"reject","params":[{"name":"sig","declared_type":"Signature"}],"values":[{"name":"value","program":"sender"}]}],"value":"value","body_bytecode":"537a641b000000537a7cae7cac690000c3c251567ac1632a000000537a7cae7cac690000c3c251557ac1","body_opcodes":"3 ROLL JUMPIF:$reject $approve 3 ROLL SWAP TXSIGHASH SWAP CHECKSIG VERIFY 0 0 AMOUNT ASSET 1 6 ROLL CHECKOUTPUT JUMP:$_end $reject 3 ROLL SWAP TXSIGHASH SWAP CHECKSIG VERIFY 0 0 AMOUNT ASSET 1 5 ROLL CHECKOUTPUT $_end","recursive":false}]`, - }, - { - "RevealPreimage", - ivytest.RevealPreimage, - `[{"name":"RevealPreimage","params":[{"name":"hash","declared_type":"Hash","inferred_type":"Sha3(String)"}],"clauses":[{"name":"reveal","params":[{"name":"string","declared_type":"String"}],"hash_calls":[{"hash_type":"sha3","arg":"string","arg_type":"String"}],"values":[{"name":"value"}]}],"value":"value","body_bytecode":"7caa87","body_opcodes":"SWAP SHA3 EQUAL","recursive":false}]`, - }, - { - "PriceChanger", - ivytest.PriceChanger, - `[{"name":"PriceChanger","params":[{"name":"askAmount","declared_type":"Amount"},{"name":"askAsset","declared_type":"Asset"},{"name":"sellerKey","declared_type":"PublicKey"},{"name":"sellerProg","declared_type":"Program"}],"clauses":[{"name":"changePrice","params":[{"name":"newAmount","declared_type":"Amount"},{"name":"newAsset","declared_type":"Asset"},{"name":"sig","declared_type":"Signature"}],"values":[{"name":"offered","program":"PriceChanger(newAmount, newAsset, sellerKey, sellerProg)"}],"contracts":["PriceChanger"]},{"name":"redeem","reqs":[{"name":"payment","asset":"askAsset","amount":"askAmount"}],"values":[{"name":"payment","program":"sellerProg","asset":"askAsset","amount":"askAmount"},{"name":"offered"}]}],"value":"offered","body_bytecode":"557a6433000000557a5479ae7cac690000c3c251005a7a89597a89597a89597a89567a890274787e008901c07ec1633d0000000000537a547a51577ac1","body_opcodes":"5 ROLL JUMPIF:$redeem $changePrice 5 ROLL 4 PICK TXSIGHASH SWAP CHECKSIG VERIFY 0 0 AMOUNT ASSET 1 0 10 ROLL CATPUSHDATA 9 ROLL CATPUSHDATA 9 ROLL CATPUSHDATA 9 ROLL CATPUSHDATA 6 ROLL CATPUSHDATA 0x7478 CAT 0 CATPUSHDATA 192 CAT CHECKOUTPUT JUMP:$_end $redeem 0 0 3 ROLL 4 ROLL 1 7 ROLL CHECKOUTPUT $_end","recursive":true}]`, - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - r := strings.NewReader(c.contract) - got, err := Compile(r) - if err != nil { - t.Fatal(err) - } - gotJSON, _ := json.Marshal(got) - if string(gotJSON) != c.wantJSON { - t.Errorf("\ngot %s\nwant %s", string(gotJSON), c.wantJSON) - } else { - for _, contract := range got { - t.Log(contract.Opcodes) - } - } - }) - } -} - -func mustDecodeHex(h string) []byte { - bits, err := hex.DecodeString(h) - if err != nil { - panic(err) - } - return bits -} diff --git a/exp/ivy/compiler/doc.go b/exp/ivy/compiler/doc.go deleted file mode 100644 index 5a1736a6..00000000 --- a/exp/ivy/compiler/doc.go +++ /dev/null @@ -1,126 +0,0 @@ -/* -Package ivy provides a compiler for Chain's Ivy contract language. - -A contract is a means to lock some payment in the output of a -transaction. It contains a number of clauses, each describing a way to -unlock, or redeem, the payment in a subsequent transaction. By -executing the statements in a clause, using contract arguments -supplied by the payer and clause arguments supplied by the redeemer, -nodes in a Chain network can determine whether a proposed spend is -valid. - -The language definition is in flux, but here's what's implemented as -of late May 2017. - - program = contract* - - contract = "contract" identifier "(" [params] ")" "locks" identifier "{" clause+ "}" - - The identifier after "locks" is a name for the value locked by - the contract. It must be unlocked or re-locked (with "unlock" - or "lock") in every clause. - - clause = "clause" identifier "(" [params] ")" ["requires" requirements] "{" statement+ "}" - - The requirements are blockchain values that must be present in - the spending transaction in order to spend the value locked by - the earlier transaction. Each such value must be re-locked - (with "lock") in its clause. - - statement = verify | unlock | lock - - verify = "verify" expr - - Verifies that boolean expression expr produces a true result. - - unlock = "unlock" expr - - Expr must evaluate to the contract value. This unlocks that - value for any use. - - lock = "lock" expr "with" expr - - The first expr must be a blockchain value (i.e., one named - with "locks" or "requires"). The second expr must be a - program. This unlocks expr and re-locks it with the new - program. - - requirements = requirement | requirements "," requirement - - requirement = identifier ":" expr "of" expr - - The first expr must be an amount, the second must be an - asset. This denotes that the named value must have the given - quantity and asset type. - - params = param | params "," param - - param = idlist ":" identifier - - The identifiers in idlist are individual parameter names. The - identifier after the colon is their type. Available types are: - - Amount; Asset; Boolean; Hash; Integer; Program; PublicKey; - Signature; String; Time - - idlist = identifier | idlist "," identifier - - expr = unary_expr | binary_expr | call_expr | identifier | "(" expr ")" | literal - - unary_expr = unary_op expr - - binary_expr = expr binary_op expr - - call_expr = expr "(" [args] ")" - - If expr is the name of an Ivy contract, then calling it (with - the appropriate arguments) produces a program suitable for use - in "lock" statements. - - Otherwise, expr should be one of these builtin functions: - - sha3(x) - SHA3-256 hash of x. - sha256(x) - SHA-256 hash of x. - size(x) - Size in bytes of x. - abs(x) - Absolute value of x. - min(x, y) - The lesser of x and y. - max(x, y) - The greater of x and y. - checkTxSig(pubkey, signature) - Whether signature matches both the spending - transaction and pubkey. - concat(x, y) - The concatenation of x and y. - concatpush(x, y) - The concatenation of x with the bytecode sequence - needed to push y on the ChainVM stack. - before(x) - Whether the spending transaction is happening before - time x. - after(x) - Whether the spending transaction is happening after - time x. - checkTxMultiSig([pubkey1, pubkey2, ...], [sig1, sig2, ...]) - Like checkTxSig, but for M-of-N signature checks. - Every sig must match both the spending transaction and - one of the pubkeys. There may be more pubkeys than - sigs, but they are only checked left-to-right so must - be supplied in the same order as the sigs. The square - brackets here are literal and must appear as shown. - - unary_op = "-" | "~" - - binary_op = ">" | "<" | ">=" | "<=" | "==" | "!=" | "^" | "|" | - "+" | "-" | "&" | "<<" | ">>" | "%" | "*" | "/" - - args = expr | args "," expr - - literal = int_literal | str_literal | hex_literal - -*/ -package compiler diff --git a/exp/ivy/compiler/environ.go b/exp/ivy/compiler/environ.go deleted file mode 100644 index 07c4645e..00000000 --- a/exp/ivy/compiler/environ.go +++ /dev/null @@ -1,72 +0,0 @@ -package compiler - -import "fmt" - -// name-binding environment -type environ struct { - entries map[string]*envEntry - parent *environ -} - -type envEntry struct { - t typeDesc - r role - c *Contract // if t == contractType -} - -type role int - -const ( - roleKeyword role = 1 + iota - roleBuiltin - roleContract - roleContractParam - roleContractValue - roleClause - roleClauseParam - roleClauseValue -) - -var roleDesc = map[role]string{ - roleKeyword: "keyword", - roleBuiltin: "built-in function", - roleContract: "contract", - roleContractParam: "contract parameter", - roleContractValue: "contract value", - roleClause: "clause", - roleClauseParam: "clause parameter", - roleClauseValue: "clause value", -} - -func newEnviron(parent *environ) *environ { - return &environ{ - entries: make(map[string]*envEntry), - parent: parent, - } -} - -func (e *environ) add(name string, t typeDesc, r role) error { - if entry := e.lookup(name); entry != nil { - return fmt.Errorf("%s \"%s\" conflicts with %s", roleDesc[r], name, roleDesc[entry.r]) - } - e.entries[name] = &envEntry{t: t, r: r} - return nil -} - -func (e *environ) addContract(contract *Contract) error { - if entry := e.lookup(contract.Name); entry != nil { - return fmt.Errorf("%s \"%s\" conflicts with %s", roleDesc[roleContract], contract.Name, roleDesc[entry.r]) - } - e.entries[contract.Name] = &envEntry{t: contractType, r: roleContract, c: contract} - return nil -} - -func (e environ) lookup(name string) *envEntry { - if res, ok := e.entries[name]; ok { - return res - } - if e.parent != nil { - return e.parent.lookup(name) - } - return nil -} diff --git a/exp/ivy/compiler/ivytest/ivytest.go b/exp/ivy/compiler/ivytest/ivytest.go deleted file mode 100644 index 55888a14..00000000 --- a/exp/ivy/compiler/ivytest/ivytest.go +++ /dev/null @@ -1,155 +0,0 @@ -package ivytest - -const TrivialLock = ` -contract TrivialLock() locks locked { - clause trivialUnlock() { - unlock locked - } -} -` - -const LockWithPublicKey = ` -contract LockWithPublicKey(publicKey: PublicKey) locks locked { - clause unlockWithSig(sig: Signature) { - verify checkTxSig(publicKey, sig) - unlock locked - } -} -` - -const LockWithPKHash = ` -contract LockWithPublicKeyHash(pubKeyHash: Hash) locks value { - clause spend(pubKey: PublicKey, sig: Signature) { - verify sha3(pubKey) == pubKeyHash - verify checkTxSig(pubKey, sig) - unlock value - } -} -` - -const LockWith2of3Keys = ` -contract LockWith3Keys(pubkey1, pubkey2, pubkey3: PublicKey) locks locked { - clause unlockWith2Sigs(sig1, sig2: Signature) { - verify checkTxMultiSig([pubkey1, pubkey2, pubkey3], [sig1, sig2]) - unlock locked - } -} -` - -const LockToOutput = ` -contract LockToOutput(address: Program) locks locked { - clause relock() { - lock locked with address - } -} -` - -const TradeOffer = ` -contract TradeOffer(requestedAsset: Asset, requestedAmount: Amount, sellerProgram: Program, sellerKey: PublicKey) locks offered { - clause trade() requires payment: requestedAmount of requestedAsset { - lock payment with sellerProgram - unlock offered - } - clause cancel(sellerSig: Signature) { - verify checkTxSig(sellerKey, sellerSig) - lock offered with sellerProgram - } -} -` - -const EscrowedTransfer = ` -contract EscrowedTransfer(agent: PublicKey, sender: Program, recipient: Program) locks value { - clause approve(sig: Signature) { - verify checkTxSig(agent, sig) - lock value with recipient - } - clause reject(sig: Signature) { - verify checkTxSig(agent, sig) - lock value with sender - } -} -` - -const CollateralizedLoan = ` -contract CollateralizedLoan(balanceAsset: Asset, balanceAmount: Amount, deadline: Time, lender: Program, borrower: Program) locks collateral { - clause repay() requires payment: balanceAmount of balanceAsset { - lock payment with lender - lock collateral with borrower - } - clause default() { - verify after(deadline) - lock collateral with lender - } -} -` - -const RevealPreimage = ` -contract RevealPreimage(hash: Hash) locks value { - clause reveal(string: String) { - verify sha3(string) == hash - unlock value - } -} -` - -const PriceChanger = ` -contract PriceChanger(askAmount: Amount, askAsset: Asset, sellerKey: PublicKey, sellerProg: Program) locks offered { - clause changePrice(newAmount: Amount, newAsset: Asset, sig: Signature) { - verify checkTxSig(sellerKey, sig) - lock offered with PriceChanger(newAmount, newAsset, sellerKey, sellerProg) - } - clause redeem() requires payment: askAmount of askAsset { - lock payment with sellerProg - unlock offered - } -} -` - -const CallOptionWithSettlement = ` -contract CallOptionWithSettlement(strikePrice: Amount, - strikeCurrency: Asset, - sellerProgram: Program, - sellerKey: PublicKey, - buyerKey: PublicKey, - deadline: Time) locks underlying { - clause exercise(buyerSig: Signature) - requires payment: strikePrice of strikeCurrency { - verify before(deadline) - verify checkTxSig(buyerKey, buyerSig) - lock payment with sellerProgram - unlock underlying - } - clause expire() { - verify after(deadline) - lock underlying with sellerProgram - } - clause settle(sellerSig: Signature, buyerSig: Signature) { - verify checkTxSig(sellerKey, sellerSig) - verify checkTxSig(buyerKey, buyerSig) - unlock underlying - } -} -` - -const OneTwo = ` -contract Two(b, c: Program, expirationTime: Time) locks value { - clause redeem() { - verify before(expirationTime) - lock value with b - } - clause default() { - verify after(expirationTime) - lock value with c - } -} -contract One(a, b, c: Program, switchTime, expirationTime: Time) locks value { - clause redeem() { - verify before(switchTime) - lock value with a - } - clause switch() { - verify after(switchTime) - lock value with Two(b, c, expirationTime) - } -} -` diff --git a/exp/ivy/compiler/optimize.go b/exp/ivy/compiler/optimize.go deleted file mode 100644 index f05cac4b..00000000 --- a/exp/ivy/compiler/optimize.go +++ /dev/null @@ -1,64 +0,0 @@ -package compiler - -import "strings" - -var optimizations = []struct { - before, after string -}{ - {"0 ROLL", ""}, - {"0 PICK", "DUP"}, - {"1 ROLL", "SWAP"}, - {"1 PICK", "OVER"}, - {"2 ROLL", "ROT"}, - {"TRUE VERIFY", ""}, - {"SWAP SWAP", ""}, - {"OVER OVER", "2DUP"}, - {"SWAP OVER", "TUCK"}, - {"DROP DROP", "2DROP"}, - {"SWAP DROP", "NIP"}, - {"5 ROLL 5 ROLL", "2ROT"}, - {"3 PICK 3 PICK", "2OVER"}, - {"3 ROLL 3 ROLL", "2SWAP"}, - {"2 PICK 2 PICK 2 PICK", "3DUP"}, - {"1 ADD", "1ADD"}, - {"1 SUB", "1SUB"}, - {"EQUAL VERIFY", "EQUALVERIFY"}, - {"SWAP TXSIGHASH ROT", "TXSIGHASH SWAP"}, - {"SWAP EQUAL", "EQUAL"}, - {"SWAP EQUALVERIFY", "EQUALVERIFY"}, - {"SWAP ADD", "ADD"}, - {"SWAP BOOLAND", "BOOLAND"}, - {"SWAP BOOLOR", "BOOLOR"}, - {"SWAP MIN", "MIN"}, - {"SWAP MAX", "MAX"}, - {"DUP 2 PICK EQUAL", "2DUP EQUAL"}, - {"DUP 2 PICK EQUALVERIFY", "2DUP EQUALVERIFY"}, - {"DUP 2 PICK ADD", "2DUP ADD"}, - {"DUP 2 PICK BOOLAND", "2DUP BOOLAND"}, - {"DUP 2 PICK BOOLOR", "2DUP BOOLOR"}, - {"DUP 2 PICK MIN", "2DUP MIN"}, - {"DUP 2 PICK MAX", "2DUP MAX"}, -} - -func optimize(opcodes string) string { - opcodes = " " + opcodes + " " - looping := true - for looping { - looping = false - for _, o := range optimizations { - before := " " + o.before + " " - var after string - if o.after == "" { - after = " " - } else { - after = " " + o.after + " " - } - newOpcodes := strings.Replace(opcodes, before, after, -1) - if newOpcodes != opcodes { - looping = true - opcodes = newOpcodes - } - } - } - return strings.TrimSpace(opcodes) -} diff --git a/exp/ivy/compiler/parse.go b/exp/ivy/compiler/parse.go deleted file mode 100644 index 61a47654..00000000 --- a/exp/ivy/compiler/parse.go +++ /dev/null @@ -1,563 +0,0 @@ -package compiler - -import ( - "bytes" - "encoding/hex" - "fmt" - "strconv" - "unicode" -) - -// We have some function naming conventions. -// -// For terminals: -// scanX takes buf and position, returns new position (and maybe a value) -// peekX takes *parser, returns bool or string -// consumeX takes *parser and maybe a required literal, maybe returns value -// also updates the parser position -// -// For nonterminals: -// parseX takes *parser, returns AST node, updates parser position - -type parser struct { - buf []byte - pos int -} - -func (p *parser) errorf(format string, args ...interface{}) { - panic(parserErr{buf: p.buf, offset: p.pos, format: format, args: args}) -} - -// parse is the main entry point to the parser -func parse(buf []byte) (contracts []*Contract, err error) { - defer func() { - if val := recover(); val != nil { - if e, ok := val.(parserErr); ok { - err = e - } else { - panic(val) - } - } - }() - p := &parser{buf: buf} - contracts = parseContracts(p) - return -} - -// parse functions - -func parseContracts(p *parser) []*Contract { - var result []*Contract - for peekKeyword(p) == "contract" { - contract := parseContract(p) - result = append(result, contract) - } - return result -} - -// contract name(p1, p2: t1, p3: t2) locks value { ... } -func parseContract(p *parser) *Contract { - consumeKeyword(p, "contract") - name := consumeIdentifier(p) - params := parseParams(p) - consumeKeyword(p, "locks") - value := consumeIdentifier(p) - consumeTok(p, "{") - clauses := parseClauses(p) - consumeTok(p, "}") - return &Contract{Name: name, Params: params, Clauses: clauses, Value: value} -} - -// (p1, p2: t1, p3: t2) -func parseParams(p *parser) []*Param { - var params []*Param - consumeTok(p, "(") - first := true - for !peekTok(p, ")") { - if first { - first = false - } else { - consumeTok(p, ",") - } - pt := parseParamsType(p) - params = append(params, pt...) - } - consumeTok(p, ")") - return params -} - -func parseClauses(p *parser) []*Clause { - var clauses []*Clause - for !peekTok(p, "}") { - c := parseClause(p) - clauses = append(clauses, c) - } - return clauses -} - -func parseParamsType(p *parser) []*Param { - firstName := consumeIdentifier(p) - params := []*Param{&Param{Name: firstName}} - for peekTok(p, ",") { - consumeTok(p, ",") - name := consumeIdentifier(p) - params = append(params, &Param{Name: name}) - } - consumeTok(p, ":") - typ := consumeIdentifier(p) - for _, parm := range params { - if tdesc, ok := types[typ]; ok { - parm.Type = tdesc - } else { - p.errorf("unknown type %s", typ) - } - } - return params -} - -func parseClause(p *parser) *Clause { - var c 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, "}") { - s := parseStatement(p) - statements = append(statements, s) - } - return statements -} - -func parseStatement(p *parser) statement { - switch peekKeyword(p) { - case "verify": - return parseVerifyStmt(p) - case "lock": - return parseLockStmt(p) - case "unlock": - return parseUnlockStmt(p) - } - panic(parseErr(p.buf, p.pos, "unknown keyword \"%s\"", peekKeyword(p))) -} - -func parseVerifyStmt(p *parser) *verifyStatement { - consumeKeyword(p, "verify") - expr := parseExpr(p) - return &verifyStatement{expr: expr} -} - -func parseLockStmt(p *parser) *lockStatement { - consumeKeyword(p, "lock") - locked := parseExpr(p) - consumeKeyword(p, "with") - program := parseExpr(p) - return &lockStatement{locked: locked, program: program} -} - -func parseUnlockStmt(p *parser) *unlockStatement { - consumeKeyword(p, "unlock") - expr := parseExpr(p) - return &unlockStatement{expr} -} - -func parseExpr(p *parser) expression { - // Uses the precedence-climbing algorithm - // - expr := parseUnaryExpr(p) - expr2, pos := parseExprCont(p, expr, 0) - if pos < 0 { - p.errorf("expected expression") - } - p.pos = pos - return expr2 -} - -func parseUnaryExpr(p *parser) expression { - op, pos := scanUnaryOp(p.buf, p.pos) - if pos < 0 { - return parseExpr2(p) - } - p.pos = pos - expr := parseUnaryExpr(p) - return &unaryExpr{op: op, expr: expr} -} - -func parseExprCont(p *parser, lhs expression, minPrecedence int) (expression, int) { - for { - op, pos := scanBinaryOp(p.buf, p.pos) - if pos < 0 || op.precedence < minPrecedence { - break - } - p.pos = pos - - rhs := parseUnaryExpr(p) - - for { - op2, pos2 := scanBinaryOp(p.buf, p.pos) - if pos2 < 0 || op2.precedence <= op.precedence { - break - } - rhs, p.pos = parseExprCont(p, rhs, op2.precedence) - if p.pos < 0 { - return nil, -1 // or is this an error? - } - } - lhs = &binaryExpr{left: lhs, right: rhs, op: op} - } - return lhs, p.pos -} - -func parseExpr2(p *parser) expression { - if expr, pos := scanLiteralExpr(p.buf, p.pos); pos >= 0 { - p.pos = pos - return expr - } - return parseExpr3(p) -} - -func parseExpr3(p *parser) expression { - e := parseExpr4(p) - if peekTok(p, "(") { - args := parseArgs(p) - return &callExpr{fn: e, args: args} - } - return e -} - -func parseExpr4(p *parser) expression { - if peekTok(p, "(") { - consumeTok(p, "(") - e := parseExpr(p) - consumeTok(p, ")") - return e - } - if peekTok(p, "[") { - var elts []expression - consumeTok(p, "[") - first := true - for !peekTok(p, "]") { - if first { - first = false - } else { - consumeTok(p, ",") - } - e := parseExpr(p) - elts = append(elts, e) - } - consumeTok(p, "]") - return listExpr(elts) - } - name := consumeIdentifier(p) - return varRef(name) -} - -func parseArgs(p *parser) []expression { - var exprs []expression - consumeTok(p, "(") - first := true - for !peekTok(p, ")") { - if first { - first = false - } else { - consumeTok(p, ",") - } - e := parseExpr(p) - exprs = append(exprs, e) - } - consumeTok(p, ")") - return exprs -} - -// peek functions - -func peekKeyword(p *parser) string { - name, _ := scanIdentifier(p.buf, p.pos) - return name -} - -func peekTok(p *parser, token string) bool { - pos := scanTok(p.buf, p.pos, token) - return pos >= 0 -} - -// consume functions - -var keywords = []string{ - "contract", "clause", "verify", "output", "return", - "locks", "requires", "of", "lock", "with", "unlock", -} - -func consumeKeyword(p *parser, keyword string) { - pos := scanKeyword(p.buf, p.pos, keyword) - if pos < 0 { - p.errorf("expected keyword %s", keyword) - } - p.pos = pos -} - -func consumeIdentifier(p *parser) string { - name, pos := scanIdentifier(p.buf, p.pos) - if pos < 0 { - p.errorf("expected identifier") - } - p.pos = pos - return name -} - -func consumeTok(p *parser, token string) { - pos := scanTok(p.buf, p.pos, token) - if pos < 0 { - p.errorf("expected %s token", token) - } - p.pos = pos -} - -// scan functions - -func scanUnaryOp(buf []byte, offset int) (*unaryOp, int) { - // Maximum munch. Make sure "-3" scans as ("-3"), not ("-", "3"). - if _, pos := scanIntLiteral(buf, offset); pos >= 0 { - return nil, -1 - } - for _, op := range unaryOps { - newOffset := scanTok(buf, offset, op.op) - if newOffset >= 0 { - return &op, newOffset - } - } - return nil, -1 -} - -func scanBinaryOp(buf []byte, offset int) (*binaryOp, int) { - offset = skipWsAndComments(buf, offset) - var ( - found *binaryOp - newOffset = -1 - ) - for i, op := range binaryOps { - offset2 := scanTok(buf, offset, op.op) - if offset2 >= 0 { - if found == nil || len(op.op) > len(found.op) { - found = &binaryOps[i] - newOffset = offset2 - } - } - } - return found, newOffset -} - -// TODO(bobg): boolean literals? -func scanLiteralExpr(buf []byte, offset int) (expression, int) { - offset = skipWsAndComments(buf, offset) - intliteral, newOffset := scanIntLiteral(buf, offset) - if newOffset >= 0 { - return intliteral, newOffset - } - strliteral, newOffset := scanStrLiteral(buf, offset) - if newOffset >= 0 { - return strliteral, newOffset - } - bytesliteral, newOffset := scanBytesLiteral(buf, offset) // 0x6c249a... - if newOffset >= 0 { - return bytesliteral, newOffset - } - return nil, -1 -} - -func scanIdentifier(buf []byte, offset int) (string, int) { - offset = skipWsAndComments(buf, offset) - i := offset - for ; i < len(buf) && isIDChar(buf[i], i == offset); i++ { - } - if i == offset { - return "", -1 - } - return string(buf[offset:i]), i -} - -func scanTok(buf []byte, offset int, s string) int { - offset = skipWsAndComments(buf, offset) - prefix := []byte(s) - if bytes.HasPrefix(buf[offset:], prefix) { - return offset + len(prefix) - } - return -1 -} - -func scanKeyword(buf []byte, offset int, keyword string) int { - id, newOffset := scanIdentifier(buf, offset) - if newOffset < 0 { - return -1 - } - if id != keyword { - return -1 - } - return newOffset -} - -func scanIntLiteral(buf []byte, offset int) (integerLiteral, int) { - offset = skipWsAndComments(buf, offset) - start := offset - if offset < len(buf) && buf[offset] == '-' { - offset++ - } - i := offset - for ; i < len(buf) && unicode.IsDigit(rune(buf[i])); i++ { - } - if i > offset { - n, err := strconv.ParseInt(string(buf[start:i]), 10, 64) - if err != nil { - return 0, -1 - } - return integerLiteral(n), i - } - return 0, -1 -} - -func scanStrLiteral(buf []byte, offset int) (bytesLiteral, int) { - offset = skipWsAndComments(buf, offset) - if offset >= len(buf) || buf[offset] != '\'' { - return bytesLiteral{}, -1 - } - for i := offset + 1; i < len(buf); i++ { - if buf[i] == '\'' { - return bytesLiteral(buf[offset : i+1]), i + 1 - } - if buf[i] == '\\' { - i++ - } - } - panic(parseErr(buf, offset, "unterminated string literal")) -} - -func scanBytesLiteral(buf []byte, offset int) (bytesLiteral, int) { - offset = skipWsAndComments(buf, offset) - if offset+4 >= len(buf) { - return nil, -1 - } - if buf[offset] != '0' || (buf[offset+1] != 'x' && buf[offset+1] != 'X') { - return nil, -1 - } - if !isHexDigit(buf[offset+2]) || !isHexDigit(buf[offset+3]) { - return nil, -1 - } - i := offset + 4 - for ; i < len(buf); i += 2 { - if i == len(buf)-1 { - panic(parseErr(buf, offset, "odd number of digits in hex literal")) - } - if !isHexDigit(buf[i]) { - break - } - if !isHexDigit(buf[i+1]) { - panic(parseErr(buf, offset, "odd number of digits in hex literal")) - } - } - decoded := make([]byte, hex.DecodedLen(i-(offset+2))) - _, err := hex.Decode(decoded, buf[offset+2:i]) - if err != nil { - return bytesLiteral{}, -1 - } - return bytesLiteral(decoded), i -} - -func skipWsAndComments(buf []byte, offset int) int { - var inComment bool - for ; offset < len(buf); offset++ { - c := buf[offset] - if inComment { - if c == '\n' { - inComment = false - } - } else { - if c == '/' && offset < len(buf)-1 && buf[offset+1] == '/' { - inComment = true - offset++ // skip two chars instead of one - } else if !unicode.IsSpace(rune(c)) { - break - } - } - } - return offset -} - -func isHexDigit(b byte) bool { - return (b >= '0' && b <= '9') || (b >= 'a' && b <= 'f') || (b >= 'A' && b <= 'F') -} - -func isIDChar(c byte, initial bool) bool { - if c >= 'a' && c <= 'z' { - return true - } - if c >= 'A' && c <= 'Z' { - return true - } - if c == '_' { - return true - } - if initial { - return false - } - return unicode.IsDigit(rune(c)) -} - -type parserErr struct { - buf []byte - offset int - format string - args []interface{} -} - -func parseErr(buf []byte, offset int, format string, args ...interface{}) error { - return parserErr{buf: buf, offset: offset, format: format, args: args} -} - -func (p parserErr) Error() string { - // Lines start at 1, columns start at 0, like nature intended. - line := 1 - col := 0 - for i := 0; i < p.offset; i++ { - if p.buf[i] == '\n' { - line++ - col = 0 - } else { - col++ - } - } - args := []interface{}{line, col} - args = append(args, p.args...) - return fmt.Sprintf("line %d, col %d: "+p.format, args...) -} diff --git a/exp/ivy/compiler/stack.go b/exp/ivy/compiler/stack.go deleted file mode 100644 index 1459f02a..00000000 --- a/exp/ivy/compiler/stack.go +++ /dev/null @@ -1,116 +0,0 @@ -package compiler - -type ( - stack struct { - *stackEntry - } - stackEntry struct { - str string - prev *stackEntry - } -) - -func (stk stack) isEmpty() bool { - return stk.stackEntry == nil -} - -func (stk stack) top() string { - if stk.isEmpty() { - return "" - } - return stk.str -} - -func (stk stack) add(str string) stack { - e := &stackEntry{ - str: str, - prev: stk.stackEntry, - } - return stack{e} -} - -func (stk stack) addFromStack(other stack) stack { - if other.isEmpty() { - return stk - } - res := stk.addFromStack(other.drop()) - return res.add(other.top()) -} - -func (stk stack) drop() stack { - if !stk.isEmpty() { - stk = stack{stk.prev} - } - return stk -} - -func (stk stack) dropN(n int) stack { - for n > 0 { - stk = stk.drop() - n-- - } - return stk -} - -func (stk stack) find(str string) int { - if stk.isEmpty() { - return -1 - } - if stk.str == str { - return 0 - } - res := stk.drop().find(str) - if res < 0 { - return res - } - return res + 1 -} - -func (stk stack) roll(n int) stack { - var x func(stack, int) (stack, string) - x = func(stk stack, n int) (stack, string) { - if n == 0 { - return stk.drop(), stk.top() - } - stk2, entry := x(stk.drop(), n-1) - return stk2.add(stk.top()), entry - } - stk, entry := x(stk, n) - return stk.add(entry) -} - -func (stk stack) swap() stack { - a := stk.top() - stk = stk.drop() - b := stk.top() - stk = stk.drop() - return stk.add(a).add(b) -} - -func (stk stack) dup() stack { - return stk.add(stk.top()) -} - -func (stk stack) over() stack { - t := stk.drop().top() - return stk.add(t) -} - -func (stk stack) pick(n int) stack { - t := stk.dropN(n).top() - return stk.add(t) -} - -func (stk stack) String() string { - if stk.stackEntry == nil { - return "[]" - } - var x func(stk stack) string - x = func(stk stack) string { - if stk.stackEntry == nil { - return "" - } - return x(stk.drop()) + " " + stk.stackEntry.str - } - return "[..." + x(stk) + "]" -} diff --git a/exp/ivy/compiler/types.go b/exp/ivy/compiler/types.go deleted file mode 100644 index 4fb4bfff..00000000 --- a/exp/ivy/compiler/types.go +++ /dev/null @@ -1,78 +0,0 @@ -package compiler - -type typeDesc string - -var ( - amountType = typeDesc("Amount") - assetType = typeDesc("Asset") - boolType = typeDesc("Boolean") - contractType = typeDesc("Contract") - hashType = typeDesc("Hash") - intType = typeDesc("Integer") - listType = typeDesc("List") - nilType = typeDesc("") - predType = typeDesc("Predicate") - progType = typeDesc("Program") - pubkeyType = typeDesc("PublicKey") - sigType = typeDesc("Signature") - strType = typeDesc("String") - timeType = typeDesc("Time") - valueType = typeDesc("Value") - - sha3StrType = typeDesc("Sha3(String)") - sha3PubkeyType = typeDesc("Sha3(PublicKey)") - sha256StrType = typeDesc("Sha256(String)") - sha256PubkeyType = typeDesc("Sha256(PublicKey)") -) - -var types = map[string]typeDesc{ - string(amountType): amountType, - string(assetType): assetType, - string(boolType): boolType, - string(hashType): hashType, - string(intType): intType, - string(listType): listType, - string(nilType): nilType, - string(predType): predType, - string(progType): progType, - string(pubkeyType): pubkeyType, - string(sigType): sigType, - string(strType): strType, - string(timeType): timeType, - string(valueType): valueType, - - string(sha3StrType): sha3StrType, - string(sha3PubkeyType): sha3PubkeyType, - string(sha256StrType): sha256StrType, - string(sha256PubkeyType): sha256PubkeyType, -} - -func isHashSubtype(t typeDesc) bool { - switch t { - case sha3StrType, sha3PubkeyType, sha256StrType, sha256PubkeyType: - return true - } - return false -} - -func propagateType(contract *Contract, clause *Clause, env *environ, t typeDesc, e expression) { - v, ok := e.(varRef) - if !ok { - return - } - if entry := env.lookup(string(v)); entry != nil { - entry.t = t - for _, p := range contract.Params { - if p.Name == string(v) { - p.InferredType = t - return - } - } - for _, p := range clause.Params { - if p.Name == string(v) { - p.InferredType = t - return - } - } - } -}