Step executes the next instruction and moves the program counter to the next opcode in the script, or the next script if the current has ended. Step will return true in the case that the last opcode was successfully executed. The result of calling Step or any other method is undefined if an error
()
| 991 | // The result of calling Step or any other method is undefined if an error is |
| 992 | // returned. |
| 993 | func (vm *Engine) Step() (done bool, err error) { |
| 994 | // Verify the engine is pointing to a valid program counter. |
| 995 | if err := vm.checkValidPC(); err != nil { |
| 996 | return true, err |
| 997 | } |
| 998 | |
| 999 | // Attempt to parse the next opcode from the current script. |
| 1000 | if !vm.tokenizer.Next() { |
| 1001 | // Note that due to the fact that all scripts are checked for parse |
| 1002 | // failures before this code ever runs, there should never be an error |
| 1003 | // here, but check again to be safe in case a refactor breaks that |
| 1004 | // assumption or new script versions are introduced with different |
| 1005 | // semantics. |
| 1006 | if err := vm.tokenizer.Err(); err != nil { |
| 1007 | return false, err |
| 1008 | } |
| 1009 | |
| 1010 | str := fmt.Sprintf("attempt to step beyond script index %d (bytes %x)", |
| 1011 | vm.scriptIdx, vm.scripts[vm.scriptIdx]) |
| 1012 | return true, scriptError(ErrInvalidProgramCounter, str) |
| 1013 | } |
| 1014 | |
| 1015 | // Execute the opcode while taking into account several things such as |
| 1016 | // disabled opcodes, illegal opcodes, maximum allowed operations per script, |
| 1017 | // maximum script element sizes, and conditionals. |
| 1018 | err = vm.executeOpcode(vm.tokenizer.op, vm.tokenizer.Data()) |
| 1019 | if err != nil { |
| 1020 | return true, err |
| 1021 | } |
| 1022 | |
| 1023 | // The number of elements in the combination of the data and alt stacks |
| 1024 | // must not exceed the maximum number of stack elements allowed. |
| 1025 | combinedStackSize := vm.dstack.Depth() + vm.astack.Depth() |
| 1026 | if combinedStackSize > MaxStackSize { |
| 1027 | str := fmt.Sprintf("combined stack size %d > max allowed %d", |
| 1028 | combinedStackSize, MaxStackSize) |
| 1029 | return false, scriptError(ErrStackOverflow, str) |
| 1030 | } |
| 1031 | |
| 1032 | // Prepare for next instruction. |
| 1033 | vm.opcodeIdx++ |
| 1034 | if vm.tokenizer.Done() { |
| 1035 | // Illegal to have a conditional that straddles two scripts. |
| 1036 | if len(vm.condStack) != 0 { |
| 1037 | return false, scriptError(ErrUnbalancedConditional, |
| 1038 | "end of script reached in conditional execution") |
| 1039 | } |
| 1040 | |
| 1041 | // Alt stack doesn't persist between scripts. |
| 1042 | _ = vm.astack.DropN(vm.astack.Depth()) |
| 1043 | |
| 1044 | // The number of operations is per script. |
| 1045 | vm.numOps = 0 |
| 1046 | |
| 1047 | // Reset the opcode index for the next script. |
| 1048 | vm.opcodeIdx = 0 |
| 1049 | |
| 1050 | // Advance to the next script as needed. |