Subscribe to solutions

This example demonstrates how to subscribe to solutions from the bloXroute gateway. Only a subscriber with the correct private key can receive the solution.

Name: IntentSolutions

IntentSolutions is a GRPC stream of the new solutions that match the dappAddress of the subscription as they are propagated in the BDN. IntentSolutionsRequest arguments:

KeyDescriptionValues

dappAddress

ETH address of DApp that should receive solutions

string

hash

Keccak256Hash of the dappAddress bytes

byte[]

signature

ECDSA signature of the hash signed by DApp private key

byte[]

IntentSolutionsReply fields (stream message):

KeyDescriptionValues

intentId

UUID of the intent

string

intentSolution

solution for the specific intent

byte[]

package main

import (
	"context"
	"encoding/hex"
	"fmt"
	"log"

	pb "github.com/bloXroute-Labs/gateway/v2/protobuf"

	"github.com/ethereum/go-ethereum/crypto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

// gatewayHost is the address of the gateway to which the subscription is being made
const gatewayHost = "127.0.0.1:5002"

// header is the authorization header of your bloXroute Account
const header = "<YOUR-AUTHORIZATION-HEADER>"

func main() {
	// Dial the gateway
	conn, err := grpc.Dial(gatewayHost,
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithPerRPCCredentials(blxrCredentials{authorization: header}))
	if err != nil {
		log.Fatalln("dial grpc", err)
	}

	// Create a client
	client := pb.NewGatewayClient(conn)

	// Subscribe to solutions
	stream, err := client.IntentSolutions(context.Background(), genIntentSolutionRequest())
	if err != nil {
		log.Fatalln("subscribe to solutions", err)
	}

	for {
		fmt.Printf("listening for IntentSolutions from %s ...\n", gatewayHost)

		// Receive the solution from the stream until the stream is closed
		msg, err := stream.Recv()
		if err != nil {
			log.Fatalln("receive from stream", err)
		}

		fmt.Println("got solution:")
		fmt.Println("- id:", msg.IntentId)
		fmt.Println("- solution:", hex.EncodeToString(msg.IntentSolution))
	}
}

func genIntentSolutionRequest() *pb.IntentSolutionsRequest {
	// DApp private key used to prove the ownership of the DApp address
	const privateKeyHex = "7c4a8d09ca3762af61e59520943dc26494f8941e6e5b998321d3e84b8e4d3c16"

	// Convert the hex string to a private key
	privKey, err := crypto.HexToECDSA(privateKeyHex)
	if err != nil {
		log.Fatalln("error converting hex to ECDSA", err)
	}

	// Extract the public key
	pubKey := privKey.PublicKey
	signerAddress := crypto.PubkeyToAddress(pubKey)

	// Hash the signer address
	hash := crypto.Keccak256Hash([]byte(signerAddress.String())).Bytes() // need a hash to sign, so we're hashing the payload here
	sig, err := crypto.Sign(hash, privKey)                               // signing the hash
	if err != nil {
		log.Fatalln("error signing with private key", err)
	}

	// Return the solution request
	return &pb.IntentSolutionsRequest{
		DappAddress: signerAddress.String(),
		Hash:        hash,
		Signature:   sig,
	}
}

// blxrCredentials is an implementation of PerRPCCredentials
type blxrCredentials struct {
	authorization string
}

// GetRequestMetadata sets the authorization header
func (bc blxrCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
	return map[string]string{
		"authorization": bc.authorization,
	}, nil
}

// RequireTransportSecurity is a method of the PerRPCCredentials interface
func (bc blxrCredentials) RequireTransportSecurity() bool {
	return false
}

Last updated