The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Smart Filters and On-Chain Logic

Introduction

In MultiChain, Smart Filters provide a way to enforce business rules directly on the ledger. For example, you can ensure that a sensor only records values within a specific range or that a transaction is only valid if it contains certain metadata. To keep code clean, common logic can be stored in Libraries.

library(multichainr)

# Set the path to your MultiChain binaries
mc_set_path(Sys.getenv("MULTICHAIN_PATH"))

1. Node Initialization

We start by setting up a local node. Governance permissions (Admin) are required to create filters and libraries.

chain_name <- "logic_demo_chain"

# Create and start the node
mc_node_init(chain_name)
mc_node_start(chain_name)

# Wait for the node to initialize
Sys.sleep(3) 

# Connect to the local node
config <- mc_get_config(chain_name)
conn <- mc_connect(config)

2. Creating a JavaScript Library

A library contains helper functions. Here, we create a library called validator that checks if a temperature reading is within a safe physical range.

# Define the JavaScript code for the library
lib_js <- "
function isSafeTemp(temp) {
    return temp >= -50 && temp <= 100;
}
"

# Create the library on the blockchain
# updatemode = 'instant' means updates take effect immediately without voting
mc_create_library(conn, "validator", updatemode = "instant", js_code = lib_js)

cat("Library 'validator' created on-chain.\n")

3. Writing and Testing a Stream Filter

Now we want to create a filter for a stream called telemetry. This filter will use our library to reject any data that has an “impossible” temperature.

Defining the Filter

The filter logic must be contained in a function named filterstreamitem().

# The filter code imports the 'validator' library
filter_js <- "
function filterstreamitem() {
    var item = getfilterstreamitem();
    
    if (item.data.json && typeof item.data.json.temp !== 'undefined') {
        if (!isSafeTemp(item.data.json.temp)) {
            return 'Invalid temperature detected by on-chain logic';
        }
    }
    return true;
}
"

Local Testing (Dry Run)

Before deploying a filter to the whole network, it is best practice to test it locally using mc_test_stream_filter. To do this, we first publish a sample item to get a valid transaction ID.

# 0. Setup a temporary stream for testing
mc_create_stream(conn, "test_stream", open = TRUE)
mc_subscribe(conn, "test_stream")

# 1. Publish valid data and get its TXID
txid_valid <- mc_publish(conn, "test_stream", "key1", list(json = list(temp = 25.5)))

# Test the filter against the valid transaction
test_valid <- mc_test_stream_filter(conn, 
                                    options = list(libraries = list("validator")), 
                                    js_code = filter_js, 
                                    tx = txid_valid)
print(test_valid) # Should be TRUE (logical) or 'true' (string)

# 2. Publish invalid data and get its TXID
txid_invalid <- mc_publish(conn, "test_stream", "key1", list(json = list(temp = 500)))

# Test the filter against the invalid transaction
test_invalid <- mc_test_stream_filter(conn, 
                                      options = list(libraries = list("validator")), 
                                      js_code = filter_js, 
                                      tx = txid_invalid)
print(test_invalid) # Should return our error message string

4. Deploying and Using the Filter

Once tested, we create the filter on the blockchain and attach it to our stream.

# 1. Create the telemetry stream
mc_create_stream(conn, "telemetry", open = TRUE)

# 2. Create the stream filter globally
# Here 'options' only specifies the libraries used.
mc_create_stream_filter(conn, "temp_range_check", 
                        options = list(libraries = list("validator")), 
                        js_code = filter_js)

# 3. Activate the filter for the 'telemetry' stream
# In MultiChain, filters must be approved for specific entities.
admin_addr <- mc_get_addresses(conn)[1]

mc_approve_from(conn, 
                from_address = admin_addr, 
                entity = "temp_range_check", 
                approve = list("for" = "telemetry", approve = TRUE))

5. Verifying the Logic

After the filter is active, any attempt to publish invalid data to the telemetry stream will be rejected by every node in the network.

# This will SUCCEED (20 is within range)
txid_ok <- mc_publish(conn, "telemetry", "sensor_01", list(json = list(temp = 20)))
print(paste("Successful publish TXID:", txid_ok))

# This will FAIL at the protocol level. 
# We use try() to catch the RPC error so the vignette can continue.
publish_error <- try(mc_publish(conn, "telemetry", "sensor_01", list(json = list(temp = 999))), 
                     silent = TRUE)

if (inherits(publish_error, "try-error")) {
    cat("Rejected! As expected, the filter blocked the invalid data.\n")
}

# Retrieve the filter code from the blockchain to verify
code_info <- mc_get_filter_code(conn, "temp_range_check")
cat("Stored filter code length:", nchar(code_info), "characters.\n")

6. Cleanup

Always shut down the node and clean up the temporary directory.

# Stop the node
mc_node_stop(conn)
Sys.sleep(2)

# Determine data directory
if (.Platform$OS.type == "windows") {
  base_dir <- file.path(Sys.getenv("APPDATA"), "MultiChain")
} else if (Sys.info()["sysname"] == "Darwin") {
  base_dir <- file.path(Sys.getenv("HOME"), "Library/Application Support/MultiChain")
} else {
  base_dir <- file.path(Sys.getenv("HOME"), ".multichain")
}

chain_dir <- file.path(base_dir, chain_name)
if (dir.exists(chain_dir)) {
  unlink(chain_dir, recursive = TRUE)
}

Summary

In this vignette, we demonstrated the advanced logic capabilities of multichainr:

  1. Code Reuse: Using mc_create_library to store JavaScript functions.
  2. Logic Definition: Writing a stream filter that intercepts and validates incoming data.
  3. Prototyping: Using mc_test_stream_filter to debug JavaScript logic without making on-chain transactions.
  4. Deployment: Attaching validation rules to specific streams using mc_create_stream_filter.

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.