06 / 07Advanced
The factory chain: sending and receiving
The factory chain is the last chain in the example. It listens for production orders that the order chain emits and announces when an order is ready to ship. A real system would probably split these into separate steps, but the example keeps them together for simplicity.
Here, "manufacturing" a product means increasing its value and emitting the message again right away. The database model has just one entity. Put the logic for this blockchain in a new module, factory_chain, inside the src/factory_chain directory, with this content:
module;import lib.icmf.{ send_message };import lib.icmf.receiver.{ receive_icmf_message };import messages.{ topic.*, msg };In src/factory_chain/entities.rell, define the matching entity for manufactured products:
entity manufactured_product { key id: integer; mutable quantity: integer;}Next, add two queries to src/factory_chain/queries.rell. One returns the quantity of a manufactured product, and the other returns the total count of manufactured products.
query get_total_manufactured(id: integer) = manufactured_product @? { id }.quantity;query get_total_manufactured_products() = manufactured_product @* {} ( $.to_struct() );Sending and receiving a message
Configuring a chain to both send and receive messages builds on the configuration from earlier sections. In chromia.yml, add a factory_chain property with its configuration under the blockchains section. This defines the 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 ↑↑↑To handle the messages, add this code to functions.rell:
@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 -> log("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
Testing the factory chain requires extra configuration in chromia.yml. Under the factory_chain section, add a test property and the related 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 reacts by emitting an event. In the src/test directory, create factory_chain_test.rell with 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);}Run chr test and check the results.
Running tests for chain: factory-chainTEST: factory_chain_test:test_manufacture_orderOK: factory_chain_test:test_manufacture_order (0.688s)------------------------------------------------------------TEST RESULTS:OK factory_chain_test:test_manufacture_orderSUMMARY: 0 FAILED / 1 PASSED / 1 TOTAL (0.688s)***** OK *****You now have a dapp built on three separate blockchains. The final section covers running the blockchains locally and testing the complete flow.
For reference, the project structure and the full chromia.yml configuration after these changes are shown below. Use them to compare against your own project at this point in 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_testcompile: rellVersion: 0.14.9database: schema: schema_order_system_examplelibs: icmf: registry: https://gitlab.com/chromaway/core/directory-chain path: src/lib/icmf tagOrBranch: 1.90.1 rid: x"B56524EE85B679371EAB1A069B03BE6A3E94F36993DEA900747BA5BAE8B99122"