Factory chain (send and receive)
The final chain in our example is the factory chain. The chain should react to production orders emitted from the order chain and should notify when an order is ready for shipment. These would likely be separate steps in a real-world case, but we will simplify this in our example.
We can say a product is 'manufactured' by increasing its value and reemitting the message directly. The database model
consists of only a single entity. To encapsulate the logic for this new blockchain, we create a new module
factory_chain
in the src/factory_chain
directory with the following content:
module;
import lib.icmf.{ send_message };
import lib.icmf.receiver.{ receive_icmf_message };
import messages.{ topic.*, msg };
Define the corresponding manufactured product entity in the src/factory_chain/entities.rell
file:
entity manufactured_product {
key id: integer;
mutable quantity: integer;
}
Additionally, you need to add two queries to fetch the quantity of a manufactured product and the total number of
manufactured products to the src/factory_chain/queries.rell
file.
query get_total_manufactured(id: integer) = manufactured_product @? { id }.quantity;
query get_total_manufactured_products() = manufactured_product @* {} ( $.to_struct() );
Sending and receiving a message
To send and receive messages in a single chain, we will apply what we have learned from previous sections in our
configuration. Open the chromia.yml
file and insert the factory_chain
property, along with its underlying
configuration details, into the blockchains
section to create a new blockchain.
blockchains:
# ↓↓↓ Add this code snippet ↓↓↓
factory-chain:
module: factory_chain
config:
<<: *sender_receiver
icmf:
receiver:
local:
- topic: "L_production"
brid: null
# ↑↑↑ Add this code snippet ↑↑↑
Insert the following code to the functions.rell
file to handle our messages.
@extend(receive_icmf_message)
function (sender: byte_array, topic: text, body: gtv) {
when (topic) {
PRODUCTION_ORDER -> {
val order = msg.production_details.from_gtv(body);
for (product in order.products) manufacture_product(product);
send_message(SHIPMENT_READY, msg.shipment_ready(order.order_id).to_gtv());
}
else -> require(false, "Message type %s not handled".format(topic));
}
}
function manufacture_product(product: msg.product) {
if (not exists(manufactured_product @? { .id == product.id})) {
create manufactured_product( id = product.id, quantity = product.quantity);
} else {
update manufactured_product @ { .id == product.id } (quantity += product.quantity);
}
}
Testing
To test the factory chain, we need to add new configuration details to the chromia.yaml
file. Insert the test
property under the factory_chain
section, along with other details.
blockchains:
factory-chain:
# ↓↓↓ Add this code snippet ↓↓↓
test:
modules:
- test.factory_chain_test
# ↑↑↑ Add this code snippet ↑↑↑
The test emits a message and the dapp responds to it by emitting an event. Create a new file factory_chain_test.rell
in the src/test
directory and insert the following code:
@test module;
import factory_chain.{ manufactured_product };
import messages.{ msg, topic.* };
import lib.icmf.test.{ test_icmf_message };
function test_manufacture_order() {
rell.test.tx().op(
test_icmf_message(
x"",
PRODUCTION_ORDER,
msg.production_details(
order_id = 1,
products = [msg.product(id = 2, quantity = 10)]
)
.to_gtv()
)
)
.run();
assert_events(("icmf_message", (topic = SHIPMENT_READY, body = msg.shipment_ready(1).to_gtv()).to_gtv_pretty()));
assert_equals(manufactured_product @ { .id == 2 }.quantity, 10);
}
Then run the chr test
command to verify the test results.
Running tests for chain: factory-chain
TEST: factory_chain_test:test_manufacture_order
OK: factory_chain_test:test_manufacture_order (0.688s)
------------------------------------------------------------
TEST RESULTS:
OK factory_chain_test:test_manufacture_order
SUMMARY: 0 FAILED / 1 PASSED / 1 TOTAL (0.688s)
***** OK *****
Now, you have successfully written a dapp that uses three separate blockchains. In the final section, we will wrap things up by running the blockchains locally and testing the flow.
Below you can find the project structure and the contents of the chromia.yml
configuration file after the changes have
been made. It can be handy for comparison at this stage of the course:
Project structure
order-system-example/
├── build/
├── src/
│ ├── delivery_chain/
│ │ ├── entities.rell
│ │ ├── functions.rell
│ │ ├── module.rell
│ │ ├── operations.rell
│ │ └── queries.rell
│ ├── factory_chain/
│ │ ├── entities.rell
│ │ ├── functions.rell
│ │ ├── module.rell
│ │ └── queries.rell
│ ├── lib/
│ ├── order_chain/
│ │ ├── entities.rell
│ │ ├── functions.rell
│ │ ├── module.rell
│ │ ├── operations.rell
│ │ └── queries.rell
│ ├── test/
│ │ ├── delivery_chain_test.rell
│ │ ├── factory_chain_test.rell
│ │ └── order_chain_test.rell
│ └── messages.rell
├── .gitignore
└── chromia.yml
Final version of the chromia.yml
file
chromia.yml
filedefinitions:
- &sender # Configuration for a chain that sends messages
gtx:
modules:
- "net.postchain.d1.icmf.IcmfSenderGTXModule"
- &receiver # Base configuration for a chain that receives messages
gtx:
modules:
- "net.postchain.d1.icmf.IcmfReceiverGTXModule"
sync_ext:
- "net.postchain.d1.icmf.IcmfReceiverSynchronizationInfrastructureExtension"
- &sender_receiver # Base configuration for a chain that will both send and receive messages
gtx:
modules:
- "net.postchain.d1.icmf.IcmfSenderGTXModule"
- "net.postchain.d1.icmf.IcmfReceiverGTXModule"
sync_ext:
- "net.postchain.d1.icmf.IcmfReceiverSynchronizationInfrastructureExtension"
blockchains:
order-chain:
module: order_chain
config:
<<: *sender
test:
modules:
- test.order_chain_test
delivery-chain:
module: delivery_chain
config:
<<: *receiver
icmf:
receiver:
local:
- topic: "L_delivery"
brid: null
- topic: "L_shipment_ready"
brid: null
test:
modules:
- test.delivery_chain_test
factory-chain:
module: factory_chain
config:
<<: *sender_receiver
icmf:
receiver:
local:
- topic: "L_production"
brid: null
test:
modules:
- test.factory_chain_test
compile:
rellVersion: 0.13.5
database:
schema: schema_order_system_example
libs:
icmf:
registry: https://gitlab.com/chromaway/core/directory-chain
path: src/messaging/icmf
tagOrBranch: 1.29.0
rid: x"19D6BC28D527E6D2239843608486A84F44EDCD244E253616F13D1C65893F35F6"