Blocky AS supports more than just performing basic computations. Our WASM environment provides mechanisms for logging, making HTTP calls, verifying attestations, and more. This is done by injecting "host" functions into our WASM runtime. While you can call these directly, it requires adding build tags and memory management code to your functions. For convenience, we have released the Blocky Web Assembly (BASM) SDK, which contains wrappers for importing and calling our WASM host functions. Below we demonstrate how to use BASM to make an HTTP request.
Clone the Blocky AS examples repository and navigate to the
shipment_tracking_with_dhldirectory:git clone --branch main git@github.com:blocky/attestation-service-examples.git cd attestation-service-examples/shipment_tracking_with_dhlStart by looking at the main WASM function in main.go. There are four actions taken by our DHL tracking function. First, we use the BASM SDK to read non-sensitive input (i.e., the tracking number) from the host Blocky AS server. Next we read in our secret input (i.e., the API key). Then we use these inputs to make our API call. Finally, we write the response data back to the host Blocky AS server.
//export trackingFunc func trackingFunc(inputPtr uint64, secretPtr uint64) uint64 { var input Args inputData := basm.ReadFromHost(inputPtr) err := json.Unmarshal(inputData, &input) if err != nil { outErr := fmt.Errorf("could not unmarshal input args: %w", err) return WriteError(outErr) } var secret SecretArgs secretData := basm.ReadFromHost(secretPtr) err = json.Unmarshal(secretData, &secret) if err != nil { outErr := fmt.Errorf("could not unmarshal secret args: %w", err) return WriteError(outErr) } trackingInfo, err := getTrackingInfoFromDHL( input.TrackingNumber, secret.DHLAPIKey, ) if err != nil { outErr := fmt.Errorf("getting DHL tracking info: %w", err) return WriteError(outErr) } return WriteOutput(trackingInfo) }Now let's look at the function that makes the API call. We construct and make an HTTP request with the BASM SDK. We only need to construct the HTTP request, while BASM handles importing the HTTP host function and writing our request to the Blocky AS server's host memory. Similarly, BASM reads and returns the result of the call from the host to us.
func getTrackingInfoFromDHL( trackingNumber string, apiKey string, ) ( TrackingInfo, error, ) { req := basm.HTTPRequestInput{ Method: "GET", URL: fmt.Sprintf( "https://api-test.dhl.com/track/shipments?trackingNumber=%s", trackingNumber, ), Headers: map[string][]string{ "DHL-API-Key": []string{apiKey}, }, } resp, err := basm.HTTPRequest(req) switch { case err != nil: return TrackingInfo{}, fmt.Errorf("making http request: %w", err) case resp.StatusCode != http.StatusOK: return TrackingInfo{}, fmt.Errorf( "http request failed with status code %d", resp.StatusCode, ) } dhlTrackingInfo := DHLTrackingInfo{} err = json.Unmarshal(resp.Body, &dhlTrackingInfo) if err != nil { return TrackingInfo{}, fmt.Errorf( "unmarshaling data: %w...%s", err, resp.Body, ) } trackingInfo := TrackingInfo{ TrackingNumber: dhlTrackingInfo.Shipments[0].Id, Address: dhlTrackingInfo.Shipments[0].Status.Location.Address, Status: dhlTrackingInfo.Shipments[0].Status.Status, Description: dhlTrackingInfo.Shipments[0].Status.Description, Timestamp: dhlTrackingInfo.Shipments[0].Status.Timestamp, } return trackingInfo, nil }Lastly, we use BASM to write our output back to the host Blocky AS server, where it then gets bundled into an attestation and returned to the client.
func WriteOutput(output any) uint64 { result := Result{ Success: true, Value: output, } data, err := json.Marshal(result) if err != nil { basm.Log(fmt.Sprintf("Error marshalling output Result: %v", err)) return WriteError(err) } return basm.WriteToHost(data) }Let's try running the example. The DHL API provides a Demo key that returns mock responses. Compile and invoke
trackingFuncwith the Demo key:mkdir -p ./tmp bky-c build . ./tmp/x.wasm jq '.secret.api_key = "demo-key"' fn-call.json | bky-as attest-fn-call > out.jsonVerify the Blocky AS attestations and extract the function claims:
which outputsjq '{ enclave_attested_application_public_key: .enclave_attested_application_public_key.enclave_attestation, transitive_attested_function_call: .transitive_attested_function_call.transitive_attestation }' out.json | bky-as verify-fn-call > verified.json jq -r ".transitive_attested_function_call.claims" verified.json{ "hash_of_code": "a751600c2692a505cf97abb7e41150e9501b90d546a8141c06f8f893025f56315e78ff0ed4da8aace3e7daccdf7d363606f2a953923fd6dbd2d13997f520d517", "function": "trackingFunc", "hash_of_input": "21748ca90428bd035d9d709519727862064ab6367c2e8f3f5c52fd5f311be8a2ee2e0ed2f978f318c17f9d3990aea783f919bcfae3238ac119637a022e2520fa", "output": "eyJzdWNjZXNzIjp0cnVlLCJlcnJvciI6IiIsInZhbHVlIjp7InRyYWNraW5nX251bWJlciI6IjAwMzQwNDM0MjkyMTM1MTAwMTg2IiwiYWRkcmVzcyI6eyJjb3VudHJ5Q29kZSI6IlVTIiwicG9zdGFsQ29kZSI6Ijg5MDE0IiwiYWRkcmVzc0xvY2FsaXR5IjoiSGVuZGVyc29uLCBOViwgVVMifSwic3RhdHVzIjoiREVMSVZFUkVEIiwiZGVzY3JpcHRpb24iOiJERUxJVkVSRUQgLSBQQVJDRUwgTE9DS0VSIiwidGltZXN0YW1wIjoiMjAyMy0wNS0wOFQxMDozNzowMCJ9fQ==", "hash_of_secrets": "1e27a7c202eccad61de676101fca154fe0fed84f1fa99182e7a5920dae3f5dca43130b6a4dc4e87cd50e3a59c8c97b9918e1d297efb41a8eb51cfbaa92364fca" }Verify that the claim values match the function and inputs we specified in our call:
which outputsopenssl dgst -sha3-512 ./tmp/x.wasm jq -rcj '.input' fn-call.json | openssl dgst -sha3-512 jq '.secret.api_key = "demo-key"' fn-call.json \ | jq -rcj '.secret' \ | openssl dgst -sha3-512SHA3-512(./tmp/x.wasm)= a751600c2692a505cf97abb7e41150e9501b90d546a8141c06f8f893025f56315e78ff0ed4da8aace3e7daccdf7d363606f2a953923fd6dbd2d13997f520d517 SHA3-512(stdin)= 21748ca90428bd035d9d709519727862064ab6367c2e8f3f5c52fd5f311be8a2ee2e0ed2f978f318c17f9d3990aea783f919bcfae3238ac119637a022e2520fa SHA3-512(stdin)= 1e27a7c202eccad61de676101fca154fe0fed84f1fa99182e7a5920dae3f5dca43130b6a4dc4e87cd50e3a59c8c97b9918e1d297efb41a8eb51cfbaa92364fcaFinally, extract and decode our output:
which outputsjq -r ".transitive_attested_function_call.claims.output | @base64d | fromjson" verified.json{ "success": true, "error": "", "value": { "tracking_number": "00340434292135100186", "address": { "countryCode": "US", "postalCode": "89014", "addressLocality": "Henderson, NV, US" }, "status": "DELIVERED", "description": "DELIVERED - PARCEL LOCKER", "timestamp": "2023-05-08T10:37:00" } }
You can now make HTTP requests from your WASM functions on Blocky AS. Check out our examples repository for more HTTP request examples, or read on to learn about bringing your function output on-chain.