github.com/algorand/go-algorand-sdk@v1.24.0/logic/logic.go (about) 1 package logic 2 3 //go:generate ./bundle_langspec_json.sh 4 5 import ( 6 "encoding/binary" 7 "encoding/json" 8 "fmt" 9 10 "github.com/algorand/go-algorand-sdk/types" 11 ) 12 13 // Deprecated 14 type langSpec struct { 15 EvalMaxVersion int 16 LogicSigVersion int 17 Ops []operation 18 } 19 20 // Deprecated 21 type operation struct { 22 Opcode int 23 Name string 24 Cost int 25 Size int 26 Returns string 27 ArgEnum []string 28 ArgEnumTypes string 29 Doc string 30 ImmediateNote string 31 Group []string 32 } 33 34 // Deprecated 35 var spec *langSpec 36 37 // Deprecated 38 var opcodes []operation 39 40 // CheckProgram performs basic program validation: instruction count and program cost 41 // Deprecated: Validation relies on metadata (`langspec.json`) that does not accurately represent opcode behavior across program versions. 42 // The behavior of `CheckProgram` relies on `langspec.json`. Thus, this method is being deprecated. 43 func CheckProgram(program []byte, args [][]byte) error { 44 _, _, err := ReadProgram(program, args) 45 return err 46 } 47 48 // ReadProgram is used to validate a program as well as extract found variables 49 // Deprecated: Validation relies on metadata (`langspec.json`) that does not accurately represent opcode behavior across program versions. 50 // The behavior of `ReadProgram` relies on `langspec.json`. Thus, this method is being deprecated. 51 func ReadProgram(program []byte, args [][]byte) (ints []uint64, byteArrays [][]byte, err error) { 52 const intcblockOpcode = 32 53 const bytecblockOpcode = 38 54 const pushbytesOpcode = 128 55 const pushintOpcode = 129 56 if program == nil || len(program) == 0 { 57 err = fmt.Errorf("empty program") 58 return 59 } 60 61 if spec == nil { 62 spec = new(langSpec) 63 if err = json.Unmarshal(langSpecJson, spec); err != nil { 64 return 65 } 66 } 67 version, vlen := binary.Uvarint(program) 68 if vlen <= 0 { 69 err = fmt.Errorf("version parsing error") 70 return 71 } 72 if int(version) > spec.EvalMaxVersion { 73 err = fmt.Errorf("unsupported version") 74 return 75 } 76 77 cost := 0 78 length := len(program) 79 for _, arg := range args { 80 length += len(arg) 81 } 82 83 if length > types.LogicSigMaxSize { 84 err = fmt.Errorf("program too long") 85 return 86 } 87 88 if opcodes == nil { 89 opcodes = make([]operation, 256) 90 for _, op := range spec.Ops { 91 opcodes[op.Opcode] = op 92 } 93 } 94 95 for pc := vlen; pc < len(program); { 96 op := opcodes[program[pc]] 97 if op.Name == "" { 98 err = fmt.Errorf("invalid instruction") 99 return 100 } 101 102 cost = cost + op.Cost 103 size := op.Size 104 if size == 0 { 105 switch op.Opcode { 106 case intcblockOpcode: 107 var foundInts []uint64 108 size, foundInts, err = readIntConstBlock(program, pc) 109 ints = append(ints, foundInts...) 110 if err != nil { 111 return 112 } 113 case bytecblockOpcode: 114 var foundByteArrays [][]byte 115 size, foundByteArrays, err = readByteConstBlock(program, pc) 116 byteArrays = append(byteArrays, foundByteArrays...) 117 if err != nil { 118 return 119 } 120 case pushintOpcode: 121 var foundInt uint64 122 size, foundInt, err = readPushIntOp(program, pc) 123 ints = append(ints, foundInt) 124 if err != nil { 125 return 126 } 127 case pushbytesOpcode: 128 var foundByteArray []byte 129 size, foundByteArray, err = readPushByteOp(program, pc) 130 byteArrays = append(byteArrays, foundByteArray) 131 if err != nil { 132 return 133 } 134 default: 135 err = fmt.Errorf("invalid instruction") 136 return 137 } 138 } 139 pc = pc + size 140 } 141 142 // costs calculated dynamically starting in v4 143 if version < 4 && cost > types.LogicSigMaxCost { 144 err = fmt.Errorf("program too costly for version < 4. consider using v4.") 145 } 146 147 return 148 } 149 150 // Deprecated 151 func readIntConstBlock(program []byte, pc int) (size int, ints []uint64, err error) { 152 size = 1 153 numInts, bytesUsed := binary.Uvarint(program[pc+size:]) 154 if bytesUsed <= 0 { 155 err = fmt.Errorf("could not decode int const block size at pc=%d", pc+size) 156 return 157 } 158 159 size += bytesUsed 160 for i := uint64(0); i < numInts; i++ { 161 if pc+size >= len(program) { 162 err = fmt.Errorf("intcblock ran past end of program") 163 return 164 } 165 num, bytesUsed := binary.Uvarint(program[pc+size:]) 166 if bytesUsed <= 0 { 167 err = fmt.Errorf("could not decode int const[%d] at pc=%d", i, pc+size) 168 return 169 } 170 ints = append(ints, num) 171 size += bytesUsed 172 } 173 return 174 } 175 176 // Deprecated 177 func readByteConstBlock(program []byte, pc int) (size int, byteArrays [][]byte, err error) { 178 size = 1 179 numInts, bytesUsed := binary.Uvarint(program[pc+size:]) 180 if bytesUsed <= 0 { 181 err = fmt.Errorf("could not decode []byte const block size at pc=%d", pc+size) 182 return 183 } 184 185 size += bytesUsed 186 for i := uint64(0); i < numInts; i++ { 187 if pc+size >= len(program) { 188 err = fmt.Errorf("bytecblock ran past end of program") 189 return 190 } 191 scanTarget := program[pc+size:] 192 itemLen, bytesUsed := binary.Uvarint(scanTarget) 193 if bytesUsed <= 0 { 194 err = fmt.Errorf("could not decode []byte const[%d] at pc=%d", i, pc+size) 195 return 196 } 197 size += bytesUsed 198 if pc+size+int(itemLen) > len(program) { 199 err = fmt.Errorf("bytecblock ran past end of program") 200 return 201 } 202 byteArray := program[pc+size : pc+size+int(itemLen)] 203 byteArrays = append(byteArrays, byteArray) 204 size += int(itemLen) 205 } 206 return 207 } 208 209 // Deprecated 210 func readPushIntOp(program []byte, pc int) (size int, foundInt uint64, err error) { 211 size = 1 212 foundInt, bytesUsed := binary.Uvarint(program[pc+size:]) 213 if bytesUsed <= 0 { 214 err = fmt.Errorf("could not decode push int const at pc=%d", pc+size) 215 return 216 } 217 218 size += bytesUsed 219 return 220 } 221 222 // Deprecated 223 func readPushByteOp(program []byte, pc int) (size int, byteArray []byte, err error) { 224 size = 1 225 itemLen, bytesUsed := binary.Uvarint(program[pc+size:]) 226 if bytesUsed <= 0 { 227 err = fmt.Errorf("could not decode push []byte const size at pc=%d", pc+size) 228 return 229 } 230 231 size += bytesUsed 232 if pc+size+int(itemLen) > len(program) { 233 err = fmt.Errorf("pushbytes ran past end of program") 234 return 235 } 236 byteArray = program[pc+size : pc+size+int(itemLen)] 237 size += int(itemLen) 238 return 239 }