tx-engine 0.7.4__cp314-cp314-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tx_engine/__init__.py ADDED
@@ -0,0 +1,14 @@
1
+ """
2
+ This is a tx_engine doc str
3
+ """
4
+ # noqa: F401 - 'x' - imported but unused
5
+
6
+ from tx_engine.tx_engine import Tx, TxIn, TxOut, Script, Stack, Wallet, p2pkh_script, hash160, hash256d, address_to_public_key_hash, public_key_to_address # noqa: F401
7
+ from tx_engine.tx_engine import sig_hash_preimage, sig_hash_preimage_checksig_index, sig_hash, sig_hash_checksig_index, wif_to_bytes, bytes_to_wif, wif_from_pw_nonce # noqa: F401
8
+ from tx_engine.engine.context import Context # noqa: F401
9
+ from tx_engine.engine.util import encode_num, decode_num # noqa: F401
10
+ from tx_engine.tx.sighash import SIGHASH # noqa: F401
11
+ from tx_engine.interface.interface_factory import interface_factory # noqa: F401
12
+ from tx_engine.interface.woc_interface import WoCInterface # noqa: F401
13
+ from tx_engine.interface.mock_interface import MockInterface # noqa: F401
14
+ from tx_engine.engine.cryptography_utils import create_wallet_from_pem_bytes, create_pem_from_wallet # noqa: F401
File without changes
@@ -0,0 +1,91 @@
1
+ """ This is the execution context for the script
2
+ """
3
+
4
+ from typing import Optional, List
5
+
6
+ from tx_engine.tx_engine import py_script_eval_pystack, Script, Stack
7
+
8
+
9
+ class Context:
10
+ """ This class captures an execution context for the script
11
+ """
12
+ def __init__(self, script: None | Script = None, ip_start: None | int = None, ip_limit: None | int = None, z: None | bytes = None):
13
+ """ Intial setup
14
+ """
15
+ self.ip_start: Optional[int]
16
+ self.ip_limit: Optional[int]
17
+ self.z: Optional[bytes]
18
+ self.stack: Stack = Stack()
19
+ self.alt_stack: Stack = Stack()
20
+
21
+ if script:
22
+ self.cmds = script.get_commands()
23
+ else:
24
+ self.cmds = []
25
+
26
+ self.ip_start = ip_start if ip_start else None
27
+ self.ip_limit = ip_limit if ip_limit else None
28
+ self.z = z if z else None
29
+
30
+ def set_commands(self, script: Script) -> None:
31
+ """ Set the commands
32
+ """
33
+ self.cmds = script.get_commands()
34
+
35
+ def reset_stacks(self) -> None:
36
+ """ Reset the stacks
37
+ """
38
+ self.stack = Stack()
39
+ self.alt_stack = Stack()
40
+
41
+ def evaluate_core(self, quiet: bool = False) -> bool:
42
+ """ evaluate_core calls the interpreter and returns the stacks
43
+ if quiet is true, dont print exceptions
44
+ """
45
+
46
+ try:
47
+ (self.stack, self.alt_stack, finish_loc) = py_script_eval_pystack(self.cmds, self.ip_start, self.ip_limit, self.z, self.stack, self.alt_stack)
48
+ except Exception as e:
49
+ if not quiet:
50
+ print(f"script_eval exception '{e}'")
51
+ return False
52
+ return True
53
+
54
+ def evaluate(self, quiet: bool = False) -> bool:
55
+ """ evaluate calls Evaluate_core and checks the stack has the correct value on return
56
+ if quiet is true, dont print exceptions
57
+ """
58
+ if not self.evaluate_core(quiet):
59
+ return False
60
+
61
+ if self.stack.size() == 0:
62
+ return False
63
+
64
+ # if the size is 1, then check the top element for either empty or zero
65
+ if self.stack.size() == 1:
66
+ # no entry or 0 => false.
67
+ if self.get_stack() == Stack([[]]) or self.get_stack() == Stack([[0]]):
68
+ return False
69
+
70
+ if self.get_stack()[0] == [0] or self.get_stack()[0] == []:
71
+ return False
72
+ return True
73
+
74
+ def get_stack(self) -> Stack:
75
+ """ Return the data stack as human readable
76
+ """
77
+ return self.stack
78
+
79
+ def get_altstack(self) -> Stack:
80
+ """ Return the get_altstack as human readable
81
+ """
82
+ return self.alt_stack
83
+
84
+ def get_stack_hex(self) -> List[str]:
85
+ return self.stack.get_stack_hex()
86
+
87
+ def set_ip_start(self, start: int) -> None:
88
+ self.ip_start = start
89
+
90
+ def set_ip_limit(self, limit: int) -> None:
91
+ self.ip_limit = limit
@@ -0,0 +1,46 @@
1
+ from tx_engine import Wallet
2
+
3
+ from cryptography.hazmat.primitives.serialization import load_pem_private_key
4
+ from cryptography.hazmat.backends import default_backend
5
+ from cryptography.hazmat.primitives.asymmetric import ec
6
+ from cryptography.hazmat.primitives import serialization
7
+
8
+
9
+ def bigint_to_private_key(private_key_int: int) -> ec.EllipticCurvePrivateKey:
10
+ '''Convert a BigInt to an ECDSA private key.
11
+ '''
12
+ return ec.derive_private_key(private_key_int, ec.SECP256K1())
13
+
14
+
15
+ def create_wallet_from_pem_file(pem_file_path: str, network: str) -> Wallet:
16
+ # Load the PEM file
17
+ with open(pem_file_path, 'rb') as pem_file:
18
+ pem_data = pem_file.read()
19
+
20
+ return create_wallet_from_pem_bytes(pem_data, network)
21
+
22
+
23
+ def create_wallet_from_pem_bytes(pem_data: bytes, network: str) -> Wallet:
24
+ # Load the private key from the PEM data
25
+ private_key = load_pem_private_key(pem_data, password=None, backend=default_backend())
26
+ assert isinstance(private_key, ec.EllipticCurvePrivateKey)
27
+
28
+ # Extract the private numbers (this includes the scalar/private key value)
29
+ private_numbers = private_key.private_numbers()
30
+
31
+ # The scalar value of the private key (as an integer)
32
+ private_key_scalar = private_numbers.private_value
33
+
34
+ # Convert the scalar value to bytes
35
+ private_key_bytes = private_key_scalar.to_bytes((private_key_scalar.bit_length() + 7) // 8, byteorder='big')
36
+
37
+ return Wallet.from_bytes(network, private_key_bytes)
38
+
39
+
40
+ def create_pem_from_wallet(user_wallet: Wallet) -> str:
41
+ ec_pri_key: ec.EllipticCurvePrivateKey = bigint_to_private_key(user_wallet.to_int())
42
+ pem = ec_pri_key.private_bytes(encoding=serialization.Encoding.PEM,
43
+ format=serialization.PrivateFormat.PKCS8,
44
+ encryption_algorithm=serialization.NoEncryption()
45
+ )
46
+ return pem.decode('utf-8')
@@ -0,0 +1,9 @@
1
+ """ This contains the base types used by tx-engine
2
+ """
3
+ from typing import List, Union, MutableSequence
4
+
5
+ Command = Union[int, bytes]
6
+ Commands = MutableSequence[Command]
7
+
8
+ StackElement = Union[int, bytes]
9
+ Stack = List[StackElement]
@@ -0,0 +1,110 @@
1
+ """ OP code number to name mapping
2
+ """
3
+ from typing import Dict
4
+
5
+ OP_CODE_NAMES: Dict[int, str] = {
6
+ 0: "OP_0",
7
+ 76: "OP_PUSHDATA1",
8
+ 77: "OP_PUSHDATA2",
9
+ 78: "OP_PUSHDATA4",
10
+ 79: "OP_1NEGATE",
11
+ 80: "OP_RESERVED",
12
+ 81: "OP_1",
13
+ 82: "OP_2",
14
+ 83: "OP_3",
15
+ 84: "OP_4",
16
+ 85: "OP_5",
17
+ 86: "OP_6",
18
+ 87: "OP_7",
19
+ 88: "OP_8",
20
+ 89: "OP_9",
21
+ 90: "OP_10",
22
+ 91: "OP_11",
23
+ 92: "OP_12",
24
+ 93: "OP_13",
25
+ 94: "OP_14",
26
+ 95: "OP_15",
27
+ 96: "OP_16",
28
+ 97: "OP_NOP",
29
+ 98: "OP_VER",
30
+ 99: "OP_IF",
31
+ 100: "OP_NOTIF",
32
+ 101: "OP_VERIF",
33
+ 102: "OP_NOTVERIF",
34
+ 103: "OP_ELSE",
35
+ 104: "OP_ENDIF",
36
+ 105: "OP_VERIFY",
37
+ 106: "OP_RETURN",
38
+ 107: "OP_TOALTSTACK",
39
+ 108: "OP_FROMALTSTACK",
40
+ 109: "OP_2DROP",
41
+ 110: "OP_2DUP",
42
+ 111: "OP_3DUP",
43
+ 112: "OP_2OVER",
44
+ 113: "OP_2ROT",
45
+ 114: "OP_2SWAP",
46
+ 115: "OP_IFDUP",
47
+ 116: "OP_DEPTH",
48
+ 117: "OP_DROP",
49
+ 118: "OP_DUP",
50
+ 119: "OP_NIP",
51
+ 120: "OP_OVER",
52
+ 121: "OP_PICK",
53
+ 122: "OP_ROLL",
54
+ 123: "OP_ROT",
55
+ 124: "OP_SWAP",
56
+ 125: "OP_TUCK",
57
+ 126: "OP_CAT",
58
+ 127: "OP_SPLIT",
59
+ 128: "OP_NUM2BIN",
60
+ 129: "OP_BIN2NUM",
61
+ 130: "OP_SIZE",
62
+ 131: "OP_INVERT",
63
+ 132: "OP_AND",
64
+ 133: "OP_OR",
65
+ 134: "OP_XOR",
66
+ 135: "OP_EQUAL",
67
+ 136: "OP_EQUALVERIFY",
68
+ 137: "OP_RESERVED1",
69
+ 138: "OP_RESERVED2",
70
+ 139: "OP_1ADD",
71
+ 140: "OP_1SUB",
72
+ 141: "OP_2MUL",
73
+ 142: "OP_2DIV",
74
+ 143: "OP_NEGATE",
75
+ 144: "OP_ABS",
76
+ 145: "OP_NOT",
77
+ 146: "OP_0NOTEQUAL",
78
+ 147: "OP_ADD",
79
+ 148: "OP_SUB",
80
+ 149: "OP_MUL",
81
+ 150: "OP_DIV",
82
+ 151: "OP_MOD",
83
+ 152: "OP_LSHIFT",
84
+ 153: "OP_RSHIFT",
85
+ 154: "OP_BOOLAND",
86
+ 155: "OP_BOOLOR",
87
+ 156: "OP_NUMEQUAL",
88
+ 157: "OP_NUMEQUALVERIFY",
89
+ 158: "OP_NUMNOTEQUAL",
90
+ 159: "OP_LESSTHAN",
91
+ 160: "OP_GREATERTHAN",
92
+ 161: "OP_LESSTHANOREQUAL",
93
+ 162: "OP_GREATERTHANOREQUAL",
94
+ 163: "OP_MIN",
95
+ 164: "OP_MAX",
96
+ 165: "OP_WITHIN",
97
+ 166: "OP_RIPEMD160",
98
+ 167: "OP_SHA1",
99
+ 168: "OP_SHA256",
100
+ 169: "OP_HASH160",
101
+ 170: "OP_HASH256",
102
+ 171: "OP_CODESEPARATOR",
103
+ 172: "OP_CHECKSIG",
104
+ 173: "OP_CHECKSIGVERIFY",
105
+ 174: "OP_CHECKMULTISIG",
106
+ 175: "OP_CHECKMULTISIGVERIFY",
107
+ 176: "OP_NOP1",
108
+ 177: "OP_CHECKLOCKTIMEVERIFY",
109
+ 178: "OP_CHECKSEQUENCEVERIFY"
110
+ }
@@ -0,0 +1,126 @@
1
+ """ OP codes as first class Python constants
2
+ """
3
+
4
+ from typing import Final
5
+ # Used to convert OP name to code
6
+
7
+ OP_0: Final = 0
8
+ OP_FALSE: Final = 0
9
+ OP_PUSHDATA1: Final = 0x4C
10
+ OP_PUSHDATA2: Final = 0x4D
11
+ OP_PUSHDATA4: Final = 0x4E
12
+ OP_1NEGATE: Final = 0x4F
13
+ OP_RESERVED: Final = 0x50
14
+ OP_TRUE: Final = 0x51
15
+ OP_1: Final = 0x51
16
+ OP_2: Final = 0x52
17
+ OP_3: Final = 0x53
18
+ OP_4: Final = 0x54
19
+ OP_5: Final = 0x55
20
+ OP_6: Final = 0x56
21
+ OP_7: Final = 0x57
22
+ OP_8: Final = 0x58
23
+ OP_9: Final = 0x59
24
+ OP_10: Final = 0x5A
25
+ OP_11: Final = 0x5B
26
+ OP_12: Final = 0x5C
27
+ OP_13: Final = 0x5D
28
+ OP_14: Final = 0x5E
29
+ OP_15: Final = 0x5F
30
+ OP_16: Final = 0x60
31
+
32
+ # Control
33
+ OP_NOP: Final = 0x61
34
+ OP_VER: Final = 0x62
35
+ OP_IF: Final = 0x63
36
+ OP_NOTIF: Final = 0x64
37
+ OP_VERIF: Final = 0x65
38
+ OP_VERNOTIF: Final = 0x66
39
+ OP_ELSE: Final = 0x67
40
+ OP_ENDIF: Final = 0x68
41
+ OP_VERIFY: Final = 0x69
42
+ OP_RETURN: Final = 0x6A
43
+
44
+ # stack ops
45
+ OP_TOALTSTACK: Final = 0x6B
46
+ OP_FROMALTSTACK: Final = 0x6C
47
+ OP_2DROP: Final = 0x6D
48
+ OP_2DUP: Final = 0x6E
49
+ OP_3DUP: Final = 0x6F
50
+ OP_2OVER: Final = 0x70
51
+ OP_2ROT: Final = 0x71
52
+ OP_2SWAP: Final = 0x72
53
+ OP_IFDUP: Final = 0x73
54
+ OP_DEPTH: Final = 0x74
55
+ OP_DROP: Final = 0x75
56
+ OP_DUP: Final = 0x76
57
+ OP_NIP: Final = 0x77
58
+ OP_OVER: Final = 0x78
59
+ OP_PICK: Final = 0x79
60
+ OP_ROLL: Final = 0x7A
61
+ OP_ROT: Final = 0x7B
62
+ OP_SWAP: Final = 0x7C
63
+ OP_TUCK: Final = 0x7D
64
+
65
+ # Splice ops - BSV
66
+ OP_CAT: Final = 0x7E
67
+ OP_SPLIT: Final = 0x7F
68
+ OP_NUM2BIN: Final = 0x80
69
+ OP_BIN2NUM: Final = 0x81
70
+ OP_SIZE: Final = 0x82
71
+ # 0x83{131} .. 0x86{134} - transaction invalid for non BSV
72
+
73
+ # Bit logic - BSV
74
+ OP_INVERT: Final = 0x83
75
+ OP_AND: Final = 0x84
76
+ OP_OR: Final = 0x85
77
+ OP_XOR: Final = 0x86
78
+ OP_EQUAL: Final = 0x87
79
+ OP_EQUALVERIFY: Final = 0x88
80
+ OP_RESERVED1: Final = 0x89
81
+ OP_RESERVED2: Final = 0x8A
82
+
83
+ # Artithmetic
84
+ OP_1ADD: Final = 0x8B
85
+ OP_1SUB: Final = 0x8C
86
+ OP_2MUL: Final = 0x8D
87
+ OP_2DIV: Final = 0x8E
88
+ OP_NEGATE: Final = 0x8F
89
+ OP_ABS: Final = 0x90
90
+ OP_NOT: Final = 0x91
91
+ OP_0NOTEQUAL: Final = 0x92
92
+ OP_ADD: Final = 0x93
93
+ OP_SUB: Final = 0x94
94
+ OP_MUL: Final = 0x95
95
+ OP_DIV: Final = 0x96
96
+ OP_MOD: Final = 0x97
97
+ OP_LSHIFT: Final = 0x98
98
+ OP_RSHIFT: Final = 0x99
99
+
100
+ OP_BOOLAND: Final = 0x9A
101
+ OP_BOOLOR: Final = 0x9B
102
+ OP_NUMEQUAL: Final = 0x9C
103
+ OP_NUMEQUALVERIFY: Final = 0x9D
104
+ OP_NUMNOTEQUAL: Final = 0x9E
105
+ OP_LESSTHAN: Final = 0x9F
106
+ OP_GREATERTHAN: Final = 0xA0
107
+ OP_LESSTHANOREQUAL: Final = 0xA1
108
+ OP_GREATERTHANOREQUAL: Final = 0xA2
109
+ OP_MIN: Final = 0xA3
110
+ OP_MAX: Final = 0xA4
111
+ OP_WITHIN: Final = 0xA5
112
+ OP_RIPEMD160: Final = 0xA6
113
+ OP_SHA1: Final = 0xA7
114
+ OP_SHA256: Final = 0xA8
115
+ OP_HASH160: Final = 0xA9
116
+ OP_HASH256: Final = 0xAA
117
+ OP_CODESEPARATOR: Final = 0xAB
118
+ OP_CHECKSIG: Final = 0xAC
119
+ OP_CHECKSIGVERIFY: Final = 0xAD
120
+ OP_CHECKMULTISIG: Final = 0xAE
121
+ OP_CHECKMULTISIGVERIFY: Final = 0xAF
122
+
123
+ # Expansion
124
+ OP_NOP1: Final = 0xB0
125
+ OP_CHECKLOCKTIMEVERIFY: Final = 0xB1
126
+ OP_CHECKSEQUENCEVERIFY: Final = 0xB2
@@ -0,0 +1,109 @@
1
+ """ Engine utility scripts and constants
2
+ """
3
+ from typing import List, Final
4
+
5
+ from .engine_types import StackElement
6
+
7
+ # Maximum script number length before Genesis (equal to CScriptNum::MAXIMUM_ELEMENT_SIZE)
8
+ MAX_SCRIPT_NUM_LENGTH_BEFORE_GENESIS: Final = 4
9
+ # Maximum script number length after Genesis
10
+ MAX_SCRIPT_NUM_LENGTH_AFTER_GENESIS: Final = 750 * 1000
11
+ # Maximum size that we are using
12
+ MAXIMUM_ELEMENT_SIZE: Final = MAX_SCRIPT_NUM_LENGTH_AFTER_GENESIS
13
+
14
+ # Curve constants
15
+ # perhaps we can source these from the secp256k1 library rather than here?
16
+ PRIME_INT: Final = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
17
+ GROUP_ORDER_INT: Final = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
18
+ HALF_GROUP_ORDER_INT: Final = GROUP_ORDER_INT // 2
19
+ Gx: Final = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
20
+ Gx_bytes: Final = bytes.fromhex('79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798')
21
+ Gy: Final = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
22
+ GUncompressed: Final = "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"
23
+
24
+
25
+ def encode_num(num: int) -> bytes:
26
+ """ Encode a number, return a bytearray in little endian
27
+ """
28
+ if num == 0:
29
+ return b""
30
+ abs_num = abs(num)
31
+ negative = num < 0
32
+ result = bytearray()
33
+ while abs_num:
34
+ result.append(abs_num & 0xFF)
35
+ abs_num >>= 8
36
+ if result[-1] & 0x80:
37
+ if negative:
38
+ result.append(0x80)
39
+ else:
40
+ result.append(0)
41
+ elif negative:
42
+ result[-1] |= 0x80
43
+ return bytes(result)
44
+
45
+
46
+ def is_minimally_encoded(element, max_element_size=MAXIMUM_ELEMENT_SIZE) -> bool:
47
+ """ Determines if an element is minimally encoded, returns True if it is.
48
+ Code copied from SV codebase for details see:
49
+ file: int_serialization.h, function: IsMinimallyEncoded, line: 98
50
+ """
51
+ if isinstance(element, int):
52
+ return True
53
+ size = len(element)
54
+ if size > max_element_size:
55
+ return False
56
+ if size > 0:
57
+ elem = element[::-1]
58
+ if elem[0] & 0x7f == 0:
59
+ if size <= 1 or (elem[1] & 0x80 == 0):
60
+ return False
61
+ return True
62
+
63
+
64
+ def decode_num(element: StackElement, check_encoding=False) -> int:
65
+ """ Take a byte(array), return a number
66
+ """
67
+ if element == b"":
68
+ return 0
69
+
70
+ if check_encoding and not is_minimally_encoded(element):
71
+ if isinstance(element, bytes):
72
+ raise ValueError(f"Value is not minimally encoded: {element.hex()}")
73
+ raise ValueError(f"Value is not minimally encoded: {element}")
74
+
75
+ if isinstance(element, int):
76
+ return element
77
+
78
+ if isinstance(element, bytes):
79
+ try:
80
+ b = element
81
+ big_endian = b[::-1]
82
+ except TypeError:
83
+ # TypeError: 'int' object is not subscriptable
84
+ return int(element)
85
+ if big_endian[0] & 0x80:
86
+ negative = True
87
+ result = big_endian[0] & 0x7F
88
+ else:
89
+ negative = False
90
+ result = big_endian[0]
91
+ for c in big_endian[1:]:
92
+ result <<= 8
93
+ result += c
94
+ if negative:
95
+ return -result
96
+ return result
97
+ else:
98
+ raise ValueError(f"Value is of unknown type: {element} {type(element)}")
99
+
100
+
101
+ def insert_num(val: int) -> List[int]:
102
+ """ This function is used to insert numbers into script
103
+ """
104
+ val_as_bytes = bytearray(encode_num(val))
105
+ length = len(val_as_bytes)
106
+ assert length < 0x4c, "Length of number too long, need to encode using OP_PUSHDATA"
107
+ # Insert the length
108
+ val_as_bytes.insert(0, length)
109
+ return list(val_as_bytes)
File without changes
@@ -0,0 +1,84 @@
1
+ """ This contains the base class for all blockchain interfaces
2
+ """
3
+
4
+ from abc import ABC, abstractmethod
5
+ from typing import Dict, Optional, MutableMapping, Any, List
6
+
7
+ ConfigType = MutableMapping[str, Any]
8
+
9
+
10
+ class BlockchainInterface(ABC):
11
+ """ This is a BlockchainInterface abstract base class
12
+ This will need to be extended with the used methods
13
+ """
14
+
15
+ @abstractmethod
16
+ def set_config(self, config: ConfigType):
17
+ """ Configures the interface based on the provided config
18
+ """
19
+
20
+ @abstractmethod
21
+ def get_utxo(self, address: str):
22
+ """ Given the address returns the associated UTXO
23
+ """
24
+
25
+ @abstractmethod
26
+ def get_block_count(self) -> int:
27
+ """ Returns the current block height
28
+ """
29
+
30
+ @abstractmethod
31
+ def get_raw_transaction(self, txid: str) -> Optional[str]:
32
+ """ Given the txid return the transaction
33
+ """
34
+
35
+ @abstractmethod
36
+ def get_transaction(self, txid: str) -> Dict:
37
+ """ Given the txid return the transaction as a dictionary
38
+ """
39
+
40
+ @abstractmethod
41
+ def broadcast_tx(self, tx: str):
42
+ """ Broadcast a transaction
43
+ """
44
+
45
+ @abstractmethod
46
+ def is_testnet(self) -> bool:
47
+ """ Return true if the interface is operating on testnet
48
+ """
49
+
50
+ @abstractmethod
51
+ def get_balance(self, address) -> int:
52
+ """ Return the balance associated with the provided address
53
+ """
54
+
55
+ @abstractmethod
56
+ def get_best_block_hash(self) -> str:
57
+ """ Return the current best block hash
58
+ """
59
+
60
+ @abstractmethod
61
+ def get_tx_out(self, txid: str, txindex: int) -> Dict:
62
+ """ abstract method definition to define the get_tx_out call to an RPC SV node
63
+ """
64
+
65
+ @abstractmethod
66
+ def get_block(self, blockhash: str) -> Dict:
67
+ """ Given the blockhash return the block as a dictionary
68
+ """
69
+
70
+ @abstractmethod
71
+ def get_merkle_proof(self, block_hash: str, tx_id: str) -> str:
72
+ """ Given the blockhash and tx_id return the merkle proof
73
+ """
74
+
75
+ @abstractmethod
76
+ def get_block_header(self, block_hash: str) -> Dict:
77
+ """ Given the block hash return the block header
78
+ """
79
+
80
+ @abstractmethod
81
+ def verifyscript(self, scripts: list, stop_on_first_invalid: bool = True, timeout: int = 100) -> List[Any]:
82
+ """ Given an script and context, verify the script
83
+ This call is only available from local RPC interface
84
+ """
@@ -0,0 +1,28 @@
1
+ """ This creates interfaces to the BSV blockchain
2
+ """
3
+ from .blockchain_interface import ConfigType
4
+ from .mock_interface import MockInterface
5
+ from .woc_interface import WoCInterface
6
+ from .rpc_interface import RPCInterface
7
+
8
+
9
+ INTERFACE_MAPPING = {
10
+ "mock": MockInterface,
11
+ "woc": WoCInterface,
12
+ "rpc": RPCInterface,
13
+ }
14
+
15
+
16
+ class InterfaceFactory:
17
+ """ A class for creating interfaces to the BSV blockchain """
18
+
19
+ def set_config(self, config: ConfigType) -> MockInterface | WoCInterface | RPCInterface:
20
+ """ Given a config returns the required configured Interface
21
+ """
22
+ interface_type = config['interface_type']
23
+ interface = INTERFACE_MAPPING[interface_type]() # type: ignore[abstract]
24
+ interface.set_config(config)
25
+ return interface # type: ignore[return-value]
26
+
27
+
28
+ interface_factory = InterfaceFactory()