Skip to content

Commit cbe44f9

Browse files
author
Mark A. Greenslade
committed
1. Deploy from bytes serialisation added. 2. Unit tests improved. 3. Default crypto settings file.
1 parent 14c0411 commit cbe44f9

19 files changed

+671
-156
lines changed

pycspr/crypto/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from pycspr.crypto.cl_operations import get_account_key_algo
55
from pycspr.crypto.cl_operations import get_signature_for_deploy_approval
66
from pycspr.crypto.cl_operations import verify_deploy_approval_signature
7+
from pycspr.crypto.defaults import DEFAULT_HASH_ALGO
8+
from pycspr.crypto.defaults import DEFAULT_KEY_ALGO
79
from pycspr.crypto.ecc import get_key_pair
810
from pycspr.crypto.ecc import get_key_pair_from_base64
911
from pycspr.crypto.ecc import get_key_pair_from_bytes

pycspr/crypto/defaults.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from pycspr.crypto.enums import HashAlgorithm
2+
from pycspr.crypto.enums import KeyAlgorithm
3+
4+
DEFAULT_KEY_ALGO = KeyAlgorithm.ED25519
5+
DEFAULT_HASH_ALGO = HashAlgorithm.BLAKE2B

pycspr/crypto/ecc.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from pycspr.crypto import ecc_ed25519 as ed25519
66
from pycspr.crypto import ecc_secp256k1 as secp256k1
7+
from pycspr.crypto.defaults import DEFAULT_KEY_ALGO
78
from pycspr.crypto.enums import KeyAlgorithm
89

910

@@ -14,7 +15,7 @@
1415
}
1516

1617

