Skip to content

Commit d094ac2

Browse files
committed
feat(script): deprecate unnecessary opcodes
1 parent 076215e commit d094ac2

File tree

20 files changed

+174
-66
lines changed

20 files changed

+174
-66
lines changed

.yamllint.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
extends: default
22

3+
ignore:
4+
- .venv/
5+
36
rules:
47
document-start: disable
58
line-length:

hathor/conf/settings.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,9 @@ def GENESIS_TX2_TIMESTAMP(self) -> int:
485485
# Used to enable fee-based tokens.
486486
ENABLE_FEE_BASED_TOKENS: FeatureSetting = FeatureSetting.DISABLED
487487

488+
# Used to enable opcodes V2.
489+
ENABLE_OPCODES_V2: FeatureSetting = FeatureSetting.DISABLED
490+
488491
# List of enabled blueprints.
489492
BLUEPRINTS: dict[bytes, str] = {}
490493

hathor/conf/unittests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ FEATURE_ACTIVATION:
2525

2626
ENABLE_NANO_CONTRACTS: enabled
2727
ENABLE_FEE_BASED_TOKENS: enabled
28+
ENABLE_OPCODES_V2: enabled
2829

2930
NC_ON_CHAIN_BLUEPRINT_ALLOWED_ADDRESSES:
3031
# keypair wallet:

hathor/consensus/consensus.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from hathor.transaction import BaseTransaction, Block, Transaction
3131
from hathor.transaction.exceptions import RewardLocked
3232
from hathor.util import not_none
33+
from hathor.verification.verification_params import VerificationParams
3334

