tx-engine 0.6.6__cp312-none-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,13 @@
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, 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, wif_to_bytes # 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
File without changes
@@ -0,0 +1,116 @@
1
+ from typing import Optional
2
+
3
+ from tx_engine.tx_engine import py_script_eval, Script
4
+ from tx_engine.engine.util import decode_num
5
+
6
+
7
+ from .engine_types import Commands, Stack, StackElement
8
+ from .op_codes import OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4
9
+
10
+
11
+ def decode_element(elem: StackElement) -> int:
12
+ try:
13
+ retval = decode_num(bytes(elem))
14
+ except RuntimeError as e:
15
+ print(f"runtime error {e}")
16
+ retval = elem # type: ignore[assignment]
17
+ print(f"elem={elem}, retval={retval}, type={type(retval)}") # type: ignore[str-bytes-safe]
18
+ return retval
19
+
20
+
21
+ def cmds_as_bytes(cmds: Commands) -> bytes:
22
+ """ Given commands return bytes - prior to passing to Rust
23
+ """
24
+ retval = bytearray()
25
+ for c in cmds:
26
+ if isinstance(c, int):
27
+ retval += c.to_bytes(1, byteorder='big')
28
+ elif isinstance(c, list):
29
+ retval += cmds_as_bytes(c)
30
+ else:
31
+ # If we have a byte array without a preceeding length, add it, if less than 0x4c
32
+ # Otherwise would expect OP_PUSHDATA preceeding
33
+ if len(c) < 0x4c:
34
+ if len(retval) == 0:
35
+ retval += len(c).to_bytes(1, byteorder='big')
36
+ elif not retval[-1] in [OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4] and retval[-1] != len(c):
37
+ retval += len(c).to_bytes(1, byteorder='big')
38
+ retval += c
39
+ return bytes(retval)
40
+
41
+
42
+ class Context:
43
+ """ This class captures an execution context for the script
44
+ """
45
+ def __init__(self, script: None | Script = None, cmds: None | Commands = None, ip_limit: None | int = None, z: None | bytes = None):
46
+ self.cmds: Commands
47
+ self.ip_limit: Optional[int]
48
+ self.z: Optional[bytes]
49
+ self.stack: Stack = []
50
+ self.alt_stack: Stack = []
51
+ self.raw_stack: Stack = []
52
+ self.raw_alt_stack: Stack = []
53
+
54
+ if script:
55
+ self.cmds = script.get_commands()
56
+ elif cmds:
57
+ self.cmds = cmds[:]
58
+ else:
59
+ self.cmds = []
60
+
61
+ self.ip_limit = ip_limit if ip_limit else None
62
+ self.z = z if z else None
63
+
64
+ def set_commands(self, cmds: Commands) -> None:
65
+ self.cmds = cmds[:]
66
+
67
+ def reset_stacks(self) -> None:
68
+ self.stack = []
69
+ self.alt_stack = []
70
+ self.raw_stack = []
71
+ self.raw_alt_stack = []
72
+
73
+ def evaluate_core(self, quiet: bool = False) -> bool:
74
+ """ evaluate_core calls the interpreter and returns the stacks
75
+ if quiet is true, dont print exceptions
76
+ """
77
+ try:
78
+ cmds = cmds_as_bytes(self.cmds)
79
+ except Exception as e:
80
+ if not quiet:
81
+ print(f"cmds_as_bytes exception '{e}'")
82
+ return False
83
+ try:
84
+ (self.raw_stack, self.raw_alt_stack) = py_script_eval(cmds, self.ip_limit, self.z)
85
+ except Exception as e:
86
+ if not quiet:
87
+ print(f"script_eval exception '{e}'")
88
+ return False
89
+ else:
90
+ return True
91
+
92
+ def evaluate(self, quiet: bool = False) -> bool:
93
+ """ evaluate calls Evaluate_core and checks the stack has the correct value on return
94
+ if quiet is true, dont print exceptions
95
+ """
96
+ if not self.evaluate_core(quiet):
97
+ return False
98
+ self.stack = [decode_element(s) for s in self.raw_stack]
99
+ self.alt_stack = [decode_element(s) for s in self.raw_alt_stack]
100
+ if len(self.stack) == 0:
101
+ return False
102
+ if self.stack[-1] == 0: # was b""
103
+ return False
104
+ return True
105
+
106
+ def get_stack(self) -> Stack:
107
+ """ Return the data stack as human readable
108
+ """
109
+ self.stack = [decode_element(s) for s in self.raw_stack]
110
+ return self.stack
111
+
112
+ def get_altstack(self):
113
+ """ Return the get_altstack as human readable
114
+ """
115
+ self.alt_stack = [decode_element(s) for s in self.raw_alt_stack]
116
+ return self.alt_stack
@@ -0,0 +1,38 @@
1
+ from typing import Union
2
+
3
+ from .util import encode_num
4
+ from .op_code_names import OP_CODE_NAMES
5
+
6
+
7
+ # Create a dictionary of OP_DUP -> 118, DUP -> 118
8
+ OPS_STANDARD = {v: k for (k, v) in OP_CODE_NAMES.items()}
9
+ SHORT_TO_LONG_OP = {k.split("_")[1]: v for (k, v) in OPS_STANDARD.items()}
10
+ ALL_OPS = {**OPS_STANDARD, **SHORT_TO_LONG_OP}
11
+
12
+
13
+ def decode_op(op: str) -> Union[int, bytes]:
14
+ """ Given an op as string convert it to parsable value
15
+ e.g. "OP_2" -> 0x52
16
+ """
17
+ op = op.strip()
18
+ if op[:2] == "0x":
19
+ b: bytes = bytes.fromhex(op[2:])
20
+ return b
21
+
22
+ elif op in ALL_OPS:
23
+ n: int = ALL_OPS[op]
24
+ return n
25
+
26
+ else:
27
+ n = eval(op)
28
+ if isinstance(n, int):
29
+ x: bytes = encode_num(n)
30
+ return x
31
+ elif isinstance(n, str):
32
+ y = n.encode("utf-8")
33
+ return y
34
+ elif isinstance(n, bytes):
35
+ return n
36
+ else:
37
+ # have not captured conversion
38
+ assert 1 == 2 # should not get here
@@ -0,0 +1,7 @@
1
+ from typing import List, Union, MutableSequence
2
+
3
+ Command = Union[int, bytes]
4
+ Commands = MutableSequence[Command]
5
+
6
+ StackElement = Union[int, bytes]
7
+ Stack = List[StackElement]
@@ -0,0 +1,108 @@
1
+ from typing import Dict
2
+
3
+ OP_CODE_NAMES: Dict[int, str] = {
4
+ 0: "OP_0",
5
+ 76: "OP_PUSHDATA1",
6
+ 77: "OP_PUSHDATA2",
7
+ 78: "OP_PUSHDATA4",
8
+ 79: "OP_1NEGATE",
9
+ 80: "OP_RESERVED",
10
+ 81: "OP_1",
11
+ 82: "OP_2",
12
+ 83: "OP_3",
13
+ 84: "OP_4",
14
+ 85: "OP_5",
15
+ 86: "OP_6",
16
+ 87: "OP_7",
17
+ 88: "OP_8",
18
+ 89: "OP_9",
19
+ 90: "OP_10",
20
+ 91: "OP_11",
21
+ 92: "OP_12",
22
+ 93: "OP_13",
23
+ 94: "OP_14",
24
+ 95: "OP_15",
25
+ 96: "OP_16",
26
+ 97: "OP_NOP",
27
+ 98: "OP_VER",
28
+ 99: "OP_IF",
29
+ 100: "OP_NOTIF",
30
+ 101: "OP_VERIF",
31
+ 102: "OP_NOTVERIF",
32
+ 103: "OP_ELSE",
33
+ 104: "OP_ENDIF",
34
+ 105: "OP_VERIFY",
35
+ 106: "OP_RETURN",
36
+ 107: "OP_TOALTSTACK",
37
+ 108: "OP_FROMALTSTACK",
38
+ 109: "OP_2DROP",
39
+ 110: "OP_2DUP",
40
+ 111: "OP_3DUP",
41
+ 112: "OP_2OVER",
42
+ 113: "OP_2ROT",
43
+ 114: "OP_2SWAP",
44
+ 115: "OP_IFDUP",
45
+ 116: "OP_DEPTH",
46
+ 117: "OP_DROP",
47
+ 118: "OP_DUP",
48
+ 119: "OP_NIP",
49
+ 120: "OP_OVER",
50
+ 121: "OP_PICK",
51
+ 122: "OP_ROLL",
52
+ 123: "OP_ROT",
53
+ 124: "OP_SWAP",
54
+ 125: "OP_TUCK",
55
+ 126: "OP_CAT",
56
+ 127: "OP_SPLIT",
57
+ 128: "OP_NUM2BIN",
58
+ 129: "OP_BIN2NUM",
59
+ 130: "OP_SIZE",
60
+ 131: "OP_INVERT",
61
+ 132: "OP_AND",
62
+ 133: "OP_OR",
63
+ 134: "OP_XOR",
64
+ 135: "OP_EQUAL",
65
+ 136: "OP_EQUALVERIFY",
66
+ 137: "OP_RESERVED1",
67
+ 138: "OP_RESERVED2",
68
+ 139: "OP_1ADD",
69
+ 140: "OP_1SUB",
70
+ 141: "OP_2MUL", # disabled
71
+ 142: "OP_2DIV", # disabled
72
+ 143: "OP_NEGATE",
73
+ 144: "OP_ABS",
74
+ 145: "OP_NOT",
75
+ 146: "OP_0NOTEQUAL",
76
+ 147: "OP_ADD",
77
+ 148: "OP_SUB",
78
+ 149: "OP_MUL",
79
+ 150: "OP_DIV",
80
+ 151: "OP_MOD",
81
+ 152: "OP_LSHIFT",
82
+ 153: "OP_RSHIFT",
83
+ 154: "OP_BOOLAND",
84
+ 155: "OP_BOOLOR",
85
+ 156: "OP_NUMEQUAL",
86
+ 157: "OP_NUMEQUALVERIFY",
87
+ 158: "OP_NUMNOTEQUAL",
88
+ 159: "OP_LESSTHAN",
89
+ 160: "OP_GREATERTHAN",
90
+ 161: "OP_LESSTHANOREQUAL",
91
+ 162: "OP_GREATERTHANOREQUAL",
92
+ 163: "OP_MIN",
93
+ 164: "OP_MAX",
94
+ 165: "OP_WITHIN",
95
+ 166: "OP_RIPEMD160",
96
+ 167: "OP_SHA1",
97
+ 168: "OP_SHA256",
98
+ 169: "OP_HASH160",
99
+ 170: "OP_HASH256",
100
+ 171: "OP_CODESEPARATOR",
101
+ 172: "OP_CHECKSIG",
102
+ 173: "OP_CHECKSIGVERIFY",
103
+ 174: "OP_CHECKMULTISIG",
104
+ 175: "OP_CHECKMULTISIGVERIFY",
105
+ 176: "OP_NOP1",
106
+ 177: "OP_CHECKLOCKTIMEVERIFY",
107
+ 178: "OP_CHECKSEQUENCEVERIFY"
108
+ }
@@ -0,0 +1,122 @@
1
+ # Used to convert OP name to code
2
+
3
+ OP_0 = 0
4
+ OP_FALSE = 0
5
+ OP_PUSHDATA1 = 0x4C
6
+ OP_PUSHDATA2 = 0x4D
7
+ OP_PUSHDATA4 = 0x4E
8
+ OP_1NEGATE = 0x4F
9
+ OP_RESERVED = 0x50
10
+ OP_TRUE = 0x51
11
+ OP_1 = 0x51
12
+ OP_2 = 0x52
13
+ OP_3 = 0x53
14
+ OP_4 = 0x54
15
+ OP_5 = 0x55
16
+ OP_6 = 0x56
17
+ OP_7 = 0x57
18
+ OP_8 = 0x58
19
+ OP_9 = 0x59
20
+ OP_10 = 0x5A
21
+ OP_11 = 0x5B
22
+ OP_12 = 0x5C
23
+ OP_13 = 0x5D
24
+ OP_14 = 0x5E
25
+ OP_15 = 0x5F
26
+ OP_16 = 0x60
27
+
28
+ # Control
29
+ OP_NOP = 0x61
30
+ OP_VER = 0x62
31
+ OP_IF = 0x63
32
+ OP_NOTIF = 0x64
33
+ OP_VERIF = 0x65
34
+ OP_VERNOTIF = 0x66
35
+ OP_ELSE = 0x67
36
+ OP_ENDIF = 0x68
37
+ OP_VERIFY = 0x69
38
+ OP_RETURN = 0x6A
39
+
40
+ # stack ops
41
+ OP_TOALTSTACK = 0x6B
42
+ OP_FROMALTSTACK = 0x6C
43
+ OP_2DROP = 0x6D
44
+ OP_2DUP = 0x6E
45
+ OP_3DUP = 0x6F
46
+ OP_2OVER = 0x70
47
+ OP_2ROT = 0x71
48
+ OP_2SWAP = 0x72
49
+ OP_IFDUP = 0x73
50
+ OP_DEPTH = 0x74
51
+ OP_DROP = 0x75
52
+ OP_DUP = 0x76
53
+ OP_NIP = 0x77
54
+ OP_OVER = 0x78
55
+ OP_PICK = 0x79
56
+ OP_ROLL = 0x7A
57
+ OP_ROT = 0x7B
58
+ OP_SWAP = 0x7C
59
+ OP_TUCK = 0x7D
60
+
61
+ # Splice ops - BSV
62
+ OP_CAT = 0x7E
63
+ OP_SPLIT = 0x7F
64
+ OP_NUM2BIN = 0x80
65
+ OP_BIN2NUM = 0x81
66
+ OP_SIZE = 0x82
67
+ # 0x83{131} .. 0x86{134} - transaction invalid for non BSV
68
+
69
+ # Bit logic - BSV
70
+ OP_INVERT = 0x83
71
+ OP_AND = 0x84
72
+ OP_OR = 0x85
73
+ OP_XOR = 0x86
74
+ OP_EQUAL = 0x87
75
+ OP_EQUALVERIFY = 0x88
76
+ OP_RESERVED1 = 0x89
77
+ OP_RESERVED2 = 0x8A
78
+
79
+ # Artithmetic
80
+ OP_1ADD = 0x8B
81
+ OP_1SUB = 0x8C
82
+ OP_2MUL = 0x8D # Disabled
83
+ OP_2DIV = 0x8E # Disabled
84
+ OP_NEGATE = 0x8F
85
+ OP_ABS = 0x90
86
+ OP_NOT = 0x91
87
+ OP_0NOTEQUAL = 0x92
88
+ OP_ADD = 0x93
89
+ OP_SUB = 0x94
90
+ OP_MUL = 0x95
91
+ OP_DIV = 0x96
92
+ OP_MOD = 0x97
93
+ OP_LSHIFT = 0x98
94
+ OP_RSHIFT = 0x99
95
+
96
+ OP_BOOLAND = 0x9A
97
+ OP_BOOLOR = 0x9B
98
+ OP_NUMEQUAL = 0x9C
99
+ OP_NUMEQUALVERIFY = 0x9D
100
+ OP_NUMNOTEQUAL = 0x9E
101
+ OP_LESSTHAN = 0x9F
102
+ OP_GREATERTHAN = 0xA0
103
+ OP_LESSTHANOREQUAL = 0xA1
104
+ OP_GREATERTHANOREQUAL = 0xA2
105
+ OP_MIN = 0xA3
106
+ OP_MAX = 0xA4
107
+ OP_WITHIN = 0xA5
108
+ OP_RIPEMD160 = 0xA6
109
+ OP_SHA1 = 0xA7
110
+ OP_SHA256 = 0xA8
111
+ OP_HASH160 = 0xA9
112
+ OP_HASH256 = 0xAA
113
+ OP_CODESEPARATOR = 0xAB
114
+ OP_CHECKSIG = 0xAC
115
+ OP_CHECKSIGVERIFY = 0xAD
116
+ OP_CHECKMULTISIG = 0xAE
117
+ OP_CHECKMULTISIGVERIFY = 0xAF
118
+
119
+ # Expansion
120
+ OP_NOP1 = 0xB0
121
+ OP_CHECKLOCKTIMEVERIFY = 0xB1
122
+ OP_CHECKSEQUENCEVERIFY = 0xB2
@@ -0,0 +1,109 @@
1
+ from typing import List
2
+
3
+ from .engine_types import StackElement
4
+
5
+ # Maximum script number length before Genesis (equal to CScriptNum::MAXIMUM_ELEMENT_SIZE)
6
+ MAX_SCRIPT_NUM_LENGTH_BEFORE_GENESIS = 4
7
+ # Maximum script number length after Genesis
8
+ MAX_SCRIPT_NUM_LENGTH_AFTER_GENESIS = 750 * 1000
9
+ # Maximum size that we are using
10
+ MAXIMUM_ELEMENT_SIZE = MAX_SCRIPT_NUM_LENGTH_AFTER_GENESIS
11
+
12
+ # Curve constants
13
+ # perhaps we can source these from the secp256k1 library rather than here?
14
+ PRIME_INT = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
15
+ GROUP_ORDER_INT = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
16
+ HALF_GROUP_ORDER_INT = GROUP_ORDER_INT // 2
17
+ Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
18
+ Gx_bytes = bytes.fromhex('79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798')
19
+ Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
20
+ GUncompressed = "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"
21
+
22
+
23
+ def encode_num(num: int) -> bytes:
24
+ """ Encode a number, return a bytearray in little endian
25
+ """
26
+ if num == 0:
27
+ return b""
28
+ abs_num = abs(num)
29
+ negative = num < 0
30
+ result = bytearray()
31
+ while abs_num:
32
+ result.append(abs_num & 0xFF)
33
+ abs_num >>= 8
34
+ if result[-1] & 0x80:
35
+ if negative:
36
+ result.append(0x80)
37
+ else:
38
+ result.append(0)
39
+ elif negative:
40
+ result[-1] |= 0x80
41
+ return bytes(result)
42
+
43
+
44
+ def is_minimally_encoded(element, max_element_size=MAXIMUM_ELEMENT_SIZE) -> bool:
45
+ """ Determines if an element is minimally encoded, returns True if it is.
46
+ Code copied from SV codebase for details see:
47
+ file: int_serialization.h, function: IsMinimallyEncoded, line: 98
48
+ """
49
+ if isinstance(element, int):
50
+ return True
51
+ size = len(element)
52
+ if size > max_element_size:
53
+ return False
54
+ if size > 0:
55
+ elem = element[::-1]
56
+ if elem[0] & 0x7f == 0:
57
+ if size <= 1 or (elem[1] & 0x80 == 0):
58
+ return False
59
+ return True
60
+
61
+
62
+ def decode_num(element: StackElement, check_encoding=False) -> int:
63
+ """ Take a byte(array), return a number
64
+ """
65
+ if element == b"":
66
+ return 0
67
+
68
+ if check_encoding and not is_minimally_encoded(element):
69
+ if isinstance(element, bytes):
70
+ raise ValueError(f"Value is not minimally encoded: {element.hex()}")
71
+ else:
72
+ raise ValueError(f"Value is not minimally encoded: {element}")
73
+
74
+ if isinstance(element, int):
75
+ return element
76
+
77
+ elif isinstance(element, bytes):
78
+ try:
79
+ b = element
80
+ big_endian = b[::-1]
81
+ except TypeError:
82
+ # TypeError: 'int' object is not subscriptable
83
+ return int(element)
84
+ if big_endian[0] & 0x80:
85
+ negative = True
86
+ result = big_endian[0] & 0x7F
87
+ else:
88
+ negative = False
89
+ result = big_endian[0]
90
+ for c in big_endian[1:]:
91
+ result <<= 8
92
+ result += c
93
+ if negative:
94
+ return -result
95
+ else:
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,79 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Dict, Optional, MutableMapping, Any, List
3
+
4
+ ConfigType = MutableMapping[str, Any]
5
+
6
+
7
+ class BlockchainInterface(ABC):
8
+ """ This is a BlockchainInterface abstract base class
9
+ This will need to be extended with the used methods
10
+ """
11
+
12
+ @abstractmethod
13
+ def set_config(self, config: ConfigType):
14
+ pass
15
+
16
+ @abstractmethod
17
+ def get_utxo(self, address: str):
18
+ pass
19
+
20
+ @abstractmethod
21
+ def get_block_count(self) -> int:
22
+ pass
23
+
24
+ @abstractmethod
25
+ def get_raw_transaction(self, txid: str) -> Optional[str]:
26
+ pass
27
+
28
+ @abstractmethod
29
+ def get_transaction(self, txid: str) -> Dict:
30
+ pass
31
+
32
+ @abstractmethod
33
+ def broadcast_tx(self, tx: str):
34
+ pass
35
+
36
+ @abstractmethod
37
+ def is_testnet(self) -> bool:
38
+ pass
39
+
40
+ @abstractmethod
41
+ def get_balance(self, address) -> int:
42
+ pass
43
+
44
+ """ abstract method definition for get_best_block_hash
45
+ """
46
+ @abstractmethod
47
+ def get_best_block_hash(self) -> str:
48
+ pass
49
+
50
+ """ abstract method definition to define the get_tx_out call to an RPC SV node
51
+ """
52
+ @abstractmethod
53
+ def get_tx_out(self, txid: str, txindex: int) -> Dict:
54
+ pass
55
+
56
+ """ abstract method definition to define an interface for getblock
57
+ """
58
+ @abstractmethod
59
+ def get_block(self, blockhash: str) -> Dict:
60
+ pass
61
+
62
+ """ absract method definition for merkle proof retrieval
63
+ """
64
+ @abstractmethod
65
+ def get_merkle_proof(self, block_hash: str, tx_id: str) -> str:
66
+ pass
67
+
68
+ ''' abstract method definition for getting block headers from WoC
69
+ '''
70
+ @abstractmethod
71
+ def get_block_header(self, block_hash: str) -> Dict:
72
+ pass
73
+
74
+ ''' abstract method definition for executing verify script.
75
+ This call is not available from WoC
76
+ '''
77
+ @abstractmethod
78
+ def verifyscript(self, scripts: list, stopOnFirstInvalid: bool = True, totalTimeout: int = 100) -> List[Any]:
79
+ pass
@@ -0,0 +1,24 @@
1
+ from .blockchain_interface import ConfigType
2
+ from .mock_interface import MockInterface
3
+ from .woc_interface import WoCInterface
4
+ from .rpc_interface import RPCInterface
5
+
6
+
7
+ INTERFACE_MAPPING = {
8
+ "mock": MockInterface,
9
+ "woc": WoCInterface,
10
+ "rpc": RPCInterface,
11
+ }
12
+
13
+
14
+ class InterfaceFactory:
15
+ """ A class for creating interfaces to the BSV blockchain """
16
+
17
+ def set_config(self, config: ConfigType) -> MockInterface | WoCInterface | RPCInterface:
18
+ interface_type = config['interface_type']
19
+ interface = INTERFACE_MAPPING[interface_type]() # type: ignore[abstract]
20
+ interface.set_config(config)
21
+ return interface # type: ignore[return-value]
22
+
23
+
24
+ interface_factory = InterfaceFactory()