smartcontractkit / smartcontractkit/chainlink

[NODE] Can’t create multi-variable response

Open
#6,659 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

[NODE] Can’t create multi-variable response

Description

On version 1.4.0-root, 1.4.1-root (maybe 1.3.0 too), job pipeline tasks successful completes, but submit_tx step (ethtx task) don't send transaction to Operator contract.
After downgrade to version 1.1.1, submit_tx sends transaction correctly!!!

Digging deep

Result of encode_tx step (ethabiencode task) and all previous steps in both Node version equals.

Temporary solve

Downgrade chainlink node to version 1.1.1

Steps to reproduce

  • ... std steps to setup interaction with chainlink node jobs from your smart contract. Please, read docs for this.
  • Call MultiWordConsumer.sol->fulfillMultipleParameters
  • After transaction ends call by example, btc property. Just to make sure the value is 0. This incorrect behaviour, that should be equal to corresponding number from job run result. On version 1.1.1 value equals to correct value from job run

Environment

Docker version 20.10.16, build aa7e414
docker-compose version 1.29.2, build unknown

Compose spec
services:
  eth-blockchain:
    image: trufflesuite/ganache-cli:v6.12.2
    command: --port 8545 --chainId 1337 --forkCacheSize 268435456 --debug --verbose --mnemonic "$HD_WALLET_MNEMONIC" --account_keys_path accounts.json --db /data/ganache
    networks:
      - blockchain
    ports:
      - 8545:8545
    volumes:
      - ./data/eth-blockchain/accounts/accounts.json:/app/accounts.json
      - blockchain_data:/data/ganache

  db-chainlink-node:
    image: postgres:14.2-alpine
    env_file:
      - ./services/db-chainlink-node/.env
    networks:
      - blockchain
    volumes:
      - db_chainlink_data:/var/lib/postgresql/data

  chainlink-node:
    image: smartcontract/chainlink:1.4.0-root
    command: local node -p /chainlink/.password -a /chainlink/.api
    env_file:
      - ./services/chainlink-node/.env.local
    networks:
      - blockchain
    ports:
      - 6688:6688
    volumes:
      - ./services/chainlink-node/.password:/chainlink/.password
      - ./services/chainlink-node/.api:/chainlink/.api
      - chainlink-node-data:/chainlink

networks:
  blockchain:
    driver: bridge

volumes:
  chainlink-node-data:
    driver: local
    driver_opts:
      type: none
      device: "./data/chainlink-node"
      o: bind

  blockchain_data:
    driver: local
    driver_opts:
      type: none
      device: "./data/eth-blockchain/blockchain"
      o: bind

  db_chainlink_data:
    driver: local
    driver_opts:
      type: none
      device: "./data/db-chainlink-node"
      o: bind
./services/chainlink-node/.env.local
ROOT=/chainlink
LOG_LEVEL=debug
ETH_CHAIN_ID=1337
CHAINLINK_TLS_PORT=0
SECURE_COOKIES=false
ALLOW_ORIGINS=*
ETH_URL=ws://eth-blockchain:8545
DATABASE_URL=postgresql://admin:admin@db-chainlink-node:5432/chainlink_node?sslmode=disable
CHAINLINK_DEV=true
CHAINLINK_PORT=6688
DEFAULT_HTTP_ALLOW_UNRESTRICTED_NETWORK_ACCESS=true
INSECURE_SKIP_VERIFY=true
./services/db-chainlink-node/.env
POSTGRES_USER=admin
POSTGRES_PASSWORD=admin
POSTGRES_DB=chainlink_node
./.env.local
HD_WALLET_MNEMONIC=stereo consider quality wild fat farm symptom bundle laundry side one lemon
./services/chainlink-node/.password:/chainlink/.password
stereo consider quality wild fat farm symptom bundle laundry side one lemon
./services/chainlink-node/.api:/chainlink/.api
user@example.com
password
multi_word_job.toml

This file equals to multi-variable job example from Chainlink official docs

