能否在Composer中订阅资产创建事件?及替代实现方案咨询
Absolutely, both of your needs are fully achievable in Hyperledger Fabric—let’s walk through each scenario step by step:
1. Receiving Events on Asset Creation to Execute Actions
This is not only feasible but also a common pattern for integrating external systems or triggering post-creation workflows. Here’s how to implement it:
Step 1: Emit an Event from Your Chaincode
When your chaincode’s asset creation function (e.g., CreateAsset) successfully writes the asset to the ledger, use the chaincode stub to emit a custom event with relevant asset details.
Example Go chaincode snippet:
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, assetID string, value string) error { // Check if asset already exists exists, err := s.AssetExists(ctx, assetID) if err != nil { return err } if exists { return fmt.Errorf("the asset %s already exists", assetID) } asset := Asset{ ID: assetID, Value: value, } assetJSON, err := json.Marshal(asset) if err != nil { return err } // Write asset to ledger err = ctx.GetStub().PutState(assetID, assetJSON) if err != nil { return err } // Emit custom event with asset data err = ctx.GetStub().SetEvent("AssetCreated", []byte(assetID)) if err != nil { return err } return nil }
Step 2: Listen for the Event via Node SDK
Use the Fabric Node SDK to register a listener for the AssetCreated event. You can listen at the contract level or block level, depending on your needs.
Example Node SDK code:
const { Gateway, Wallets } = require('fabric-network'); const path = require('path'); async function listenForAssetEvents() { const walletPath = path.join(__dirname, 'wallet'); const wallet = await Wallets.newFileSystemWallet(walletPath); const gateway = new Gateway(); const connectionProfile = path.join(__dirname, 'connection-org1.json'); const connectionOptions = { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } }; await gateway.connect(connectionProfile, connectionOptions); const network = await gateway.getNetwork('mychannel'); const contract = network.getContract('mychaincode'); // Register a contract listener for the AssetCreated event const listener = async (event) => { const assetID = event.payload.toString(); console.log(`Received AssetCreated event for asset: ${assetID}`); // Execute your custom action here (e.g., update external database, send notification) await performPostCreationAction(assetID); }; // Start listening (use 'unregister()' later to stop) await contract.addContractListener('assetCreatedListener', 'AssetCreated', listener); } async function performPostCreationAction(assetID) { // Your custom business logic goes here console.log(`Executing post-creation action for asset ${assetID}`); } listenForAssetEvents().catch(err => { console.error(`Error listening for events: ${err}`); process.exit(1); });
Key notes:
- Events are only emitted after the transaction is successfully committed to the ledger, so you don’t have to worry about handling uncommitted transactions.
- Remember to handle listener reconnection logic if the connection drops—you can wrap the listener setup in a retry loop.
2. Alternative: Trigger a Specific Function When an Asset is Added
If you prefer to handle the workflow entirely within the chaincode (no external listeners), you can directly call a dedicated function after asset creation. This keeps all logic within the distributed ledger’s transaction context.
Option 1: Direct Function Call in Chaincode
Modify your CreateAsset function to invoke a post-processing function immediately after writing the asset. Since this happens in the same transaction, any failure in the post-processing will roll back the entire asset creation (which is useful for maintaining data consistency).
Example Go chaincode snippet:
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, assetID string, value string) error { // ... (existing asset creation logic) // After successful asset write, call post-processing function err = s.OnAssetCreated(ctx, assetID, value) if err != nil { return err } return nil } // Dedicated function to run on asset creation func (s *SmartContract) OnAssetCreated(ctx contractapi.TransactionContextInterface, assetID string, value string) error { // Your custom logic here: e.g., update related assets, log audit info, trigger other business rules fmt.Printf("Processing new asset %s with value %s\n", assetID, value) // Example: Write an audit entry to the ledger auditEntry := AuditEntry{ AssetID: assetID, Timestamp: time.Now().UTC().String(), Action: "CREATED", } auditJSON, err := json.Marshal(auditEntry) if err != nil { return err } return ctx.GetStub().PutState(fmt.Sprintf("audit_%s", assetID), auditJSON) }
Option 2: Use a Generic Entry Function (Advanced)
If you want more flexibility, you can create a generic Invoke function that routes to specific handlers based on the operation type, including triggering post-creation logic. However, this is more complex and usually unnecessary unless you need dynamic routing.
内容的提问来源于stack exchange,提问作者Stefan Bates

