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  }