Skip to content
Last updated

Overview

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.

Make an HTTP Request

  1. Clone the Blocky AS examples repository and navigate to the shipment_tracking_with_dhl directory:

    git clone --branch main git@github.com:blocky/attestation-service-examples.git
    cd attestation-service-examples/shipment_tracking_with_dhl
  2. Start 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)
    }
  3. 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
    }
  4. 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)
    }
  5. Let's try running the example. The DHL API provides a Demo key that returns mock responses. Compile and invoke trackingFunc with 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.json
  6. Verify the Blocky AS attestations and extract the function claims:

    jq '{
    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
    which outputs
    {
      "hash_of_code": "a751600c2692a505cf97abb7e41150e9501b90d546a8141c06f8f893025f56315e78ff0ed4da8aace3e7daccdf7d363606f2a953923fd6dbd2d13997f520d517",
      "function": "trackingFunc",
      "hash_of_input": "21748ca90428bd035d9d709519727862064ab6367c2e8f3f5c52fd5f311be8a2ee2e0ed2f978f318c17f9d3990aea783f919bcfae3238ac119637a022e2520fa",
      "output": "eyJzdWNjZXNzIjp0cnVlLCJlcnJvciI6IiIsInZhbHVlIjp7InRyYWNraW5nX251bWJlciI6IjAwMzQwNDM0MjkyMTM1MTAwMTg2IiwiYWRkcmVzcyI6eyJjb3VudHJ5Q29kZSI6IlVTIiwicG9zdGFsQ29kZSI6Ijg5MDE0IiwiYWRkcmVzc0xvY2FsaXR5IjoiSGVuZGVyc29uLCBOViwgVVMifSwic3RhdHVzIjoiREVMSVZFUkVEIiwiZGVzY3JpcHRpb24iOiJERUxJVkVSRUQgLSBQQVJDRUwgTE9DS0VSIiwidGltZXN0YW1wIjoiMjAyMy0wNS0wOFQxMDozNzowMCJ9fQ==",
      "hash_of_secrets": "1e27a7c202eccad61de676101fca154fe0fed84f1fa99182e7a5920dae3f5dca43130b6a4dc4e87cd50e3a59c8c97b9918e1d297efb41a8eb51cfbaa92364fca"
    }
  7. Verify that the claim values match the function and inputs we specified in our call:

    openssl 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-512
    which outputs
    SHA3-512(./tmp/x.wasm)= a751600c2692a505cf97abb7e41150e9501b90d546a8141c06f8f893025f56315e78ff0ed4da8aace3e7daccdf7d363606f2a953923fd6dbd2d13997f520d517
    SHA3-512(stdin)= 21748ca90428bd035d9d709519727862064ab6367c2e8f3f5c52fd5f311be8a2ee2e0ed2f978f318c17f9d3990aea783f919bcfae3238ac119637a022e2520fa
    SHA3-512(stdin)= 1e27a7c202eccad61de676101fca154fe0fed84f1fa99182e7a5920dae3f5dca43130b6a4dc4e87cd50e3a59c8c97b9918e1d297efb41a8eb51cfbaa92364fca
  8. Finally, extract and decode our output:

    jq -r ".transitive_attested_function_call.claims.output | @base64d | fromjson" verified.json
    which outputs
    {
      "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"
      }
    }

Next Steps

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.