17-
def get_key_pair(algo: KeyAlgorithm = KeyAlgorithm.ED25519) -> typing.Tuple[bytes, bytes]:
18+
def get_key_pair(algo: KeyAlgorithm = DEFAULT_KEY_ALGO) -> typing.Tuple[bytes, bytes]:
1819
"""Returns an ECC key pair, each key is a 32 byte array.
1920
2021
:param algo: Type of ECC algo to be used when generating key pair.
@@ -28,7 +29,7 @@ def get_key_pair(algo: KeyAlgorithm = KeyAlgorithm.ED25519) -> typing.Tuple[byte
2829

2930
def get_key_pair_from_bytes(
3031
pvk: bytes,
31-
algo: KeyAlgorithm = KeyAlgorithm.ED25519
32+
algo: KeyAlgorithm = DEFAULT_KEY_ALGO
3233
) -> typing.Tuple[bytes, bytes]:
3334
"""Returns a key pair mapped from a byte array representation of a private key.
3435
@@ -44,7 +45,7 @@ def get_key_pair_from_bytes(
4445

4546
def get_key_pair_from_base64(
4647
pvk_b64: str,
47-
algo: KeyAlgorithm = KeyAlgorithm.ED25519
48+
algo: KeyAlgorithm = DEFAULT_KEY_ALGO
4849
) -> typing.Tuple[bytes, bytes]:
4950
"""Returns a key pair mapped from a base 64 representation of a private key.
5051
@@ -58,7 +59,7 @@ def get_key_pair_from_base64(
5859

5960
def get_key_pair_from_hex_string(
6061
pvk_hex: str,
61-
algo: KeyAlgorithm = KeyAlgorithm.ED25519
62+
algo: KeyAlgorithm = DEFAULT_KEY_ALGO
6263
) -> typing.Tuple[bytes, bytes]:
6364
"""Returns an ECC key pair derived from a hexadecimal string encoded private key.
6465
@@ -72,7 +73,7 @@ def get_key_pair_from_hex_string(
7273

7374
def get_key_pair_from_pem_file(
7475
fpath: str,
75-
algo: KeyAlgorithm = KeyAlgorithm.ED25519
76+
algo: KeyAlgorithm = DEFAULT_KEY_ALGO
7677
) -> typing.Tuple[bytes, bytes]:
7778
"""Returns an ECC key pair derived from a previously persisted PEM file.
7879
@@ -88,7 +89,7 @@ def get_key_pair_from_pem_file(
8889

8990
def get_pvk_pem_from_bytes(
9091
pvk: bytes,
91-
algo: KeyAlgorithm = KeyAlgorithm.ED25519
92+
algo: KeyAlgorithm = DEFAULT_KEY_ALGO
9293
) -> bytes:
9394
"""Returns an ECC private key in PEM format.
9495
@@ -102,7 +103,7 @@ def get_pvk_pem_from_bytes(
102103

103104
def get_pvk_pem_from_hex_string(
104105
pvk: str,
105-
algo: KeyAlgorithm = KeyAlgorithm.ED25519
106+
algo: KeyAlgorithm = DEFAULT_KEY_ALGO
106107
) -> bytes:
107108
"""Returns an ECC private key mapped from a private key encoded as a hexadecial string.
108109
@@ -116,7 +117,7 @@ def get_pvk_pem_from_hex_string(
116117

117118
def get_pvk_pem_file_from_bytes(
118119
pvk: bytes,
119-
algo: KeyAlgorithm = KeyAlgorithm.ED25519
120+
algo: KeyAlgorithm = DEFAULT_KEY_ALGO
120121
) -> bytes:
121122
"""Returns path to a file containing an ECC private key in PEM format.
122123
@@ -136,7 +137,7 @@ def get_pvk_pem_file_from_bytes(
136137
def get_signature(
137138
msg_hash: bytes,
138139
pvk: bytes,
139-
algo: KeyAlgorithm = KeyAlgorithm.ED25519
140+
algo: KeyAlgorithm = DEFAULT_KEY_ALGO
140141
) -> bytes:
141142
"""Returns an ED25519 digital signature of data signed from a private key.
142143
@@ -152,7 +153,7 @@ def get_signature(
152153
def get_signature_from_pem_file(
153154
msg_hash: bytes,
154155
fpath: str,
155-
algo: KeyAlgorithm = KeyAlgorithm.ED25519
156+
algo: KeyAlgorithm = DEFAULT_KEY_ALGO
156157
) -> bytes:
157158
"""Returns an ED25519 digital signature of data signed from a private key PEM file.
158159
@@ -171,7 +172,7 @@ def is_signature_valid(
171172
msg_hash: bytes,
172173
sig: bytes,
173174
vk: bytes,
174-
algo: KeyAlgorithm = KeyAlgorithm.ED25519
175+
algo: KeyAlgorithm = DEFAULT_KEY_ALGO
175176
) -> bool:
176177
"""Returns a flag indicating whether a signature was signed by a signing key.
177178

pycspr/crypto/hashifier.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from pycspr.crypto.hashifier_blake2b import get_hash as blake2b
2+
from pycspr.crypto.defaults import DEFAULT_HASH_ALGO
23
from pycspr.crypto.enums import HashAlgorithm
34

45

@@ -14,9 +15,9 @@
1415
def get_hash(
1516
data: bytes,
1617
size: int = DIGEST_LENGTH,
17-
algo: HashAlgorithm = HashAlgorithm.BLAKE2B
18+
algo: HashAlgorithm = DEFAULT_HASH_ALGO
1819
) -> bytes:
19-
"""Maps input to a blake2b hash.
20+
"""Maps input to a hash function output.
2021
2122
:param data: Data to be hashed.
2223
:param size: Desired hashing output length.
Lines changed: 244 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,251 @@
1-
def decode(bstream: bytes, typedef: object) -> object:
1+
import typing
2+
3+
from pycspr.crypto import KeyAlgorithm
4+
from pycspr.factory import create_public_key
5+
from pycspr.serialisation.cl_type_from_bytes import decode as cl_type_from_bytes
6+
from pycspr.serialisation.cl_value_from_bytes import decode as cl_value_from_bytes
7+
from pycspr.types import Timestamp
8+
from pycspr.types import cl_types
9+
from pycspr.types import Deploy
10+
from pycspr.types import DeployApproval
11+
from pycspr.types import DeployArgument
12+
from pycspr.types import DeployBody
13+
from pycspr.types import DeployExecutableItem
14+
from pycspr.types import DeployHeader
15+
from pycspr.types import DeployTimeToLive
16+
from pycspr.types import ModuleBytes
17+
from pycspr.types import StoredContractByHash
18+
from pycspr.types import StoredContractByHashVersioned
19+
from pycspr.types import StoredContractByName
20+
from pycspr.types import StoredContractByNameVersioned
21+
from pycspr.types import Transfer
22+
23+
24+
def decode(bstream: bytes, typedef: object) -> typing.Tuple[bytes, object]:
225
"""Decodes a deploy from a byte array.
326
427
:param bstream: An array of bytes being decoded.
528
:param typedef: Deploy related type definition.
629
:returns: A deploy related type.
730
831
"""
9-
raise NotImplementedError(typedef, bstream)
32+
try:
33+
decoder = _DECODERS[typedef]
34+
except KeyError:
35+
raise ValueError(f"Cannot decode {typedef} from bytes")
36+
else:
37+
return decoder(bstream)
38+
39+
40+
def _decode_deploy(bstream: bytes) -> typing.Tuple[bytes, Deploy]:
41+
bstream, header = decode(bstream, DeployHeader)
42+
bstream, deploy_hash = cl_value_from_bytes(bstream, cl_types.CL_Type_ByteArray(32))
43+
bstream, payment = decode(bstream, DeployExecutableItem)
44+
bstream, session = decode(bstream, DeployExecutableItem)
45+
bstream, approvals = _decode_deploy_approval_set(bstream)
46+
47+
return bstream, Deploy(
48+
approvals=approvals,
49+
hash=deploy_hash.value,
50+
header=header,
51+
payment=payment,
52+
session=session
53+
)
54+
55+
56+
def _decode_deploy_approval(bstream: bytes) -> typing.Tuple[bytes, DeployApproval]:
57+
algo = KeyAlgorithm(bstream[0])
58+
if algo == KeyAlgorithm.ED25519:
59+
key_length = 32
60+
elif algo == KeyAlgorithm.SECP256K1:
61+
key_length = 33
62+
else:
63+
raise ValueError("Invalid Key Algorithm")
64+
pbk = bstream[1:key_length + 1]
65+
sig = bstream[key_length + 1:key_length + 66]
66+
bstream = bstream[1 + key_length + 66:]
67+
68+
return bstream, DeployApproval(
69+
signer=create_public_key(algo, pbk),
70+
signature=sig
71+
)
72+
73+
74+
def _decode_deploy_approval_set(bstream: bytes) -> typing.Tuple[bytes, typing.List[DeployApproval]]:
75+
approvals = []
76+
bstream, args_length = cl_value_from_bytes(bstream, cl_types.CL_Type_U32())
77+
for _ in range(args_length.value):
78+
bstream, approval = decode(bstream, DeployApproval)
79+
approvals.append(approval)
80+
81+
return bstream, approvals
82+
83+
84+
def _decode_deploy_argument(bstream: bytes) -> typing.Tuple[bytes, DeployArgument]:
85+
bstream, name = cl_value_from_bytes(bstream, cl_types.CL_Type_String())
86+
bstream, val_bytes_length = cl_value_from_bytes(bstream, cl_types.CL_Type_U32())
87+
bstream_rem, arg_cl_type = cl_type_from_bytes(bstream[val_bytes_length.value:])
88+
_, arg_cl_value = cl_value_from_bytes(bstream, arg_cl_type)
89+
90+
return bstream_rem, DeployArgument(name.value, arg_cl_value)
91+
92+
93+
def _decode_deploy_argument_set(bstream: bytes) -> typing.Tuple[bytes, typing.List[DeployArgument]]:
94+
args = []
95+
bstream, args_length = cl_value_from_bytes(bstream, cl_types.CL_Type_U32())
96+
for _ in range(args_length.value):
97+
bstream, arg = decode(bstream, DeployArgument)
98+
args.append(arg)
99+
100+
return bstream, args
101+
102+
103+
def _decode_deploy_body(bstream: bytes) -> typing.Tuple[bytes, DeployBody]:
104+
bstream, payment = _decode_deploy_executable_item(bstream)
105+
bstream, session = _decode_deploy_executable_item(bstream)
106+
bstream, body_hash = cl_value_from_bytes(bstream, cl_types.CL_Type_ByteArray(32))
107+
108+
return bstream, DeployBody(payment, session, body_hash.value)
109+
110+
111+
def _decode_deploy_executable_item(bstream: bytes) -> DeployExecutableItem:
112+
if bstream[0] == 0:
113+
return decode(bstream, ModuleBytes)
114+
elif bstream[0] == 1:
115+
return decode(bstream, StoredContractByHash)
116+
elif bstream[0] == 2:
117+
return decode(bstream, StoredContractByHashVersioned)
118+
elif bstream[0] == 3:
119+
return decode(bstream, StoredContractByName)
120+
elif bstream[0] == 4:
121+
return decode(bstream, StoredContractByNameVersioned)
122+
elif bstream[0] == 5:
123+
return decode(bstream, Transfer)
124+
125+
raise ValueError("Invalid deploy executable item type tag")
126+
127+
128+
def _decode_deploy_header(bstream: bytes) -> typing.Tuple[bytes, DeployHeader]:
129+
bstream, account_public_key = cl_value_from_bytes(
130+
bstream, cl_types.CL_Type_PublicKey()
131+
)
132+
bstream, timestamp = cl_value_from_bytes(
133+
bstream, cl_types.CL_Type_U64()
134+
)
135+
bstream, ttl = cl_value_from_bytes(
136+
bstream, cl_types.CL_Type_U64()
137+
)
138+
bstream, gas_price = cl_value_from_bytes(
139+
bstream, cl_types.CL_Type_U64()
140+
)
141+
bstream, body_hash = cl_value_from_bytes(
142+
bstream, cl_types.CL_Type_ByteArray(32)
143+
)
144+
bstream, dependencies = cl_value_from_bytes(
145+
bstream, cl_types.CL_Type_List(cl_types.CL_Type_ByteArray(32))
146+
)
147+
bstream, chain_name = cl_value_from_bytes(
148+
bstream, cl_types.CL_Type_String()
149+
)
150+
151+
return bstream, DeployHeader(
152+
account_public_key=account_public_key,
153+
body_hash=body_hash.value,
154+
chain_name=chain_name.value,
155+
dependencies=dependencies.vector,
156+
gas_price=gas_price.value,
157+
timestamp=Timestamp(timestamp.value / 1000),
158+
ttl=DeployTimeToLive.from_milliseconds(ttl.value)
159+
)
160+
161+
162+
def _decode_module_bytes(bstream: bytes) -> typing.Tuple[bytes, ModuleBytes]:
163+
bstream = bstream[1:]
164+
bstream, length = cl_value_from_bytes(bstream, cl_types.CL_Type_U32())
165+
if length.value > 0:
166+
module_bytes = bstream[:length.value]
167+
bstream = bstream[length.value:]
168+
else:
169+
module_bytes = bytes([])
170+
bstream, args = _decode_deploy_argument_set(bstream)
171+
172+
return bstream, ModuleBytes(args, module_bytes)
173+
174+
175+
def _decode_stored_contract_by_hash(bstream: bytes) -> typing.Tuple[bytes, StoredContractByHash]:
176+
bstream = bstream[1:]
177+
bstream, contract_hash = cl_value_from_bytes(bstream, cl_types.CL_Type_ByteArray(32))
178+
bstream, entry_point = cl_value_from_bytes(bstream, cl_types.CL_Type_String())
179+
bstream, args = _decode_deploy_argument_set(bstream)
180+
181+
return bstream, StoredContractByHash(
182+
args=args,
183+
entry_point=entry_point.value,
184+
hash=contract_hash.value
185+
)
186+
187+
188+
def _decode_stored_contract_by_hash_versioned(bstream: bytes) -> typing.Tuple[bytes, StoredContractByHashVersioned]:
189+
bstream = bstream[1:]
190+
bstream, contract_hash = cl_value_from_bytes(bstream, cl_types.CL_Type_ByteArray(32))
191+
bstream, contract_version = cl_value_from_bytes(bstream, cl_types.CL_Type_U32())
192+
bstream, entry_point = cl_value_from_bytes(bstream, cl_types.CL_Type_String())
193+
bstream, args = _decode_deploy_argument_set(bstream)
194+
195+
return bstream, StoredContractByHashVersioned(
196+
args=args,
197+
entry_point=entry_point.value,
198+
hash=contract_hash.value,
199+
version=contract_version.value
200+
)
201+
202+
203+
def _decode_stored_contract_by_name(bstream: bytes) -> typing.Tuple[bytes, StoredContractByName]:
204+
bstream = bstream[1:]
205+
bstream, contract_name = cl_value_from_bytes(bstream, cl_types.CL_Type_String())
206+
bstream, entry_point = cl_value_from_bytes(bstream, cl_types.CL_Type_String())
207+
bstream, args = _decode_deploy_argument_set(bstream)
208+
209+
return bstream, StoredContractByName(
210+
args=args,
211+
entry_point=entry_point.value,
212+
name=contract_name.value
213+
)
214+
215+
216+
def _decode_stored_contract_by_name_versioned(bstream: bytes) -> typing.Tuple[bytes, StoredContractByNameVersioned]:
217+
bstream = bstream[1:]
218+
bstream, contract_name = cl_value_from_bytes(bstream, cl_types.CL_Type_String())
219+
bstream, contract_version = cl_value_from_bytes(bstream, cl_types.CL_Type_U32())
220+
bstream, entry_point = cl_value_from_bytes(bstream, cl_types.CL_Type_String())
221+
bstream, args = _decode_deploy_argument_set(bstream)
222+
223+
return bstream, StoredContractByNameVersioned(
224+
args=args,
225+
entry_point=entry_point.value,
226+
name=contract_name.value,
227+
version=contract_version.value
228+
)
229+
230+
231+
def _decode_transfer(bstream: bytes) -> typing.Tuple[bytes, Transfer]:
232+
bstream = bstream[1:]
233+
bstream, args = _decode_deploy_argument_set(bstream)
234+
235+
return bstream, Transfer(args)
236+
237+
238+
_DECODERS = {
239+
Deploy: _decode_deploy,
240+
DeployApproval: _decode_deploy_approval,
241+
DeployArgument: _decode_deploy_argument,
242+
DeployBody: _decode_deploy_body,
243+
DeployExecutableItem: _decode_deploy_executable_item,
244+
DeployHeader: _decode_deploy_header,
245+
ModuleBytes: _decode_module_bytes,
246+
StoredContractByHash: _decode_stored_contract_by_hash,
247+
StoredContractByHashVersioned: _decode_stored_contract_by_hash_versioned,
248+
StoredContractByName: _decode_stored_contract_by_name,
249+
StoredContractByNameVersioned: _decode_stored_contract_by_name_versioned,
250+
Transfer: _decode_transfer
251+
}

0 commit comments

Comments
 (0)