type = "directrequest"
schemaVersion = 1
name = "multi-word (TOML)"
maxTaskDuration = "0s"
contractAddress = "YOUR_OPERATOR_CONTRACT_ADDRESS"
externalJobID = "438c9be2-7aff-4c58-bb7f-9632acaa418f"
minIncomingConfirmations = 0
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)"]
    decode_log -> decode_cbor
    decode_cbor -> btc
    decode_cbor -> usd
    decode_cbor -> eur
    btc          [type="http" method=GET url="$(decode_cbor.urlBTC)" allowunrestrictednetworkaccess="true"]
    btc_parse    [type="jsonparse" path="$(decode_cbor.pathBTC)" data="$(btc)"]
    btc_multiply [type="multiply" input="$(btc_parse)", times="100000"]
    btc -> btc_parse -> btc_multiply
    usd          [type="http" method=GET url="$(decode_cbor.urlUSD)" allowunrestrictednetworkaccess="true"]
    usd_parse    [type="jsonparse" path="$(decode_cbor.pathUSD)" data="$(usd)"]
    usd_multiply [type="multiply" input="$(usd_parse)", times="100000"]
    usd -> usd_parse -> usd_multiply
    eur          [type="http" method=GET url="$(decode_cbor.urlEUR)" allowunrestrictednetworkaccess="true"]
    eur_parse    [type="jsonparse" path="$(decode_cbor.pathEUR)" data="$(eur)"]
    eurs_multiply [type="multiply" input="$(eur_parse)", times="100000"]
    eur -> eur_parse -> eurs_multiply
    btc_multiply -> encode_mwr
    usd_multiply -> encode_mwr
    eurs_multiply -> encode_mwr
    // MWR API does NOT auto populate the requestID.
    encode_mwr [type="ethabiencode"
                abi="(bytes32 requestId, uint256 _btc, uint256 _usd, uint256 _eurs)"
                data="{\\"requestId\\": $(decode_log.requestId), \\"_btc\\": $(btc_multiply), \\"_usd\\": $(usd_multiply), \\"_eurs\\": $(eurs_multiply)}"
                ]
    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_mwr)}"
                ]
    submit_tx  [type="ethtx" to="YOUR_OPERATOR_CONTRACT_ADDRESS" data="$(encode_tx)"]
    encode_mwr -> encode_tx -> submit_tx
"""
MultiWordConsumer.sol

This file so close (only constructor modified) to multi-variable response example contract from Chainlink official docs

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import '@chainlink/contracts/src/v0.8/ChainlinkClient.sol';
import '@chainlink/contracts/src/v0.8/ConfirmedOwner.sol';

/**
 * Request testnet LINK and ETH here: https://faucets.chain.link/
 * Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
 */

/**
 * THIS IS AN EXAMPLE CONTRACT WHICH USES HARDCODED VALUES FOR CLARITY.
 * PLEASE DO NOT USE THIS CODE IN PRODUCTION.
 */
contract MultiWordConsumer is ChainlinkClient, ConfirmedOwner {
    using Chainlink for Chainlink.Request;

    bytes32 private jobId;
    uint256 private fee;

    // multiple params returned in a single oracle response
    uint256 public btc;
    uint256 public usd;
    uint256 public eur;

    event RequestMultipleFulfilled(bytes32 indexed requestId, uint256 btc, uint256 usd, uint256 eur);

    /**
     * @notice Initialize the link token and target oracle
     * @dev The oracle address must be an Operator contract for multiword response
     *
     *
     * Kovan Testnet details:
     * Link Token: 0xa36085F69e2889c224210F603D836748e7dC0088
     * Oracle: 0x74EcC8Bdeb76F2C6760eD2dc8A46ca5e581fA656 (Chainlink DevRel)
     * jobId: 53f9755920cd451a8fe46f5087468395
     *
     */
    constructor(address _linkToken, address _operator) ConfirmedOwner(msg.sender) {
        setChainlinkToken(_linkToken);
        setChainlinkOracle(_operator);
        jobId = '438c9be27aff4c58bb7f9632acaa418f';
        fee = (1 * LINK_DIVISIBILITY) / 10; // 0,1 * 10**18 (Varies by network and job)
    }

    /**
     * @notice Request mutiple parameters from the oracle in a single transaction
     */
    function requestMultipleParameters() public {
        Chainlink.Request memory req = buildChainlinkRequest(
            jobId,
            address(this),
            this.fulfillMultipleParameters.selector
        );
        req.add('urlBTC', 'https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=BTC');
        req.add('pathBTC', 'BTC');
        req.add('urlUSD', 'https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD');
        req.add('pathUSD', 'USD');
        req.add('urlEUR', 'https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=EUR');
        req.add('pathEUR', 'EUR');
        sendChainlinkRequest(req, fee); // MWR API.
    }

    /**
     * @notice Fulfillment function for multiple parameters in a single request
     * @dev This is called by the oracle. recordChainlinkFulfillment must be used.
     */
    function fulfillMultipleParameters(
        bytes32 requestId,
        uint256 btcResponse,
        uint256 usdResponse,
        uint256 eurResponse
    ) public recordChainlinkFulfillment(requestId) {
        emit RequestMultipleFulfilled(requestId, btcResponse, usdResponse, eurResponse);
        btc = btcResponse;
        usd = usdResponse;
        eur = eurResponse;
    }

    /**
     * Allow withdraw of Link tokens from the contract
     */
    function withdrawLink() public onlyOwner {
        LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
        require(link.transfer(msg.sender, link.balanceOf(address(this))), 'Unable to transfer');
    }
}

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 multi_word_job.toml, MultiWordConsumer.sol, and the submit_tx ethtx step, then reproduce the job with the supplied Docker Compose configuration on Chainlink node 1.4.0-root and compare it with 1.1.1. Done means the transaction is sent and the btc, usd, and eur values are populated from the job result.

Written by the indexing model from the issue text.

Assessment

Tech stack
docker, docker-compose, postgresql, solidity
Domain
backend, blockchain
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
32/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.