// In response to an off-chain Turing request, obtain the requested data and
// rewrite the parameters so that the contract can be called without reverting.
func bobaTuringRandom(input []byte) hexutil.Bytes {
//some things are easier with a hex string
inputHexUtil := hexutil.Bytes(input)
// If things fail, we'll return an integer parameter which will fail a
// "require" in the contract.
retError := make([]byte, len(inputHexUtil))
copy(retError, inputHexUtil)
// 1 for Request, 2 for Response, integer >= 10 for various failures
log.Warn("TURING-1 bobaTuringRandom:Wrong state (rType != 1)", "rType", rType)
retError[35] = 10 // Wrong input state
log.Warn("TURING-2 bobaTuringRandom:Calldata too short", "len < 2*32", rlen)
retError[35] = 11 // Calldata too short
// Generate cryptographically strong pseudo-random int between 0 - 2^256 - 1
// Max random value 2^256 - 1
max = max.Exp(two, big.NewInt(int64(256)), nil).Sub(max, one)
n, err := rand.Int(rand.Reader, max)
log.Warn("TURING bobaTuringRandom: Random Number Generation Failed", "err", err)
retError[35] = 16 // RNG Failure
//generate a BigInt random number
methodID := make([]byte, 4)
copy(methodID, inputHexUtil[0:4])
ret = append(methodID, hexutil.MustDecode(fmt.Sprintf("0x%064x", 2))...) // the usual prefix and the rType, now changed to 2
ret = append(ret, hexutil.MustDecode(fmt.Sprintf("0x%064x", randomBigInt))...)
// In response to an off-chain Turing request, obtain the requested data and
// rewrite the parameters so that the contract can be called without reverting.
func bobaTuringCall(input []byte, caller common.Address) hexutil.Bytes {
var responseStringEnc string
var responseString []byte
inputHexUtil := hexutil.Bytes(input)
restHexUtil := inputHexUtil[4:]
retError := make([]byte, len(inputHexUtil))
copy(retError, inputHexUtil)
// 1 for Request, 2 for Response, integer >= 10 for various failures
retError[35] = 10 // Wrong input state
retError[35] = 11 // Calldata too short
// A micro-ABI decoder... this works because we know that all these numbers can never exceed 256
// Since the rType is 32 bytes and the three headers are 32 bytes each, the max possible value
// of any of these numbers is 32 + 32 + 32 + 32 + 64 = 192
// Thus, we only need to read one byte
// 64 - 95 = payload start
// 96 - 127 = length URL string
// 128 - ??? = URL string
// ??? - ??? = payload length
startIDXurl := int(rest[63]) + 32
// the +32 means that we are going directly for the actual string
// bytes 0 to 31 are the string length
startIDXpayload := int(rest[95]) // the start of the payload
lengthURL := int(rest[127]) // the length of the URL string
// Note: we do not handle URLs that are longer than 64 characters
retError[35] = 12 // URL string > 64 bytes
// The URL we are going to query
endIDX := startIDXurl + lengthURL
url := string(rest[startIDXurl:endIDX])
// we use a specific end value (startIDXurl+lengthURL) since the URL is right-packed with zeros
// At this point, we have the API endpoint and the payload that needs to go there...
payload := restHexUtil[startIDXpayload:] //using hex here since that makes it easy to get the string
log.Debug("TURING-4 bobaTuringCall:Have URL and payload",
client, err := rpc.Dial(url)
if err := client.Call(&responseStringEnc, caller.String(), payload); err != nil {
retError[35] = 13 // Client Error
responseString, err = hexutil.Decode(responseStringEnc)
retError[35] = 14 // Client Response Decode Error
retError[35] = 15 // Could not create client
// build the modified calldata
ret := make([]byte, startIDXpayload+4)
copy(ret, inputHexUtil[0:startIDXpayload+4]) // take the original input
ret[35] = 2 // change byte 3 + 32 = 35 (rType) to indicate a valid response
ret = append(ret, responseString...) // and tack on the payload