Skip to content

Commit 5110f14

Browse files
committed
admin_key private public user-friendly and docs
1 parent 44fa7a6 commit 5110f14

File tree

6 files changed

+108
-18
lines changed

6 files changed

+108
-18
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ transaction = (
109109
.freeze_with(client)
110110
)
111111
112+
transaction.sign(admin_key)
112113
transaction.sign(operator_key)
113114
transaction.execute(client)
114115
```
@@ -148,9 +149,10 @@ transaction = (
148149
TokenDeleteTransaction()
149150
.set_token_id(token_id)
150151
.freeze_with(client)
151-
.sign(operator_key)
152152
)
153+
153154
transaction.sign(admin_key)
155+
transaction.sign(operator_key)
154156
transaction.execute(client)
155157
```
156158

examples/token_create.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from src.crypto.private_key import PrivateKey
1111
from src.tokens.token_create_transaction import TokenCreateTransaction
1212
from src.client.network import Network
13+
from cryptography.hazmat.primitives import serialization
1314

1415
load_dotenv()
1516

@@ -20,7 +21,7 @@ def create_token():
2021
operator_id = AccountId.from_string(os.getenv('OPERATOR_ID'))
2122
operator_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY'))
2223
admin_key = PrivateKey.from_string(os.getenv('ADMIN_KEY'))
23-
24+
2425
client.set_operator(operator_id, operator_key)
2526

2627
transaction = (
@@ -33,8 +34,9 @@ def create_token():
3334
.set_admin_key(admin_key)
3435
.freeze_with(client)
3536
.sign(operator_key)
37+
.sign(admin_key)
3638
)
37-
39+
3840
try:
3941
receipt = transaction.execute(client)
4042
if receipt and receipt.tokenId:

src/tokens/token_create_transaction.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from src.proto import token_create_pb2
33
from src.response_code import ResponseCode
44
from src.proto import basic_types_pb2
5+
from cryptography.hazmat.primitives import serialization
56

