Oracle with Custom Chaincode

Example:

Oracle id: 64e8bc2c4708667b390d8694

Weather API details:

https://rapidapi.com/weatherapi/api/weatherapi-com

Oracle config:
METHOD: Get
URL: https://weatherapi-com.p.rapidapi.com/current.json

Params Type: Query
Authentication Method: Custom
Authentication Headers: 
X-RapidAPI-Key: **************************
Custom Headers:
X-RapidAPI-Host: weatherapi-com.p.rapidapi.com

Scheduler config:

Cron: * * * * *
Oracle Request Params: 
q: Texas
App: <Custom Chaincode>
Function name: <ContractName:CustomChaincodeName> 
(Eg: SmartContract:OracleScheduleHandler)

On Demand Request:

To get external data (oracle data) from a custom chaincode, users must perform a cross-chaincode call to Spydra’s oracle chaincode which acts as an interface to the oracle services that Spydra offer.

  • Spydra’s oracle chaincode is deployed with a constant name oracle. If you have created multiple channels in your network and have created oracles for those channels as well, then the first deployed oracle chaincode's name is oracle , the deployed oracle chaincode's name is oracle1 and so on. The chaincode name of a particular oracle can always be found on the oracle descriptions or list page. You can reach the oracle listing page by clicking Oracle tab in the Network page.

  • It has a OracleRequest function that accepts the following json data as input

{
  oracleSetId: string; // The oracle id found on UI
  params: any; // query/path/body params of the defined oracle - this can be an object or string or empty
}
  • To make a cross chaincode call from HLF chaincode, stub.InvokeChaincode api can be used. stub.InvokeChaincode accepts 3 parameters:

1. chaincodeName (string)

2. args ([][]byte or Array.<Array.<byte>> or array of array of bytes)

3.channel (string)

  • The first element of args must contain the function name of the calling chaincode.

  • Further elements of args can contain the actual arguments of the function.

HLF API doc reference: Golang: https://pkg.go.dev/github.com/hyperledger/fabric-chaincode-go/shim#ChaincodeStubInterface Node.js: https://hyperledger.github.io/fabric-chaincode-node/main/api/fabric-shim.ChaincodeStub.html#invokeChaincode__anchor

Example: To request external data from above defined oracle

// ToChaincodeArgs converts string args to []byte args
func ToChaincodeArgs(args ...string) [][]byte {
    bargs := make([][]byte, len(args))
    for i, arg := range args {
        bargs[i] = []byte(arg)
    }
    return bargs
}


// My chaincode function
func (s *SmartContract) MyCustomFunction(ctx contractapi.TransactionContextInterface) (output string, err error) {
    stub := ctx.GetStub()
    txId := stub.GetTxID()


    var oracleRequestParams string = `{"oracleSetId":"64e8bc2c4708667b390d8694","params":{"q":"Texas"}}`
    chaincodeArgs := util.ToChaincodeArgs("OracleContract:OracleRequest", oracleRequestParams)


    // Request external data (oracle)
    response := stub.InvokeChaincode("oracle", chaincodeArgs, "") // channel parameter can be left empty


    fmt.Println("ORACLE RESPONSE: ", response.Status, response.Message, string(response.Payload))


    if response.Status != 200 {
        err = fmt.Errorf(string(response.Message))
        return
    }


    dataBytes := response.GetPayload()


    // Save to blockchain state
    err = stub.PutState(txId, dataBytes)
    if err != nil {
        fmt.Println(err)
        return
    }


    output = string(dataBytes)
    return
}

Oracle Scheduler

A scheduled oracle retrieves data from the API configured in the oracle definition and calls the chaincode function as configured in the APPLICATION section of the oracle scheduler config.

The scheduler calls the chaincode function with the following argument:

{
  oracleCronId: string; // The internal oracle cron scheduler id
  refId: string; // Reference id for this scheduled transaction
  data: any; // Response given by API configured in oracle
}

Example: To receive data from the oracle scheduler

type OracleCallbackArgs struct {
    OracleCronId string      `json:"oracleCronId"`
    RefId        string      `json:"refId"`
    Data         interface{} `json:"data,omitempty" metadata:",optional"`
}


func (s *SmartContract) OracleScheduleHandler(ctx contractapi.TransactionContextInterface, input OracleCallbackArgs) (err error) {
    stub := ctx.GetStub()
    txId := stub.GetTxID()


    var minput []byte
    minput, err = json.Marshal(input)
    if err != nil {
        fmt.Println(err)
        return
    }


    err = stub.PutState(txId, minput)
    if err != nil {
        fmt.Println(err)
        return
    }


    return
}

Last updated