--- /dev/null
+package vm
+
+import (
+ "encoding/binary"
+ "fmt"
+ "math"
+ "reflect"
+
+ "github.com/bytom/errors"
+ "github.com/bytom/math/checked"
+)
+
+type Op uint8
+
+func (op Op) String() string {
+ return ops[op].name
+}
+
+type Instruction struct {
+ Op Op
+ Len uint32
+ Data []byte
+}
+
+const (
+ OP_FALSE Op = 0x00
+ OP_0 Op = 0x00 // synonym
+
+ OP_1 Op = 0x51
+ OP_TRUE Op = 0x51 // synonym
+
+ OP_2 Op = 0x52
+ OP_3 Op = 0x53
+ OP_4 Op = 0x54
+ OP_5 Op = 0x55
+ OP_6 Op = 0x56
+ OP_7 Op = 0x57
+ OP_8 Op = 0x58
+ OP_9 Op = 0x59
+ OP_10 Op = 0x5a
+ OP_11 Op = 0x5b
+ OP_12 Op = 0x5c
+ OP_13 Op = 0x5d
+ OP_14 Op = 0x5e
+ OP_15 Op = 0x5f
+ OP_16 Op = 0x60
+
+ OP_DATA_1 Op = 0x01
+ OP_DATA_2 Op = 0x02
+ OP_DATA_3 Op = 0x03
+ OP_DATA_4 Op = 0x04
+ OP_DATA_5 Op = 0x05
+ OP_DATA_6 Op = 0x06
+ OP_DATA_7 Op = 0x07
+ OP_DATA_8 Op = 0x08
+ OP_DATA_9 Op = 0x09
+ OP_DATA_10 Op = 0x0a
+ OP_DATA_11 Op = 0x0b
+ OP_DATA_12 Op = 0x0c
+ OP_DATA_13 Op = 0x0d
+ OP_DATA_14 Op = 0x0e
+ OP_DATA_15 Op = 0x0f
+ OP_DATA_16 Op = 0x10
+ OP_DATA_17 Op = 0x11
+ OP_DATA_18 Op = 0x12
+ OP_DATA_19 Op = 0x13
+ OP_DATA_20 Op = 0x14
+ OP_DATA_21 Op = 0x15
+ OP_DATA_22 Op = 0x16
+ OP_DATA_23 Op = 0x17
+ OP_DATA_24 Op = 0x18
+ OP_DATA_25 Op = 0x19
+ OP_DATA_26 Op = 0x1a
+ OP_DATA_27 Op = 0x1b
+ OP_DATA_28 Op = 0x1c
+ OP_DATA_29 Op = 0x1d
+ OP_DATA_30 Op = 0x1e
+ OP_DATA_31 Op = 0x1f
+ OP_DATA_32 Op = 0x20
+ OP_DATA_33 Op = 0x21
+ OP_DATA_34 Op = 0x22
+ OP_DATA_35 Op = 0x23
+ OP_DATA_36 Op = 0x24
+ OP_DATA_37 Op = 0x25
+ OP_DATA_38 Op = 0x26
+ OP_DATA_39 Op = 0x27
+ OP_DATA_40 Op = 0x28
+ OP_DATA_41 Op = 0x29
+ OP_DATA_42 Op = 0x2a
+ OP_DATA_43 Op = 0x2b
+ OP_DATA_44 Op = 0x2c
+ OP_DATA_45 Op = 0x2d
+ OP_DATA_46 Op = 0x2e
+ OP_DATA_47 Op = 0x2f
+ OP_DATA_48 Op = 0x30
+ OP_DATA_49 Op = 0x31
+ OP_DATA_50 Op = 0x32
+ OP_DATA_51 Op = 0x33
+ OP_DATA_52 Op = 0x34
+ OP_DATA_53 Op = 0x35
+ OP_DATA_54 Op = 0x36
+ OP_DATA_55 Op = 0x37
+ OP_DATA_56 Op = 0x38
+ OP_DATA_57 Op = 0x39
+ OP_DATA_58 Op = 0x3a
+ OP_DATA_59 Op = 0x3b
+ OP_DATA_60 Op = 0x3c
+ OP_DATA_61 Op = 0x3d
+ OP_DATA_62 Op = 0x3e
+ OP_DATA_63 Op = 0x3f
+ OP_DATA_64 Op = 0x40
+ OP_DATA_65 Op = 0x41
+ OP_DATA_66 Op = 0x42
+ OP_DATA_67 Op = 0x43
+ OP_DATA_68 Op = 0x44
+ OP_DATA_69 Op = 0x45
+ OP_DATA_70 Op = 0x46
+ OP_DATA_71 Op = 0x47
+ OP_DATA_72 Op = 0x48
+ OP_DATA_73 Op = 0x49
+ OP_DATA_74 Op = 0x4a
+ OP_DATA_75 Op = 0x4b
+
+ OP_PUSHDATA1 Op = 0x4c
+ OP_PUSHDATA2 Op = 0x4d
+ OP_PUSHDATA4 Op = 0x4e
+ OP_1NEGATE Op = 0x4f
+ OP_NOP Op = 0x61
+
+ OP_JUMP Op = 0x63
+ OP_JUMPIF Op = 0x64
+ OP_VERIFY Op = 0x69
+ OP_FAIL Op = 0x6a
+ OP_CHECKPREDICATE Op = 0xc0
+
+ OP_TOALTSTACK Op = 0x6b
+ OP_FROMALTSTACK Op = 0x6c
+ OP_2DROP Op = 0x6d
+ OP_2DUP Op = 0x6e
+ OP_3DUP Op = 0x6f
+ OP_2OVER Op = 0x70
+ OP_2ROT Op = 0x71
+ OP_2SWAP Op = 0x72
+ OP_IFDUP Op = 0x73
+ OP_DEPTH Op = 0x74
+ OP_DROP Op = 0x75
+ OP_DUP Op = 0x76
+ OP_NIP Op = 0x77
+ OP_OVER Op = 0x78
+ OP_PICK Op = 0x79
+ OP_ROLL Op = 0x7a
+ OP_ROT Op = 0x7b
+ OP_SWAP Op = 0x7c
+ OP_TUCK Op = 0x7d
+
+ OP_CAT Op = 0x7e
+ OP_SUBSTR Op = 0x7f
+ OP_LEFT Op = 0x80
+ OP_RIGHT Op = 0x81
+ OP_SIZE Op = 0x82
+ OP_CATPUSHDATA Op = 0x89
+
+ OP_INVERT Op = 0x83
+ OP_AND Op = 0x84
+ OP_OR Op = 0x85
+ OP_XOR Op = 0x86
+ OP_EQUAL Op = 0x87
+ OP_EQUALVERIFY Op = 0x88
+
+ OP_1ADD Op = 0x8b
+ OP_1SUB Op = 0x8c
+ OP_2MUL Op = 0x8d
+ OP_2DIV Op = 0x8e
+ OP_NEGATE Op = 0x8f
+ OP_ABS Op = 0x90
+ OP_NOT Op = 0x91
+ OP_0NOTEQUAL Op = 0x92
+ OP_ADD Op = 0x93
+ OP_SUB Op = 0x94
+ OP_MUL Op = 0x95
+ OP_DIV Op = 0x96
+ OP_MOD Op = 0x97
+ OP_LSHIFT Op = 0x98
+ OP_RSHIFT Op = 0x99
+ OP_BOOLAND Op = 0x9a
+ OP_BOOLOR Op = 0x9b
+ OP_NUMEQUAL Op = 0x9c
+ OP_NUMEQUALVERIFY Op = 0x9d
+ OP_NUMNOTEQUAL Op = 0x9e
+ OP_LESSTHAN Op = 0x9f
+ OP_GREATERTHAN Op = 0xa0
+ OP_LESSTHANOREQUAL Op = 0xa1
+ OP_GREATERTHANOREQUAL Op = 0xa2
+ OP_MIN Op = 0xa3
+ OP_MAX Op = 0xa4
+ OP_WITHIN Op = 0xa5
+
+ OP_SHA256 Op = 0xa8
+ OP_SHA3 Op = 0xaa
+ OP_HASH160 Op = 0xab
+ OP_CHECKSIG Op = 0xac
+ OP_CHECKMULTISIG Op = 0xad
+ OP_TXSIGHASH Op = 0xae
+
+ OP_CHECKOUTPUT Op = 0xc1
+ OP_ASSET Op = 0xc2
+ OP_AMOUNT Op = 0xc3
+ OP_PROGRAM Op = 0xc4
+ OP_INDEX Op = 0xc9
+ OP_ENTRYID Op = 0xca
+ OP_OUTPUTID Op = 0xcb
+ OP_BLOCKHEIGHT Op = 0xcd
+)
+
+type opInfo struct {
+ op Op
+ name string
+ fn func(*virtualMachine) error
+}
+
+var (
+ ops = [256]opInfo{
+ // data pushing
+ OP_FALSE: {OP_FALSE, "FALSE", opFalse},
+
+ // sic: the PUSHDATA ops all share an implementation
+ OP_PUSHDATA1: {OP_PUSHDATA1, "PUSHDATA1", opPushdata},
+ OP_PUSHDATA2: {OP_PUSHDATA2, "PUSHDATA2", opPushdata},
+ OP_PUSHDATA4: {OP_PUSHDATA4, "PUSHDATA4", opPushdata},
+
+ OP_1NEGATE: {OP_1NEGATE, "1NEGATE", op1Negate},
+
+ OP_NOP: {OP_NOP, "NOP", opNop},
+
+ // control flow
+ OP_JUMP: {OP_JUMP, "JUMP", opJump},
+ OP_JUMPIF: {OP_JUMPIF, "JUMPIF", opJumpIf},
+
+ OP_VERIFY: {OP_VERIFY, "VERIFY", opVerify},
+ OP_FAIL: {OP_FAIL, "FAIL", opFail},
+
+ OP_TOALTSTACK: {OP_TOALTSTACK, "TOALTSTACK", opToAltStack},
+ OP_FROMALTSTACK: {OP_FROMALTSTACK, "FROMALTSTACK", opFromAltStack},
+ OP_2DROP: {OP_2DROP, "2DROP", op2Drop},
+ OP_2DUP: {OP_2DUP, "2DUP", op2Dup},
+ OP_3DUP: {OP_3DUP, "3DUP", op3Dup},
+ OP_2OVER: {OP_2OVER, "2OVER", op2Over},
+ OP_2ROT: {OP_2ROT, "2ROT", op2Rot},
+ OP_2SWAP: {OP_2SWAP, "2SWAP", op2Swap},
+ OP_IFDUP: {OP_IFDUP, "IFDUP", opIfDup},
+ OP_DEPTH: {OP_DEPTH, "DEPTH", opDepth},
+ OP_DROP: {OP_DROP, "DROP", opDrop},
+ OP_DUP: {OP_DUP, "DUP", opDup},
+ OP_NIP: {OP_NIP, "NIP", opNip},
+ OP_OVER: {OP_OVER, "OVER", opOver},
+ OP_PICK: {OP_PICK, "PICK", opPick},
+ OP_ROLL: {OP_ROLL, "ROLL", opRoll},
+ OP_ROT: {OP_ROT, "ROT", opRot},
+ OP_SWAP: {OP_SWAP, "SWAP", opSwap},
+ OP_TUCK: {OP_TUCK, "TUCK", opTuck},
+
+ OP_CAT: {OP_CAT, "CAT", opCat},
+ OP_SUBSTR: {OP_SUBSTR, "SUBSTR", opSubstr},
+ OP_LEFT: {OP_LEFT, "LEFT", opLeft},
+ OP_RIGHT: {OP_RIGHT, "RIGHT", opRight},
+ OP_SIZE: {OP_SIZE, "SIZE", opSize},
+ OP_CATPUSHDATA: {OP_CATPUSHDATA, "CATPUSHDATA", opCatpushdata},
+
+ OP_INVERT: {OP_INVERT, "INVERT", opInvert},
+ OP_AND: {OP_AND, "AND", opAnd},
+ OP_OR: {OP_OR, "OR", opOr},
+ OP_XOR: {OP_XOR, "XOR", opXor},
+ OP_EQUAL: {OP_EQUAL, "EQUAL", opEqual},
+ OP_EQUALVERIFY: {OP_EQUALVERIFY, "EQUALVERIFY", opEqualVerify},
+
+ OP_1ADD: {OP_1ADD, "1ADD", op1Add},
+ OP_1SUB: {OP_1SUB, "1SUB", op1Sub},
+ OP_2MUL: {OP_2MUL, "2MUL", op2Mul},
+ OP_2DIV: {OP_2DIV, "2DIV", op2Div},
+ OP_NEGATE: {OP_NEGATE, "NEGATE", opNegate},
+ OP_ABS: {OP_ABS, "ABS", opAbs},
+ OP_NOT: {OP_NOT, "NOT", opNot},
+ OP_0NOTEQUAL: {OP_0NOTEQUAL, "0NOTEQUAL", op0NotEqual},
+ OP_ADD: {OP_ADD, "ADD", opAdd},
+ OP_SUB: {OP_SUB, "SUB", opSub},
+ OP_MUL: {OP_MUL, "MUL", opMul},
+ OP_DIV: {OP_DIV, "DIV", opDiv},
+ OP_MOD: {OP_MOD, "MOD", opMod},
+ OP_LSHIFT: {OP_LSHIFT, "LSHIFT", opLshift},
+ OP_RSHIFT: {OP_RSHIFT, "RSHIFT", opRshift},
+ OP_BOOLAND: {OP_BOOLAND, "BOOLAND", opBoolAnd},
+ OP_BOOLOR: {OP_BOOLOR, "BOOLOR", opBoolOr},
+ OP_NUMEQUAL: {OP_NUMEQUAL, "NUMEQUAL", opNumEqual},
+ OP_NUMEQUALVERIFY: {OP_NUMEQUALVERIFY, "NUMEQUALVERIFY", opNumEqualVerify},
+ OP_NUMNOTEQUAL: {OP_NUMNOTEQUAL, "NUMNOTEQUAL", opNumNotEqual},
+ OP_LESSTHAN: {OP_LESSTHAN, "LESSTHAN", opLessThan},
+ OP_GREATERTHAN: {OP_GREATERTHAN, "GREATERTHAN", opGreaterThan},
+ OP_LESSTHANOREQUAL: {OP_LESSTHANOREQUAL, "LESSTHANOREQUAL", opLessThanOrEqual},
+ OP_GREATERTHANOREQUAL: {OP_GREATERTHANOREQUAL, "GREATERTHANOREQUAL", opGreaterThanOrEqual},
+ OP_MIN: {OP_MIN, "MIN", opMin},
+ OP_MAX: {OP_MAX, "MAX", opMax},
+ OP_WITHIN: {OP_WITHIN, "WITHIN", opWithin},
+
+ OP_SHA256: {OP_SHA256, "SHA256", opSha256},
+ OP_SHA3: {OP_SHA3, "SHA3", opSha3},
+ OP_HASH160: {OP_HASH160, "HASH160", opHash160},
+ OP_CHECKSIG: {OP_CHECKSIG, "CHECKSIG", opCheckSig},
+ OP_CHECKMULTISIG: {OP_CHECKMULTISIG, "CHECKMULTISIG", opCheckMultiSig},
+ OP_TXSIGHASH: {OP_TXSIGHASH, "TXSIGHASH", opTxSigHash},
+
+ OP_CHECKOUTPUT: {OP_CHECKOUTPUT, "CHECKOUTPUT", opCheckOutput},
+ OP_ASSET: {OP_ASSET, "ASSET", opAsset},
+ OP_AMOUNT: {OP_AMOUNT, "AMOUNT", opAmount},
+ OP_PROGRAM: {OP_PROGRAM, "PROGRAM", opProgram},
+ OP_INDEX: {OP_INDEX, "INDEX", opIndex},
+ OP_ENTRYID: {OP_ENTRYID, "ENTRYID", opEntryID},
+ OP_OUTPUTID: {OP_OUTPUTID, "OUTPUTID", opOutputID},
+ OP_BLOCKHEIGHT: {OP_BLOCKHEIGHT, "BLOCKHEIGHT", opBlockHeight},
+ }
+
+ opsByName map[string]opInfo
+)
+
+// ParseOp parses the op at position pc in prog, returning the parsed
+// instruction (opcode plus any associated data).
+func ParseOp(prog []byte, pc uint32) (inst Instruction, err error) {
+ if len(prog) > math.MaxInt32 {
+ err = ErrLongProgram
+ }
+ l := uint32(len(prog))
+ if pc >= l {
+ err = ErrShortProgram
+ return
+ }
+ opcode := Op(prog[pc])
+ inst.Op = opcode
+ inst.Len = 1
+ if opcode >= OP_1 && opcode <= OP_16 {
+ inst.Data = []byte{uint8(opcode-OP_1) + 1}
+ return
+ }
+ if opcode >= OP_DATA_1 && opcode <= OP_DATA_75 {
+ inst.Len += uint32(opcode - OP_DATA_1 + 1)
+ end, ok := checked.AddUint32(pc, inst.Len)
+ if !ok {
+ err = errors.WithDetail(checked.ErrOverflow, "data length exceeds max program size")
+ return
+ }
+ if end > l {
+ err = ErrShortProgram
+ return
+ }
+ inst.Data = prog[pc+1 : end]
+ return
+ }
+ if opcode == OP_PUSHDATA1 {
+ if pc == l-1 {
+ err = ErrShortProgram
+ return
+ }
+ n := prog[pc+1]
+ inst.Len += uint32(n) + 1
+ end, ok := checked.AddUint32(pc, inst.Len)
+ if !ok {
+ err = errors.WithDetail(checked.ErrOverflow, "data length exceeds max program size")
+ }
+ if end > l {
+ err = ErrShortProgram
+ return
+ }
+ inst.Data = prog[pc+2 : end]
+ return
+ }
+ if opcode == OP_PUSHDATA2 {
+ if len(prog) < 3 || pc > l-3 {
+ err = ErrShortProgram
+ return
+ }
+ n := binary.LittleEndian.Uint16(prog[pc+1 : pc+3])
+ inst.Len += uint32(n) + 2
+ end, ok := checked.AddUint32(pc, inst.Len)
+ if !ok {
+ err = errors.WithDetail(checked.ErrOverflow, "data length exceeds max program size")
+ return
+ }
+ if end > l {
+ err = ErrShortProgram
+ return
+ }
+ inst.Data = prog[pc+3 : end]
+ return
+ }
+ if opcode == OP_PUSHDATA4 {
+ if len(prog) < 5 || pc > l-5 {
+ err = ErrShortProgram
+ return
+ }
+ inst.Len += 4
+
+ n := binary.LittleEndian.Uint32(prog[pc+1 : pc+5])
+ var ok bool
+ inst.Len, ok = checked.AddUint32(inst.Len, n)
+ if !ok {
+ err = errors.WithDetail(checked.ErrOverflow, "data length exceeds max program size")
+ return
+ }
+ end, ok := checked.AddUint32(pc, inst.Len)
+ if !ok {
+ err = errors.WithDetail(checked.ErrOverflow, "data length exceeds max program size")
+ return
+ }
+ if end > l {
+ err = ErrShortProgram
+ return
+ }
+ inst.Data = prog[pc+5 : end]
+ return
+ }
+ if opcode == OP_JUMP || opcode == OP_JUMPIF {
+ inst.Len += 4
+ end, ok := checked.AddUint32(pc, inst.Len)
+ if !ok {
+ err = errors.WithDetail(checked.ErrOverflow, "jump target exceeds max program size")
+ return
+ }
+ if end > l {
+ err = ErrShortProgram
+ return
+ }
+ inst.Data = prog[pc+1 : end]
+ return
+ }
+ return
+}
+
+func ParseProgram(prog []byte) ([]Instruction, error) {
+ var result []Instruction
+ for pc := uint32(0); pc < uint32(len(prog)); { // update pc inside the loop
+ inst, err := ParseOp(prog, pc)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, inst)
+ var ok bool
+ pc, ok = checked.AddUint32(pc, inst.Len)
+ if !ok {
+ return nil, errors.WithDetail(checked.ErrOverflow, "program counter exceeds max program size")
+ }
+ }
+ return result, nil
+}
+
+var isExpansion [256]bool
+
+func init() {
+ for i := 1; i <= 75; i++ {
+ ops[i] = opInfo{Op(i), fmt.Sprintf("DATA_%d", i), opPushdata}
+ }
+ for i := uint8(0); i <= 15; i++ {
+ op := uint8(OP_1) + i
+ ops[op] = opInfo{Op(op), fmt.Sprintf("%d", i+1), opPushdata}
+ }
+
+ // This is here to break a dependency cycle
+ ops[OP_CHECKPREDICATE] = opInfo{OP_CHECKPREDICATE, "CHECKPREDICATE", opCheckPredicate}
+
+ opsByName = make(map[string]opInfo)
+ for _, info := range ops {
+ opsByName[info.name] = info
+ }
+ opsByName["0"] = ops[OP_FALSE]
+ opsByName["TRUE"] = ops[OP_1]
+
+ for i := 0; i <= 255; i++ {
+ if ops[i].name == "" {
+ ops[i] = opInfo{Op(i), fmt.Sprintf("NOPx%02x", i), opNop}
+ isExpansion[i] = true
+ }
+ }
+}
+
+// IsPushdata judge instruction whether is a pushdata operation(include opFalse operation)
+func (inst *Instruction) IsPushdata() bool {
+ if reflect.ValueOf(ops[inst.Op].fn) == reflect.ValueOf(ops[OP_1].fn) ||
+ reflect.ValueOf(ops[inst.Op].fn) == reflect.ValueOf(ops[OP_0].fn) {
+ return true
+ }
+
+ return false
+}