67
class TokenCreateTransaction(Transaction):
78
"""
@@ -77,17 +78,21 @@ def build_transaction_body(self):
7778
]):
7879
raise ValueError("Missing required fields")
7980

80-
key = basic_types_pb2.Key(
81-
ed25519=self.admin_key if self.admin_key else None
82-
)
81+
admin_key_proto = None
82+
if self.admin_key:
83+
admin_public_key_bytes = self.admin_key.public_key().public_bytes(
84+
encoding=serialization.Encoding.Raw,
85+
format=serialization.PublicFormat.Raw
86+
)
87+
admin_key_proto = basic_types_pb2.Key(ed25519=admin_public_key_bytes)
8388

8489
token_create_body = token_create_pb2.TokenCreateTransactionBody(
8590
name=self.token_name,
8691
symbol=self.token_symbol,
8792
decimals=self.decimals,
8893
initialSupply=self.initial_supply,
8994
treasury=self.treasury_account_id.to_proto(),
90-
adminKey=key if self.admin_key else None
95+
adminKey=admin_key_proto
9196
)
9297

9398
transaction_body = self.build_base_transaction_body()

test.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,11 @@
55
from src.account.account_id import AccountId
66
from src.account.account_create_transaction import AccountCreateTransaction
77
from src.crypto.private_key import PrivateKey
8-
from src.crypto.public_key import PublicKey
98
from src.tokens.token_create_transaction import TokenCreateTransaction
109
from src.tokens.token_associate_transaction import TokenAssociateTransaction
1110
from src.transaction.transfer_transaction import TransferTransaction
1211
from src.response_code import ResponseCode
1312
from src.tokens.token_delete_transaction import TokenDeleteTransaction
14-
from cryptography.hazmat.primitives import serialization
1513

1614
def load_operator_credentials():
1715
"""Load operator credentials from environment variables."""
@@ -56,18 +54,15 @@ def create_new_account(client, initial_balance=100000000):
5654

5755
def create_token(client, operator_id, admin_key):
5856
"""Create a new token and return its TokenId instance."""
59-
admin_public_key_bytes = admin_key.public_key().public_bytes(
60-
encoding=serialization.Encoding.Raw,
61-
format=serialization.PublicFormat.Raw
62-
)
57+
6358
transaction = (
6459
TokenCreateTransaction()
6560
.set_token_name("ExampleToken")
6661
.set_token_symbol("EXT")
6762
.set_decimals(2)
6863
.set_initial_supply(1000)
6964
.set_treasury_account_id(operator_id)
70-
.set_admin_key(admin_public_key_bytes)
65+
.set_admin_key(admin_key)
7166
.freeze_with(client)
7267
)
7368
transaction.sign(admin_key)

tests/conftest.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import pytest
22
from src.account.account_id import AccountId
33
from src.tokens.token_id import TokenId
4+
from src.crypto.private_key import PrivateKey
5+
from cryptography.hazmat.primitives import serialization
46

57
@pytest.fixture
68
def mock_account_ids():
@@ -11,3 +13,14 @@ def mock_account_ids():
1113
token_id_1 = TokenId(1, 1, 1)
1214
token_id_2 = TokenId(2, 2, 2)
1315
return account_id_sender, account_id_recipient, node_account_id, token_id_1, token_id_2
16+
17+
@pytest.fixture
18+
def admin_key():
19+
admin_key = PrivateKey.generate()
20+
21+
admin_public_key_bytes = admin_key.public_key().public_bytes(
22+
encoding=serialization.Encoding.Raw,
23+
format=serialization.PublicFormat.Raw
24+
)
25+
26+
return admin_key, admin_public_key_bytes

tests/test_token_create_transaction.py

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from src.tokens.token_create_transaction import TokenCreateTransaction
44
from src.proto import basic_types_pb2, timestamp_pb2
55
from src.transaction.transaction_id import TransactionId
6+
from src.proto import transaction_pb2, transaction_body_pb2
7+
from cryptography.hazmat.primitives import serialization
68

79
def generate_transaction_id(account_id_proto):
810
"""Generate a unique transaction ID based on the account ID and the current timestamp."""
@@ -19,7 +21,28 @@ def generate_transaction_id(account_id_proto):
1921
)
2022
return tx_id
2123

22-
def test_build_transaction_body(mock_account_ids):
24+
def test_build_transaction_body_without_admin_key(mock_account_ids):
25+
"""Test building a token creation transaction body without an admin key."""
26+
treasury_account, _, node_account_id, _, _ = mock_account_ids
27+
28+
token_tx = TokenCreateTransaction()
29+
token_tx.set_token_name("MyToken")
30+
token_tx.set_token_symbol("MTK")
31+
token_tx.set_decimals(2)
32+
token_tx.set_initial_supply(1000)
33+
token_tx.set_treasury_account_id(treasury_account)
34+
token_tx.transaction_id = generate_transaction_id(treasury_account)
35+
token_tx.node_account_id = node_account_id
36+
37+
transaction_body = token_tx.build_transaction_body()
38+
39+
assert transaction_body.tokenCreation.name == "MyToken"
40+
assert transaction_body.tokenCreation.symbol == "MTK"
41+
assert transaction_body.tokenCreation.decimals == 2
42+
assert transaction_body.tokenCreation.initialSupply == 1000
43+
assert not transaction_body.tokenCreation.HasField("adminKey")
44+
45+
def test_build_transaction_body(mock_account_ids, admin_key):
2346
"""Test building a token creation transaction body with valid values."""
2447
treasury_account, _, node_account_id, _, _ = mock_account_ids
2548

@@ -30,6 +53,7 @@ def test_build_transaction_body(mock_account_ids):
3053
token_tx.set_initial_supply(1000)
3154
token_tx.set_treasury_account_id(treasury_account)
3255
token_tx.transaction_id = generate_transaction_id(treasury_account)
56+
token_tx.set_admin_key(admin_key[0])
3357
token_tx.node_account_id = node_account_id
3458

3559
transaction_body = token_tx.build_transaction_body()
@@ -38,6 +62,7 @@ def test_build_transaction_body(mock_account_ids):
3862
assert transaction_body.tokenCreation.symbol == "MTK"
3963
assert transaction_body.tokenCreation.decimals == 2
4064
assert transaction_body.tokenCreation.initialSupply == 1000
65+
assert transaction_body.tokenCreation.adminKey.ed25519 == admin_key[1]
4166

4267
def test_missing_fields():
4368
"""Test that building a transaction without required fields raises a ValueError."""
@@ -48,6 +73,7 @@ def test_missing_fields():
4873
def test_sign_transaction(mock_account_ids):
4974
"""Test signing the token creation transaction with a private key."""
5075
treasury_account, _, node_account_id, _, _ = mock_account_ids
76+
5177
token_tx = TokenCreateTransaction()
5278
token_tx.set_token_name("MyToken")
5379
token_tx.set_token_symbol("MTK")
@@ -61,32 +87,79 @@ def test_sign_transaction(mock_account_ids):
6187
private_key.sign.return_value = b'signature'
6288
private_key.public_key().public_bytes.return_value = b'public_key'
6389

90+
private_key_admin = MagicMock()
91+
private_key_admin.sign.return_value = b'admin_signature'
92+
private_key_admin.public_key().public_bytes.return_value = b'admin_public_key'
93+
6494
token_tx.sign(private_key)
95+
token_tx.sign(private_key_admin)
96+
97+
assert len(token_tx.signature_map.sigPair) == 2
6598

66-
assert len(token_tx.signature_map.sigPair) == 1
6799
sig_pair = token_tx.signature_map.sigPair[0]
68100
assert sig_pair.pubKeyPrefix == b'public_key'
69101
assert sig_pair.ed25519 == b'signature'
70102

103+
sig_pair_admin = token_tx.signature_map.sigPair[1]
104+
assert sig_pair_admin.pubKeyPrefix == b'admin_public_key'
105+
assert sig_pair_admin.ed25519 == b'admin_signature'
71106

72-
def test_to_proto(mock_account_ids):
107+
def test_to_proto_without_admin_key(mock_account_ids):
108+
"""Test protobuf conversion when admin key is not set."""
109+
treasury_account, _, node_account_id, _, _ = mock_account_ids
110+
111+
token_tx = TokenCreateTransaction()
112+
token_tx.set_token_name("MyToken")
113+
token_tx.set_token_symbol("MTK")
114+
token_tx.set_decimals(2)
115+
token_tx.set_initial_supply(1000)
116+
token_tx.set_treasury_account_id(treasury_account)
117+
token_tx.transaction_id = generate_transaction_id(treasury_account)
118+
token_tx.node_account_id = node_account_id
119+
120+
private_key = MagicMock()
121+
private_key.sign.return_value = b'signature'
122+
private_key.public_key().public_bytes.return_value = b'public_key'
123+
124+
token_tx.sign(private_key)
125+
proto = token_tx.to_proto()
126+
127+
assert len(proto.signedTransactionBytes) > 0
128+
129+
transaction = transaction_pb2.Transaction.FromString(proto.signedTransactionBytes)
130+
transaction_body = transaction_body_pb2.TransactionBody.FromString(transaction.bodyBytes)
131+
132+
assert not transaction_body.tokenCreation.HasField("adminKey")
133+
134+
def test_to_proto(mock_account_ids, admin_key):
73135
"""Test converting the token creation transaction to protobuf format after signing."""
74136
treasury_account, _, node_account_id, _, _ = mock_account_ids
137+
75138
token_tx = TokenCreateTransaction()
76139
token_tx.set_token_name("MyToken")
77140
token_tx.set_token_symbol("MTK")
78141
token_tx.set_decimals(2)
79142
token_tx.set_initial_supply(1000)
80143
token_tx.set_treasury_account_id(treasury_account)
144+
token_tx.set_admin_key(admin_key[0])
81145
token_tx.transaction_id = generate_transaction_id(treasury_account)
82146
token_tx.node_account_id = node_account_id
83147

84148
private_key = MagicMock()
85149
private_key.sign.return_value = b'signature'
86150
private_key.public_key().public_bytes.return_value = b'public_key'
87151

152+
private_key_admin = MagicMock()
153+
private_key_admin.sign.return_value = b'admin_signature'
154+
private_key_admin.public_key().public_bytes.return_value = b'admin_public_key'
155+
88156
token_tx.sign(private_key)
157+
token_tx.sign(private_key_admin)
89158
proto = token_tx.to_proto()
90159

91-
assert proto.signedTransactionBytes
92160
assert len(proto.signedTransactionBytes) > 0
161+
162+
transaction = transaction_pb2.Transaction.FromString(proto.signedTransactionBytes)
163+
transaction_body = transaction_body_pb2.TransactionBody.FromString(transaction.bodyBytes)
164+
165+
assert transaction_body.tokenCreation.adminKey.ed25519 == admin_key[1]

0 commit comments

Comments
 (0)