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.
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
}