Skip to content

Commit 7036888

Browse files
committed
Add integration test
1 parent d4f87a1 commit 7036888

40 files changed

+261
-26
lines changed

integration-test/docker/README.md renamed to integration-test/README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ BFT node in the network. The node will produce all blocks and the environment/le
2121

2222
* Install [Docker](https://www.docker.com/)
2323

24-
### Steps
24+
### Launch containers
2525

2626
Bootstrap config files for a single BFT node:
2727

@@ -37,3 +37,9 @@ Clean up docker containers and volumes when done with testing:
3737

3838
`docker-compose down`
3939

40+
41+
### Test
42+
43+
Current, all integration tests could be kicked off by running:
44+
45+
`./run_tests.sh`

integration-test/__init__.py

Whitespace-only changes.

integration-test/docker/bootstrap.sh renamed to integration-test/bootstrap.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ echo "Bootstrapping network: $NETWORK"
2222

2323
if [ "$NETWORK" = "local-alonzo" ]; then
2424
echo "Updating byron startTime to present in local mode, alonzo era"
25-
jq -M ".startTime = ""$(date +%s)" configs/"$NETWORK"/"$NETWORK"-byron-genesis.json > \
26-
tmp_configs/"$NETWORK"/"$NETWORK"-byron-genesis.json
25+
jq -M ".startTime = ""$(date +%s)" configs/"$NETWORK"/byron-genesis.json > \
26+
tmp_configs/"$NETWORK"/byron-genesis.json
2727
fi
2828

2929
echo "NETWORK=$NETWORK" >.env
30-
NETWORK_MAGIC=$(jq .networkMagic tmp_configs/"$NETWORK"/"$NETWORK"-shelley-genesis.json)
30+
NETWORK_MAGIC=$(jq .networkMagic tmp_configs/"$NETWORK"/shelley-genesis.json)
3131
echo "Found network magic: $NETWORK_MAGIC"
3232
echo "NETWORK_MAGIC=$NETWORK_MAGIC" >>.env
3333
echo "Done"

integration-test/docker/configs/local/local-config.json renamed to integration-test/configs/local-alonzo/config.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"ByronGenesisFile": "local-byron-genesis.json",
3-
"ShelleyGenesisFile": "local-shelley-genesis.json",
4-
"AlonzoGenesisFile": "local-alonzo-genesis.json",
2+
"ByronGenesisFile": "byron-genesis.json",
3+
"ShelleyGenesisFile": "shelley-genesis.json",
4+
"AlonzoGenesisFile": "alonzo-genesis.json",
55
"SocketPath": "db/node.socket",
66
"MaxConcurrencyBulkSync": 1,
77
"MaxConcurrencyDeadline": 2,

integration-test/docker/configs/local/local-shelley-genesis.json renamed to integration-test/configs/local-alonzo/shelley-genesis.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"securityParam": 1000000000,
44
"slotsPerKESPeriod": 129600,
55
"updateQuorum": 2,
6-
"activeSlotsCoeff": 0.1,
6+
"activeSlotsCoeff": 0.5,
77
"protocolParams": {
88
"minUTxOValue": 1000000,
99
"eMax": 18,

integration-test/docker/configs/mainnet/mainnet-config.json renamed to integration-test/configs/mainnet/config.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
{
2-
"AlonzoGenesisFile": "mainnet-alonzo-genesis.json",
2+
"AlonzoGenesisFile": "alonzo-genesis.json",
33
"AlonzoGenesisHash": "7e94a15f55d1e82d10f09203fa1d40f8eede58fd8066542cf6566008068ed874",
44
"ApplicationName": "cardano-sl",
55
"ApplicationVersion": 1,
6-
"ByronGenesisFile": "mainnet-byron-genesis.json",
6+
"ByronGenesisFile": "byron-genesis.json",
77
"ByronGenesisHash": "5f20df933584822601f9e3f8c024eb5eb252fe8cefb24d1317dc3d432e940ebb",
88
"LastKnownBlockVersion-Alt": 0,
99
"LastKnownBlockVersion-Major": 3,
1010
"LastKnownBlockVersion-Minor": 0,
1111
"MaxKnownMajorProtocolVersion": 2,
1212
"Protocol": "Cardano",
1313
"RequiresNetworkMagic": "RequiresNoMagic",
14-
"ShelleyGenesisFile": "mainnet-shelley-genesis.json",
14+
"ShelleyGenesisFile": "shelley-genesis.json",
1515
"ShelleyGenesisHash": "1a3be38bcbb7911969283716ad7aa550250226b76a61fc51cc9a9a35d9276d81",
1616
"TraceAcceptPolicy": true,
1717
"TraceBlockFetchClient": false,

integration-test/docker/configs/testnet/testnet-config.json renamed to integration-test/configs/testnet/config.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
{
2-
"AlonzoGenesisFile": "testnet-alonzo-genesis.json",
2+
"AlonzoGenesisFile": "alonzo-genesis.json",
33
"AlonzoGenesisHash": "7e94a15f55d1e82d10f09203fa1d40f8eede58fd8066542cf6566008068ed874",
44
"ApplicationName": "cardano-sl",
55
"ApplicationVersion": 0,
6-
"ByronGenesisFile": "testnet-byron-genesis.json",
6+
"ByronGenesisFile": "byron-genesis.json",
77
"ByronGenesisHash": "96fceff972c2c06bd3bb5243c39215333be6d56aaf4823073dca31afe5038471",
88
"LastKnownBlockVersion-Alt": 0,
99
"LastKnownBlockVersion-Major": 3,
1010
"LastKnownBlockVersion-Minor": 0,
1111
"MaxKnownMajorProtocolVersion": 2,
1212
"Protocol": "Cardano",
1313
"RequiresNetworkMagic": "RequiresMagic",
14-
"ShelleyGenesisFile": "testnet-shelley-genesis.json",
14+
"ShelleyGenesisFile": "shelley-genesis.json",
1515
"ShelleyGenesisHash": "849a1764f152e1b09c89c0dfdbcbdd38d711d1fec2db5dfa0f87cf2737a0eaf4",
1616
"TraceAcceptPolicy": true,
1717
"TraceBlockFetchClient": false,

integration-test/docker/docker-compose.yml renamed to integration-test/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ services:
2727
command: [
2828
"--host", "0.0.0.0",
2929
"--node-socket", "/ipc/node.socket",
30-
"--node-config", "/code/configs/${NETWORK:-local}/${NETWORK:-local}-config.json"
30+
"--node-config", "/code/configs/${NETWORK:-local}/config.json"
3131
]
3232
volumes:
3333
- .:/code

integration-test/docker/.gitignore

Lines changed: 0 additions & 2 deletions
This file was deleted.
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
#!/bin/sh
22

3-
if [ "$NETWORK" = "local" ]
3+
if [ "$NETWORK" = "local-alonzo" ]
44
then
55
cardano-node run \
6-
--config /code/tmp_configs/"$NETWORK"/"$NETWORK"-config.json \
7-
--topology /code/tmp_configs/"$NETWORK"/"$NETWORK"-topology.json \
6+
--config /code/tmp_configs/"$NETWORK"/config.json \
7+
--topology /code/tmp_configs/"$NETWORK"/topology.json \
88
--database-path /data/db --socket-path /ipc/node.socket \
99
--shelley-kes-key /code/tmp_configs/"$NETWORK"/shelley/kes.skey \
1010
--shelley-vrf-key /code/tmp_configs/"$NETWORK"/shelley/vrf.skey \
1111
--shelley-operational-certificate /code/tmp_configs/"$NETWORK"/shelley/node.cert
1212
else
1313
cardano-node run \
14-
--config /code/tmp_configs/"$NETWORK"/"$NETWORK"-config.json \
15-
--topology /code/tmp_configs/"$NETWORK"/"$NETWORK"-topology.json \
14+
--config /code/tmp_configs/"$NETWORK"/config.json \
15+
--topology /code/tmp_configs/"$NETWORK"/topology.json \
1616
--database-path /data/db --socket-path /ipc/node.socket
1717
fi

integration-test/run_tests.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/sh
2+
3+
ROOT=$(pwd)
4+
5+
poetry install
6+
7+
# Run alonzo integration tests
8+
./bootstrap.sh local-alonzo
9+
docker-compose up -d
10+
11+
export PAYMENT_KEY="$ROOT"/configs/local-alonzo/shelley/utxo-keys/utxo1.skey
12+
poetry run pytest -s "$ROOT"/test
13+
14+
docker-compose down --volume

integration-test/test/__init__.py

Whitespace-only changes.
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
"""An example that demonstrates low-level construction of a transaction."""
2+
3+
import os
4+
import pathlib
5+
import tempfile
6+
import time
7+
8+
import websocket
9+
from retry import retry
10+
11+
from pycardano import *
12+
13+
14+
class TestMintNFT:
15+
# Define chain context
16+
NETWORK = Network.TESTNET
17+
18+
OGMIOS_WS = "ws://localhost:1337"
19+
20+
chain_context = OgmiosChainContext(OGMIOS_WS, Network.TESTNET)
21+
22+
@retry(tries=5, delay=2)
23+
def check_ogmios(self):
24+
print(f"Current chain tip: {self.chain_context.last_block_slot}")
25+
26+
def test_mint(self):
27+
self.check_ogmios()
28+
chain_context = OgmiosChainContext(self.OGMIOS_WS, Network.TESTNET)
29+
30+
payment_key_path = os.environ.get("PAYMENT_KEY")
31+
if not payment_key_path:
32+
raise Exception(
33+
"Cannot find payment key. Please specify environment variable PAYMENT_KEY"
34+
)
35+
payment_skey = PaymentSigningKey.load(payment_key_path)
36+
payment_vkey = PaymentVerificationKey.from_signing_key(payment_skey)
37+
address = Address(payment_vkey.hash(), network=self.NETWORK)
38+
39+
# Load payment keys or create them if they don't exist
40+
def load_or_create_key_pair(base_dir, base_name):
41+
skey_path = base_dir / f"{base_name}.skey"
42+
vkey_path = base_dir / f"{base_name}.vkey"
43+
44+
if skey_path.exists():
45+
skey = PaymentSigningKey.load(str(skey_path))
46+
vkey = PaymentVerificationKey.from_signing_key(skey)
47+
else:
48+
key_pair = PaymentKeyPair.generate()
49+
key_pair.signing_key.save(str(skey_path))
50+
key_pair.verification_key.save(str(vkey_path))
51+
skey = key_pair.signing_key
52+
vkey = key_pair.verification_key
53+
return skey, vkey
54+
55+
tempdir = tempfile.TemporaryDirectory()
56+
PROJECT_ROOT = tempdir.name
57+
58+
root = pathlib.Path(PROJECT_ROOT)
59+
# Create the directory if it doesn't exist
60+
root.mkdir(parents=True, exist_ok=True)
61+
"""Generate keys"""
62+
key_dir = root / "keys"
63+
key_dir.mkdir(exist_ok=True)
64+
65+
# Generate policy keys, which will be used when minting NFT
66+
policy_skey, policy_vkey = load_or_create_key_pair(key_dir, "policy")
67+
68+
"""Create policy"""
69+
# A policy that requires a signature from the policy key we generated above
70+
pub_key_policy = ScriptPubkey(policy_vkey.hash())
71+
72+
# A time policy that disallows token minting after 10000 seconds from last block
73+
must_before_slot = InvalidHereAfter(chain_context.last_block_slot + 10000)
74+
75+
# Combine two policies using ScriptAll policy
76+
policy = ScriptAll([pub_key_policy, must_before_slot])
77+
78+
# Calculate policy ID, which is the hash of the policy
79+
policy_id = policy.hash()
80+
81+
"""Define NFT"""
82+
my_nft = MultiAsset.from_primitive(
83+
{
84+
policy_id.payload: {
85+
b"MY_NFT_1": 1, # Name of our NFT1 # Quantity of this NFT
86+
b"MY_NFT_2": 1, # Name of our NFT2 # Quantity of this NFT
87+
}
88+
}
89+
)
90+
91+
native_scripts = [policy]
92+
93+
"""Create metadata"""
94+
# We need to create a metadata for our NFTs, so they could be displayed correctly by blockchain explorer
95+
metadata = {
96+
721: { # 721 refers to the metadata label registered for NFT standard here:
97+
# https://github.com/cardano-foundation/CIPs/blob/master/CIP-0010/registry.json#L14-L17
98+
policy_id.payload.hex(): {
99+
"MY_NFT_1": {
100+
"description": "This is my first NFT thanks to PyCardano",
101+
"name": "PyCardano NFT example token 1",
102+
"id": 1,
103+
"image": "ipfs://QmRhTTbUrPYEw3mJGGhQqQST9k86v1DPBiTTWJGKDJsVFw",
104+
},
105+
"MY_NFT_2": {
106+
"description": "This is my second NFT thanks to PyCardano",
107+
"name": "PyCardano NFT example token 2",
108+
"id": 2,
109+
"image": "ipfs://QmRhTTbUrPYEw3mJGGhQqQST9k86v1DPBiTTWJGKDJsVFw",
110+
},
111+
}
112+
}
113+
}
114+
115+
# Place metadata in AuxiliaryData, the format acceptable by a transaction.
116+
auxiliary_data = AuxiliaryData(AlonzoMetadata(metadata=Metadata(metadata)))
117+
118+
"""Build transaction"""
119+
120+
# Create a transaction builder
121+
builder = TransactionBuilder(chain_context)
122+
123+
# Add our own address as the input address
124+
builder.add_input_address(address)
125+
126+
# Since an InvalidHereAfter rule is included in the policy, we must specify time to live (ttl) for this transaction
127+
builder.ttl = must_before_slot.after
128+
129+
# Set nft we want to mint
130+
builder.mint = my_nft
131+
132+
# Set native script
133+
builder.native_scripts = native_scripts
134+
135+
# Set transaction metadata
136+
builder.auxiliary_data = auxiliary_data
137+
138+
# Calculate the minimum amount of lovelace that need to hold the NFT we are going to mint
139+
min_val = min_lovelace(Value(0, my_nft), chain_context)
140+
141+
# Send the NFT to our own address
142+
nft_output = TransactionOutput(address, Value(min_val, my_nft))
143+
builder.add_output(nft_output)
144+
145+
# Build a finalized transaction body with the change returning to our own address
146+
tx_body = builder.build(change_address=address)
147+
148+
"""Sign transaction and add witnesses"""
149+
# Sign the transaction body hash using the payment signing key
150+
payment_signature = payment_skey.sign(tx_body.hash())
151+
152+
# Sign the transaction body hash using the policy signing key because we are minting new tokens
153+
policy_signature = policy_skey.sign(tx_body.hash())
154+
155+
# Add verification keys and their signatures to the witness set
156+
vk_witnesses = [
157+
VerificationKeyWitness(payment_vkey, payment_signature),
158+
VerificationKeyWitness(policy_vkey, policy_signature),
159+
]
160+
161+
# Create final signed transaction
162+
signed_tx = Transaction(
163+
tx_body,
164+
# Beside vk witnesses, We also need to add the policy script to witness set when we are minting new tokens.
165+
TransactionWitnessSet(
166+
vkey_witnesses=vk_witnesses, native_scripts=native_scripts
167+
),
168+
auxiliary_data=auxiliary_data,
169+
)
170+
171+
print("############### Transaction created ###############")
172+
print(signed_tx)
173+
print(signed_tx.to_cbor())
174+
175+
# Submit signed transaction to the network
176+
print("############### Submitting transaction ###############")
177+
chain_context.submit_tx(signed_tx.to_cbor())
178+
179+
time.sleep(3)
180+
181+
utxos = chain_context.utxos(str(address))
182+
found_nft = False
183+
184+
for utxo in utxos:
185+
output = utxo.output
186+
if output == nft_output:
187+
found_nft = True
188+
189+
assert found_nft, f"Cannot find target NFT in address: {address}"

0 commit comments

Comments
 (0)