executeOpcode performs execution on the passed opcode. It takes into account whether or not it is hidden by conditionals, but some rules still must be tested in this case.
(op *opcode, data []byte)
| 458 | // whether or not it is hidden by conditionals, but some rules still must be |
| 459 | // tested in this case. |
| 460 | func (vm *Engine) executeOpcode(op *opcode, data []byte) error { |
| 461 | // Disabled opcodes are fail on program counter. |
| 462 | if isOpcodeDisabled(op.value) { |
| 463 | str := fmt.Sprintf("attempt to execute disabled opcode %s", op.name) |
| 464 | return scriptError(ErrDisabledOpcode, str) |
| 465 | } |
| 466 | |
| 467 | // Always-illegal opcodes are fail on program counter. |
| 468 | if isOpcodeAlwaysIllegal(op.value) { |
| 469 | str := fmt.Sprintf("attempt to execute reserved opcode %s", op.name) |
| 470 | return scriptError(ErrReservedOpcode, str) |
| 471 | } |
| 472 | |
| 473 | // Note that this includes OP_RESERVED which counts as a push operation. |
| 474 | if vm.taprootCtx == nil && op.value > OP_16 { |
| 475 | vm.numOps++ |
| 476 | if vm.numOps > MaxOpsPerScript { |
| 477 | str := fmt.Sprintf("exceeded max operation limit of %d", |
| 478 | MaxOpsPerScript) |
| 479 | return scriptError(ErrTooManyOperations, str) |
| 480 | } |
| 481 | |
| 482 | } else if len(data) > MaxScriptElementSize { |
| 483 | str := fmt.Sprintf("element size %d exceeds max allowed size %d", |
| 484 | len(data), MaxScriptElementSize) |
| 485 | return scriptError(ErrElementTooBig, str) |
| 486 | } |
| 487 | |
| 488 | // With ScriptVerifyConstScriptCode, OP_CODESEPARATOR in a non-segwit |
| 489 | // script is rejected even in an unexecuted branch. The script is |
| 490 | // non-segwit when neither a witness program nor a taproot execution |
| 491 | // context has been recorded on the engine: vm.witnessProgram is set |
| 492 | // for v0/v1 native witness spends and nested P2SH-witness spends, |
| 493 | // and vm.taprootCtx is set once the engine has recursed into the |
| 494 | // taproot script-path layer. Both nil means we are still executing |
| 495 | // a legacy script (scriptSig + scriptPubKey, or a P2SH redeem |
| 496 | // script), which is the only case the const-scriptcode rule |
| 497 | // applies to. The check is performed here, before the branch |
| 498 | // execution gate below, so it fires unconditionally on every |
| 499 | // OP_CODESEPARATOR encountered during script iteration. |
| 500 | if op.value == OP_CODESEPARATOR && vm.taprootCtx == nil && |
| 501 | vm.witnessProgram == nil && |
| 502 | vm.hasFlag(ScriptVerifyConstScriptCode) { |
| 503 | |
| 504 | str := "OP_CODESEPARATOR used in non-segwit script" |
| 505 | return scriptError(ErrCodeSeparator, str) |
| 506 | } |
| 507 | |
| 508 | // Nothing left to do when this is not a conditional opcode and it is |
| 509 | // not in an executing branch. |
| 510 | if !vm.isBranchExecuting() && !isOpcodeConditional(op.value) { |
| 511 | return nil |
| 512 | } |
| 513 | |
| 514 | // Ensure all executed data push opcodes use the minimal encoding when |
| 515 | // the minimal data verification flag is set. |
| 516 | if vm.dstack.verifyMinimalData && vm.isBranchExecuting() && |
| 517 | op.value >= 0 && op.value <= OP_PUSHDATA4 { |
no test coverage detected