smartcontractkit / smartcontractkit/chainlink

[NODE] Job spec with multi-word response not calling fulfillment function

Open
#5,567 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

question support
Dominant language
Go
Stars
8.2k
Forks
2k
Avg merge
1d 23h
Merged PRs (30d)
202

Description

Description
I have a node running a job spec that attempts to build a multi-word response. On the node dashboard it reports a success but the callback function is never called. A similar issue can be found at https://stackoverflow.com/questions/70107436/chainlink-multi-variable-responses-smart-contract-request-not-fulfilled-by-oracl

Basic Information

  • Network: Kovan
  • Blockchain Client: Infura
  • Go Version: Not sure
  • Operating System: Ubuntu 20.04.3
  • Commit: [log INFO line when starting node]
  • Hosting Provider: Azure
  • Startup Command: cd ~/.chainlink-kovan && docker run -p 6688:6688 -v ~/.chainlink-kovan:/chainlink -it --env-file=.env --network host smartcontract/chainlink:1.0.0 local n
  • Smart contract calling the oracle:
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "interfaces/ExternalBlockchainWalletCheckerInterface.sol";
import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";

contract BitcoinWalletChecker is
    ExternalBlockchainWalletCheckerInterface,
    ChainlinkClient
{
    using Chainlink for Chainlink.Request;

    bool private exists;
    uint256 private balance;
    uint256 private timestamp;

    address private oracle;
    bytes32 private jobId;
    uint256 private fee;

    event UpdatedWalletInfo(
        string btc_wallet,
        bool exists,
        uint256 balance,
        uint256 last_transaction
    );

    /**
     * TODO: Make this parameterized and instantiate it to whichever network
     * we are currently running on
     * Network: Kovan
     * Job webpage: https://market.link/jobs/10ce4dfb-ca85-4ffc-9849-772c2e8ae124
     */
    constructor() {
        setPublicChainlinkToken();
        oracle = 0x745d8F7A8d25043C4e975c6A2578471F8f94388d;
        jobId = "9e709ebdee48427eaa55c344a1362444"; // Get > BtcWalletInfo
        fee = 1 * 10**18; // (Varies by network and job)
    }

    function updateWalletInfo(string calldata _btcWalletAddress)
        external
        returns (bytes32 requestId)
    {
        Chainlink.Request memory request = buildChainlinkRequest(
            jobId,
            address(this),
            this.fulfill.selector
        );

        request.add(
            "get",
            string(
                bytes.concat(
                    bytes("https://lostcoin-function-app.azurewebsites.net/api/lostcoin-api?code=A3cVTg06CyiGd7FZlS647/OYppF2axary3dqtgPmP5Cy6Sc9emAcGQ==&wallet="),
                    bytes(_btcWalletAddress)
                )
            )
        );
        return sendChainlinkRequestTo(oracle, request, fee);
    }

    function fulfill(
        bytes32 _requestId,
        string calldata _btc_wallet,
        uint256 _balance,
        bool _exists,
        uint256 _time_since_last_tx
    ) external recordChainlinkFulfillment(_requestId) {
        exists = _exists;
        balance = _balance;
        timestamp = _time_since_last_tx;
        emit UpdatedWalletInfo(_btc_wallet, exists, balance, timestamp);
    }

    function checkWalletInfo(string memory _walletAddress)
        external
        view
        returns (
            bool,
            uint256,
            uint256
        )
    {
        // TODO: somehow get this to return the fulfillment result?
        // It might be impossible - maybe we need to allow the caller of RequestWalletInfo to pass
        // in a callback instead...
        return (
            exists,
            balance,
            /*age=*/
            timestamp
        );
    }
}

  • Job Spec:
type = "directrequest"
schemaVersion = 1
name = "Get > BtcWalletInfo"
contractAddress = "0x745d8F7A8d25043C4e975c6A2578471F8f94388d"
maxTaskDuration = "0s"
observationSource = """
    decode_log    [type="ethabidecodelog"
                   abi="OracleRequest(bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data)"
                   data="$(jobRun.logData)"
                   topics="$(jobRun.logTopics)"]
    decode_cbor   [type="cborparse" data="$(decode_log.data)"]
    fetch         [type="http" method=GET url="$(decode_cbor.get)"]
    parse_wallet  [type="jsonparse" path="wallet" data="$(fetch)"]
    parse_exist   [type="jsonparse" path="exist" data="$(fetch)"]
    parse_balance [type="jsonparse" path="balance" data="$(fetch)"]
    parse_last_tx [type="jsonparse" path="last_transaction" data="$(fetch)"]
    encode_data   [type="ethabiencode" abi="fulfill(bytes32 _requestId, string calldata _btc_wallet, uint256 _balance, bool _exists, uint256 _time_since_last_tx)"
                   data="{\\"_requestId\\": $(decode_log.requestId), \\"_btc_wallet\\": $(parse_wallet), \\"_balance\\": $(parse_balance), \\"_exists\\": $(parse_exist), \\"_time_since_last_tx\\": $(parse_last_tx)}"]
    encode_tx     [type="ethabiencode"
                   abi="fulfillOracleRequest2(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes calldata data)"
                   data="{\\"requestId\\": $(decode_log.requestId), \\"payment\\": $(decode_log.payment), \\"callbackAddress\\": $(decode_log.callbackAddr), \\"callbackFunctionId\\": $(decode_log.callbackFunctionId), \\"expiration\\": $(decode_log.cancelExpiration), \\"data\\": $(encode_data)}"
                  ]
    submit_tx    [type="ethtx" to="0x745d8F7A8d25043C4e975c6A2578471F8f94388d" data="$(encode_tx)"]
    
    decode_log -> decode_cbor -> fetch
    fetch -> parse_wallet -> encode_data 
    fetch -> parse_exist -> encode_data
    fetch -> parse_balance -> encode_data
    fetch -> parse_last_tx -> encode_data 
    encode_data -> encode_tx -> submit_tx
"""
externalJobID = "9e709ebd-ee48-427e-aa55-c344a1362444"
  • JSON output of job run:
{
  "outputs": [
    "{"logs": [], "root": "0x", "status": "0x0", "gasUsed": "0x6249", "blockHash": "0x971ae8460b21fc8966a720969ea49635b8a08833a62d2d6214ba816c8edce3a3", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "blockNumber": "0x1b452c9", "contractAddress": "0x0000000000000000000000000000000000000000", "transactionHash": "0xecff4dac36d134257f28481c2442356c5ff70f916abb7ecde8c07859833df461", "transactionIndex": "0x8", "cumulativeGasUsed": "0x4ab2a"}"
  ],
  "errors": [
    null
  ],
  "inputs": {
    "jobRun": {
      "logAddress": "0x745d8f7a8d25043c4e975c6a2578471f8f94388d",
      "logBlockHash": "0xe76f4aabab2312ccf76752f8dc715cd06d2628621333146d62891ed551d5faef",
      "logBlockNumber": 28594886,
      "logData": "AAAAAAAAAAAAAAAA0ub2cG0U1tlKnaqNFhv6MBP9N493SjpKUCaIQg/sNfJdIpcxIQ2md810UzFlT56a9ePN2wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3gtrOnZAAAAAAAAAAAAAAAAAAA0ub2cG0U1tlKnaqNFhv6MBP9N4+3Kj95AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABhpp1YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACuY2dldHioaHR0cHM6Ly9sb3N0Y29pbi1mdW5jdGlvbi1hcHAuYXp1cmV3ZWJzaXRlcy5uZXQvYXBpL2xvc3Rjb2luLWFwaT9jb2RlPUEzY1ZUZzA2Q3lpR2Q3RlpsUzY0Ny9PWXBwRjJheGFyeTNkcXRnUG1QNUN5NlNjOWVtQWNHUT09JndhbGxldD0xQTF6UDFlUDVRR2VmaTJETVBUZlRMNVNMbXY3RGl2Zk5hAAAAAAAAAAAAAAAAAAAAAAAA",
      "logTopics": [
        "0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65",
        "0x3965373039656264656534383432376561613535633334346131333632343434"
      ],
      "logTxHash": "0xa733880158ddf7bf701d3c82d7837d1ad1bf48bb0f80382802320b2cff5aef2a",
      "meta": {
        "oracleRequest": {
          "callbackAddr": "0xD2e6f6706d14d6d94a9DAa8d161bFa3013FD378f",
          "callbackFunctionId": "0xb72a3f79",
          "cancelExpiration": "1638309208",
          "data": "0x6367657478a868747470733a2f2f6c6f7374636f696e2d66756e6374696f6e2d6170702e617a75726577656273697465732e6e65742f6170692f6c6f7374636f696e2d6170693f636f64653d4133635654673036437969476437465a6c533634372f4f597070463261786172793364717467506d5035437936536339656d416347513d3d2677616c6c65743d3141317a5031655035514765666932444d505466544c35534c6d7637446976664e61",
          "dataVersion": "1",
          "payment": "1000000000000000000",
          "requestId": "0x774a3a4a502688420fec35f25d229731210da677cd745331654f9e9af5e3cddb",
          "requester": "0xD2e6f6706d14d6d94a9DAa8d161bFa3013FD378f",
          "specId": "0x3965373039656264656534383432376561613535633334346131333632343434"
        }
      }
    },
    "jobSpec": {
      "databaseID": 12,
      "externalJobID": "9e709ebd-ee48-427e-aa55-c344a1362444",
      "name": "Get > BtcWalletInfo"
    }
  },
  "taskRuns": [
    {
      "type": "ethtx",
      "createdAt": "2021-11-30T21:48:55.437038Z",
      "finishedAt": "2021-11-30T21:51:31.437231Z",
      "output": ""{\"logs\": [], \"root\": \"0x\", \"status\": \"0x0\", \"gasUsed\": \"0x6249\", \"blockHash\": \"0x971ae8460b21fc8966a720969ea49635b8a08833a62d2d6214ba816c8edce3a3\", \"logsBloom\": \"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\", \"blockNumber\": \"0x1b452c9\", \"contractAddress\": \"0x0000000000000000000000000000000000000000\", \"transactionHash\": \"0xecff4dac36d134257f28481c2442356c5ff70f916abb7ecde8c07859833df461\", \"transactionIndex\": \"0x8\", \"cumulativeGasUsed\": \"0x4ab2a\"}"",
      "error": null,
      "dotId": "submit_tx",
      "status": "completed"
    },
    {
      "type": "ethabidecodelog",
      "createdAt": "2021-11-30T21:48:55.443702Z",
      "finishedAt": "2021-11-30T21:48:55.444123Z",
      "output": "{"callbackAddr":"0xd2e6f6706d14d6d94a9daa8d161bfa3013fd378f","callbackFunctionId":[183,42,63,121],"cancelExpiration":1638309208,"data":"Y2dldHioaHR0cHM6Ly9sb3N0Y29pbi1mdW5jdGlvbi1hcHAuYXp1cmV3ZWJzaXRlcy5uZXQvYXBpL2xvc3Rjb2luLWFwaT9jb2RlPUEzY1ZUZzA2Q3lpR2Q3RlpsUzY0Ny9PWXBwRjJheGFyeTNkcXRnUG1QNUN5NlNjOWVtQWNHUT09JndhbGxldD0xQTF6UDFlUDVRR2VmaTJETVBUZlRMNVNMbXY3RGl2Zk5h","dataVersion":1,"payment":1000000000000000000,"requestId":[119,74,58,74,80,38,136,66,15,236,53,242,93,34,151,49,33,13,166,119,205,116,83,49,101,79,158,154,245,227,205,219],"requester":"0xd2e6f6706d14d6d94a9daa8d161bfa3013fd378f","specId":[57,101,55,48,57,101,98,100,101,101,52,56,52,50,55,101,97,97,53,53,99,51,52,52,97,49,51,54,50,52,52,52]}",
      "error": null,
      "dotId": "decode_log",
      "status": "completed"
    },
    {
      "type": "cborparse",
      "createdAt": "2021-11-30T21:48:55.444326Z",
      "finishedAt": "2021-11-30T21:48:55.444535Z",
      "output": "{"get":"https://lostcoin-function-app.azurewebsites.net/api/lostcoin-api?code=A3cVTg06CyiGd7FZlS647/OYppF2axary3dqtgPmP5Cy6Sc9emAcGQ==\u0026wallet=1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"}",
      "error": null,
      "dotId": "decode_cbor",
      "status": "completed"
    },
    {
      "type": "http",
      "createdAt": "2021-11-30T21:48:55.444696Z",
      "finishedAt": "2021-11-30T21:48:57.865101Z",
      "output": ""{\"wallet\": \"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa\", \"exist\": true, \"balance\": 6852615533, \"last_transaction\": 1638278454}"",
      "error": null,
      "dotId": "fetch",
      "status": "completed"
    },
    {
      "type": "jsonparse",
      "createdAt": "2021-11-30T21:48:57.865562Z",
      "finishedAt": "2021-11-30T21:48:57.865892Z",
      "output": ""1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"",
      "error": null,
      "dotId": "parse_wallet",
      "status": "completed"
    },
    {
      "type": "jsonparse",
      "createdAt": "2021-11-30T21:48:57.865711Z",
      "finishedAt": "2021-11-30T21:48:57.866468Z",
      "output": "true",
      "error": null,
      "dotId": "parse_exist",
      "status": "completed"
    },
    {
      "type": "jsonparse",
      "createdAt": "2021-11-30T21:48:57.866108Z",
      "finishedAt": "2021-11-30T21:48:57.86633Z",
      "output": "1638278454",
      "error": null,
      "dotId": "parse_last_tx",
      "status": "completed"
    },
    {
      "type": "jsonparse",
      "createdAt": "2021-11-30T21:48:57.86648Z",
      "finishedAt": "2021-11-30T21:48:57.866654Z",
      "output": "6852615533",
      "error": null,
      "dotId": "parse_balance",
      "status": "completed"
    },
    {
      "type": "ethabiencode",
      "createdAt": "2021-11-30T21:48:57.866766Z",
      "finishedAt": "2021-11-30T21:48:57.867079Z",
      "output": ""0xb72a3f79774a3a4a502688420fec35f25d229731210da677cd745331654f9e9af5e3cddb00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000198729d6d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000061a6253600000000000000000000000000000000000000000000000000000000000000223141317a5031655035514765666932444d505466544c35534c6d7637446976664e61000000000000000000000000000000000000000000000000000000000000"",
      "error": null,
      "dotId": "encode_data",
      "status": "completed"
    },
    {
      "type": "ethabiencode",
      "createdAt": "2021-11-30T21:48:57.867146Z",
      "finishedAt": "2021-11-30T21:48:57.867376Z",
      "output": ""0x6ae0bc76774a3a4a502688420fec35f25d229731210da677cd745331654f9e9af5e3cddb0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000d2e6f6706d14d6d94a9daa8d161bfa3013fd378fb72a3f79000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061a69d5800000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000104b72a3f79774a3a4a502688420fec35f25d229731210da677cd745331654f9e9af5e3cddb00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000198729d6d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000061a6253600000000000000000000000000000000000000000000000000000000000000223141317a5031655035514765666932444d505466544c35534c6d7637446976664e6100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"",
      "error": null,
      "dotId": "encode_tx",
      "status": "completed"
    }
  ],
  "createdAt": "2021-11-30T21:48:55.436149Z",
  "finishedAt": "2021-11-30T21:51:31.44273Z",
  "pipelineSpec": {
    "id": 12,
    "jobID": 0,
    "dotDagSource": " decode_log [type="ethabidecodelog" abi="OracleRequest(bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data)" data="$(jobRun.logData)" topics="$(jobRun.logTopics)"] decode_cbor [type="cborparse" data="$(decode_log.data)"] fetch [type="http" method=GET url="$(decode_cbor.get)"] parse_wallet [type="jsonparse" path="wallet" data="$(fetch)"] parse_exist [type="jsonparse" path="exist" data="$(fetch)"] parse_balance [type="jsonparse" path="balance" data="$(fetch)"] parse_last_tx [type="jsonparse" path="last_transaction" data="$(fetch)"] encode_data [type="ethabiencode" abi="fulfill(bytes32 _requestId, string calldata _btc_wallet, uint256 _balance, bool _exists, uint256 _time_since_last_tx)" data="{\"_requestId\": $(decode_log.requestId), \"_btc_wallet\": $(parse_wallet), \"_balance\": $(parse_balance), \"_exists\": $(parse_exist), \"_time_since_last_tx\": $(parse_last_tx)}"] encode_tx [type="ethabiencode" abi="fulfillOracleRequest2(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes calldata data)" data="{\"requestId\": $(decode_log.requestId), \"payment\": $(decode_log.payment), \"callbackAddress\": $(decode_log.callbackAddr), \"callbackFunctionId\": $(decode_log.callbackFunctionId), \"expiration\": $(decode_log.cancelExpiration), \"data\": $(encode_data)}" ] submit_tx [type="ethtx" to="0x745d8F7A8d25043C4e975c6A2578471F8f94388d" data="$(encode_tx)"] decode_log -> decode_cbor -> fetch fetch -> parse_wallet -> encode_data fetch -> parse_exist -> encode_data fetch -> parse_balance -> encode_data fetch -> parse_last_tx -> encode_data encode_data -> encode_tx -> submit_tx "
  },
  "id": "21",
  "jobId": "12",
  "status": "completed",
  "type": "Pipeline job run"
}

Environment Variables
[replace this line with the output of the environment variables when running the node in debug mode]

Steps to Reproduce

  1. Set up node following instructions in https://docs.chain.link/docs/running-a-chainlink-node/
  2. Add the job spec provided above in the node dashboard
  3. Deploy the BitcoinWalletChecker contract (replacing the oracle address and jobId with those of the freshly deployed node)
  4. Call checker.updateWalletInfo("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa") //any bitcoin address would do
  5. Wait for job run to complete in node dashboard
  6. Calling checker.checkWalletInfo("") /any string would do since the lookup is not implemented yet/ should return the values fetched by the job, but instead returns default zero values, suggesting the callback setting these values was never called.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the Solidity fulfill function and the job spec's encode_data, encode_tx, and submit_tx tasks. Compare the multi-word _btc_wallet value with the completed ethtx output and transaction status, then trace whether the callback transaction reaches the contract. Done means the fulfillment callback executes and UpdatedWalletInfo is emitted for the supplied wallet response.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, solidity
Domain
backend-api-design, blockchain
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.