3435
if TYPE_CHECKING:
3536
from hathor.conf.settings import HathorSettings
@@ -427,6 +428,9 @@ def _feature_activation_rules(self, tx: Transaction, old_best_block: Block, new_
427428
case Feature.COUNT_CHECKDATASIG_OP:
428429
if not self._checkdatasig_count_rule(tx):
429430
return False
431+
case Feature.OPCODES_V2:
432+
if not self._opcodes_v2_activation_rule(tx, is_active, new_best_block):
433+
return False
430434
case (
431435
Feature.INCREASE_MAX_MERKLE_PATH_LENGTH
432436
| Feature.NOP_FEATURE_1
@@ -489,6 +493,35 @@ def _checkdatasig_count_rule(self, tx: Transaction) -> bool:
489493
return False
490494
return True
491495

496+
def _opcodes_v2_activation_rule(self, tx: Transaction, is_active: bool, new_best_block: Block) -> bool:
497+
"""
498+
Check whether a tx became invalid because the reorg changed the opcodes V2 feature activation state.
499+
"""
500+
from hathor.verification.nano_header_verifier import NanoHeaderVerifier
501+
from hathor.verification.transaction_verifier import TransactionVerifier
502+
503+
if not is_active:
504+
# When the feature is not active, this rule has no effect.
505+
return True
506+
507+
params = VerificationParams.default_for_mempool(best_block=new_best_block)
508+
509+
# Any exception in the inputs verification will be considered
510+
# a fail and the tx will be removed from the mempool.
511+
try:
512+
TransactionVerifier._verify_inputs(self._settings, tx, params, skip_script=False)
513+
except Exception:
514+
return False
515+
516+
# Any exception in the nc_signature verification will be considered
517+
# a fail and the tx will be removed from the mempool.
518+
try:
519+
NanoHeaderVerifier._verify_nc_signature(self._settings, tx, params)
520+
except Exception:
521+
return False
522+
523+
return True
524+
492525
def _feature_activation_flips(self, *, old_best_block: Block, new_best_block: Block) -> dict[Feature, bool]:
493526
"""
494527
Get a diff of feature activation states that flipped from active to non-active,

hathor/feature_activation/feature.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ class Feature(StrEnum):
3232
COUNT_CHECKDATASIG_OP = 'COUNT_CHECKDATASIG_OP'
3333
NANO_CONTRACTS = 'NANO_CONTRACTS'
3434
FEE_TOKENS = 'FEE_TOKENS'
35+
OPCODES_V2 = 'OPCODES_V2'

hathor/feature_activation/utils.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
from hathor.feature_activation.feature import Feature
2121
from hathor.feature_activation.model.feature_state import FeatureState
22+
from hathor.transaction.scripts.opcode import OpcodesVersion
2223

2324
if TYPE_CHECKING:
2425
from hathor.conf.settings import FeatureSetting, HathorSettings
@@ -33,6 +34,7 @@ class Features:
3334
count_checkdatasig_op: bool
3435
nano: bool
3536
fee_tokens: bool
37+
opcodes_version: OpcodesVersion
3638

3739
@staticmethod
3840
def from_vertex(*, settings: HathorSettings, feature_service: FeatureService, vertex: Vertex) -> Features:
@@ -43,17 +45,21 @@ def from_vertex(*, settings: HathorSettings, feature_service: FeatureService, ve
4345
Feature.COUNT_CHECKDATASIG_OP: FeatureSetting.FEATURE_ACTIVATION,
4446
Feature.NANO_CONTRACTS: settings.ENABLE_NANO_CONTRACTS,
4547
Feature.FEE_TOKENS: settings.ENABLE_FEE_BASED_TOKENS,
48+
Feature.OPCODES_V2: settings.ENABLE_OPCODES_V2,
4649
}
4750

48-
features = {
51+
feature_is_active: dict[Feature, bool] = {
4952
feature: _is_feature_active(setting, feature_states.get(feature, FeatureState.DEFINED))
5053
for feature, setting in feature_settings.items()
5154
}
5255

56+
opcodes_version = OpcodesVersion.V2 if feature_is_active[Feature.OPCODES_V2] else OpcodesVersion.V1
57+
5358
return Features(
54-
count_checkdatasig_op=features[Feature.COUNT_CHECKDATASIG_OP],
55-
nano=features[Feature.NANO_CONTRACTS],
56-
fee_tokens=features[Feature.FEE_TOKENS],
59+
count_checkdatasig_op=feature_is_active[Feature.COUNT_CHECKDATASIG_OP],
60+
nano=feature_is_active[Feature.NANO_CONTRACTS],
61+
fee_tokens=feature_is_active[Feature.FEE_TOKENS],
62+
opcodes_version=opcodes_version,
5763
)
5864

5965

hathor/nanocontracts/types.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from hathor.nanocontracts.exception import BlueprintSyntaxError, NCSerializationError
3333
from hathor.nanocontracts.faux_immutable import FauxImmutableMeta
3434
from hathor.serialization import SerializationError
35+
from hathor.transaction.scripts.opcode import OpcodesVersion
3536
from hathor.transaction.util import bytes_to_int, get_deposit_token_withdraw_amount, int_to_bytes
3637
from hathor.utils.typing import InnerTypeMixin
3738

@@ -162,7 +163,7 @@ def checksig(self, script: bytes) -> bool:
162163
from hathor.transaction.exceptions import ScriptError
163164
from hathor.transaction.scripts import ScriptExtras
164165
from hathor.transaction.scripts.execute import raw_script_eval
165-
extras = ScriptExtras(tx=self) # type: ignore[arg-type]
166+
extras = ScriptExtras(tx=self, version=OpcodesVersion.V2) # type: ignore[arg-type]
166167
try:
167168
raw_script_eval(input_data=self.script_input, output_script=script, extras=extras)
168169
except ScriptError:

hathor/p2p/sync_v2/transaction_streaming_client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from hathor.p2p.sync_v2.streamers import StreamEnd
2929
from hathor.transaction import BaseTransaction, Transaction
3030
from hathor.transaction.exceptions import HathorError, TxValidationError
31+
from hathor.transaction.scripts.opcode import OpcodesVersion
3132
from hathor.types import VertexId
3233
from hathor.verification.verification_params import VerificationParams
3334

@@ -59,6 +60,7 @@ def __init__(self,
5960
count_checkdatasig_op=False,
6061
nano=False,
6162
fee_tokens=False,
63+
opcodes_version=OpcodesVersion.V1,
6264
)
6365
)
6466

hathor/transaction/resources/create_tx.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from hathor.transaction import Transaction, TxInput, TxOutput
2323
from hathor.transaction.scripts import create_output_script
2424
from hathor.util import api_catch_exceptions, json_dumpb, json_loadb
25+
from hathor.verification.verification_params import VerificationParams
2526

2627

2728
def from_raw_output(raw_output: dict, tokens: list[bytes]) -> TxOutput:
@@ -116,11 +117,12 @@ def _verify_unsigned_skip_pow(self, tx: Transaction) -> None:
116117
verifiers.tx.verify_output_token_indexes(tx)
117118
verifiers.vertex.verify_sigops_output(tx, enable_checkdatasig_count=True)
118119
verifiers.tx.verify_sigops_input(tx, enable_checkdatasig_count=True)
120+
best_block = self.manager.tx_storage.get_best_block()
121+
params = VerificationParams.default_for_mempool(best_block=best_block)
119122
# need to run verify_inputs first to check if all inputs exist
120-
verifiers.tx.verify_inputs(tx, skip_script=True)
123+
verifiers.tx.verify_inputs(tx, params, skip_script=True)
121124
verifiers.vertex.verify_parents(tx)
122125

123-
best_block = self.manager.tx_storage.get_best_block()
124126
block_storage = self.manager.get_nc_block_storage(best_block)
125127
verifiers.tx.verify_sum(self.manager._settings, tx, tx.get_complete_token_info(block_storage))
126128

hathor/transaction/scripts/execute.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,23 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from __future__ import annotations
16+
1517
import struct
1618
from dataclasses import dataclass
17-
from typing import NamedTuple, Optional, Union
19+
from typing import TYPE_CHECKING, NamedTuple, Optional, Union
1820

1921
from hathor.transaction import BaseTransaction, Transaction, TxInput
2022
from hathor.transaction.exceptions import DataIndexError, FinalStackInvalid, InvalidScriptError, OutOfData
2123

24+
if TYPE_CHECKING:
25+
from hathor.transaction.scripts.opcode import OpcodesVersion
26+
2227

2328
@dataclass(slots=True, frozen=True, kw_only=True)
2429
class ScriptExtras:
2530
tx: Transaction
31+
version: OpcodesVersion
2632

2733

2834
@dataclass(slots=True, frozen=True, kw_only=True)
@@ -72,7 +78,7 @@ def execute_eval(data: bytes, log: list[str], extras: ScriptExtras) -> None:
7278
continue
7379

7480
# this is an opcode manipulating the stack
75-
execute_op_code(Opcode(opcode), context)
81+
execute_op_code(Opcode(opcode), context, extras.version)
7682

7783
evaluate_final_stack(stack, log)
7884

@@ -94,7 +100,7 @@ def evaluate_final_stack(stack: Stack, log: list[str]) -> None:
94100
raise FinalStackInvalid('\n'.join(log))
95101

96102

97-
def script_eval(tx: Transaction, txin: TxInput, spent_tx: BaseTransaction) -> None:
103+
def script_eval(tx: Transaction, txin: TxInput, spent_tx: BaseTransaction, version: OpcodesVersion) -> None:
98104
"""Evaluates the output script and input data according to
99105
a very limited subset of Bitcoin's scripting language.
100106
@@ -112,7 +118,7 @@ def script_eval(tx: Transaction, txin: TxInput, spent_tx: BaseTransaction) -> No
112118
raw_script_eval(
113119
input_data=txin.data,
114120
output_script=spent_tx.outputs[txin.index].script,
115-
extras=UtxoScriptExtras(tx=tx, txin=txin, spent_tx=spent_tx),
121+
extras=UtxoScriptExtras(tx=tx, txin=txin, spent_tx=spent_tx, version=version),
116122
)
117123

118124

0 commit comments

Comments
 (0)