2

How do you return the value pushed before with only one function? I read some information about events, so I think it may be a solution, but it doesn't return the exact value pushed before. There is my new source below which added events:

pragma solidity ^0.4.0;
////////////////////////////////////////////////////////////
// This is an example contract hacked together at a meetup.
// It is by far not complete and only used to show some
// features of Solidity.
////////////////////////////////////////////////////////////
contract queue
{
    struct Queue {
        uint256[] data;
        uint256 front;
        uint256 back;
    }
    /// @dev the number of elements stored in the queue.
    function length(Queue storage q) constant internal returns (uint256) {
        return q.back - q.front;
    }
    /// @dev the number of elements this queue can hold
    function capacity(Queue storage q) constant internal returns (uint256) {
        return q.data.length - 1;
    }
    /// @dev push a new element to the back of the queue
    function push(Queue storage q, uint256 data) internal
    {
        if ((q.back + 1) % q.data.length == q.front)
            return; // throw;
        q.data[q.back] = data;
        q.back = (q.back + 1) % q.data.length;
    }


    /// @dev remove and return the element at the front of the queue
    function pop(Queue storage q) internal returns (uint256 r)
    {
        if (q.back == q.front)
            return; // throw;
        r = q.data[q.front];
        delete q.data[q.front];
        q.front = (q.front + 1) % q.data.length;
        return r;
    }
    Queue requests;
    event PopEvent(bool ok); //my event designed by me here.
    function queue() {
        requests.data.length = 200;
    }
    function addRequest(uint256 d) {
        push(requests, d);
    }
    function popRequest()  returns (uint256) {
        PopEvent(true);
        return pop(requests);
    }
    function queueLength()  constant returns (uint256) {
        return length(requests);
    }
    function hello() constant returns(string s){ 
        return 'hello world!';
    }
}

I just want the popRequest function to return the exact value pushed before.

0TTT0
  • 672
  • 2
  • 10
  • 20
jiebang
  • 993
  • 1
  • 12
  • 18

2 Answers2

6

I saw this yesterday.

There are issues in the logic. When I made the queue size very small (3) I got incorrect results starting at job 4, so there are problems in the algo.

We're not in a memory-bound world here so I question the need for heroics unless trying to optimize gas cost. A fixed sized array might be ideal if you want to re-use slots. You can drop the delete operation.

The simplest possible implementation is just a queue of unlimited size and keep walking forward (2^256 maximum jobs, ever).

It IS possible to return a value while also mutating the state change, however it's not easy to SEE the result outside of a contract. What I mean is a FIFO queue is sort of useless unless it's got a client (another contract) that submits jobs. A client contract that sends a message will indeed receive the returned value from a function that also mutates state.

That's quite different (and admittedly confusing) from a Web3 client that sends a transaction and gets a txnhash but no result.

Event emissions are one way to go. I wonder if this is sufficient in a multi-threaded situation where the clients need to know which events belong to them.

I made a little example if a FIFO queue that seems to work. It uses unlimited (2^256) queue size so it's very simple. It returns the cursor position and the data value "popped" and it sends this information back to the fifoClient contract that logs all results.

Possibly you'll find some ideas that will help.

pragma solidity ^0.4.6;

contract FIFO {

    uint[] public fifoQueue;
    uint public cursorPosition;

    function queueDepth()
        public
        constant
        returns(uint queueDepth)
    {
        return fifoQueue.length - cursorPosition;
    }

    function push(uint requestData) 
        public
        returns(uint jobNumber)
    {
        if(fifoQueue.length + 1 < fifoQueue.length) throw; // exceeded 2^256 push requests
        return fifoQueue.push(requestData) - 1;
    }

    function pop() 
        public
        returns(uint, uint)
    {
        if(fifoQueue.length==0) throw;
        if(fifoQueue.length - 1 < cursorPosition) throw;
        cursorPosition += 1;
        return (cursorPosition -1, fifoQueue[cursorPosition -1]);
    }
}

contract FifoClient {

    FIFO public jobQueue;

    event LogPush(address sender, uint jobNumber, uint jobValue);
    event LogPop (address sender, uint jobNumber, uint jobValue);

    function FifoClient() {
        // fifoClient makes it's own FIFO queue
        jobQueue = new FIFO();
    } 

    function push(uint jobValue)
        public
        returns(uint jobNumber)
    {
        uint jobNum = jobQueue.push(jobValue);
        LogPush(msg.sender, jobNum, jobValue);
        return jobNum;
    }

    function pop() 
        public
        returns(uint, uint)
    {
        uint jobNum;
        uint jobVal;
        (jobNum, jobVal) = jobQueue.pop();
        LogPop(msg.sender, jobNum, jobVal);
        return(jobNum, jobVal);
    }

}

Here it is in Remix just to show it working.

Only necessary to deploy a FifoClient. It commissions its own FIFO contract to work with automatically.

enter image description here

Remix is a great tool for working out the contract details so you can be confident about what to expect before dealing with the Web3 interface to the outside world.

Hope it helps.

Rob Hitchens
  • 55,151
  • 11
  • 89
  • 145
  • I can't tell you how much I appreciate your answer. – jiebang Apr 07 '17 at 01:47
  • Thank you for your help. Because I am a new hand of solidity, so there may be some errors in my procedure. I now can't find the cursor position and the data value "popped". Below are my procedure: – jiebang Apr 07 '17 at 02:45
  • I paste the Web3 deploy contents of client to the geth console. And then I mined for a while ,it shows me that Contract mined! address: 0x00f5a009c78505779eb174b897f6c002eeb80120 transactionHash: 0x23cfe4a3ececeb394af3edf14b436325013a75ac7f7e2e7f5f226878718096bb . Then : Contract mined! address: 0x00f5a009c78505779eb174b897f6c002eeb80120 transactionHash: 0x23cfe4a3ececeb394af3edf14b436325013a75ac7f7e2e7f5f226878718096bb – jiebang Apr 07 '17 at 02:48
  • My question is where or how to see the cursor position and the data value "popped"? thank you very much . – jiebang Apr 07 '17 at 02:49
  • I use > queue_sol_fifoclient.pop.call() [4, 3] could see the result, is it the right way to see the result? – jiebang Apr 07 '17 at 03:05
  • .call() is should give a correct response but it won't change the state and it wouldn't be threadsafe. A lot users could get the same result over the time of a block. Have a look here: http://ethereum.stackexchange.com/questions/765/what-is-the-difference-between-a-transaction-and-a-call/770#770 and here: http://ethereum.stackexchange.com/questions/2024/how-to-access-event-log-by-knowing-the-contract-address-web3. General idea is use sendTransaction (not call) to pop and event watcher to "see" results. – Rob Hitchens Apr 07 '17 at 06:24
  • Hi, I have a question here: I can use method named push to store the data such as 2 to the queue. But the question is 1st. where is the 2 stored after mining? is the 2 stored to the blockchain as the blog format? 2nd. where is the 2 stored if we do not add the event? thank you . – jiebang Apr 20 '17 at 06:39
1

Basically it's not possible to both mutate the state while return a value via one function call AFAIK.

Here is a revised version that can achieve what you want, the PopEvent is better to accept the actually element value that gets popped out instead of a boolean value.

event ElementPopped(uint256 _element);

In the revised version, additionally I added an ElementPushed event which accepts a second parameter to tell the index in the queue where the element is pushed.

Hope this helps.

Yuanfei Zhu
  • 578
  • 4
  • 16
  • Thank you very much. I compile your revised source via https://ethereum.github.io/browser-solidity. And then paste the result to the geth console.After mining for a while, I type the commands below in the geth console,but it seems not to be suitable for my hope: – jiebang Apr 07 '17 at 01:36
  • queue_sol_queue.addRequest(1)

    "0xfe2a3fafa8a47439b583a15c14c5282c1657bfbc9a485a591ca68fcb90a021b4"

    queue_sol_queue.addRequest(2)

    "0x83d1c13a2573d997617238c736e8711522a7bf84613637cc581f66fb86daad59"

    queue_sol_queue.addRequest(3)

    "0x0c428cbd9c4dd9e6638066c67a18c16dc386b01bfc9255e90d8d71ca911716eb"

    queue_sol_queue.popRequest()

    "0x772bb011248822dc69a355d1ebb9c948981dc706b944653d034fb3287f4dcb73" you can see that the queue_sol_queue.popRequest() didin't show the value but a transaction hash. So what's wrong with my procedure? thank you.

    – jiebang Apr 07 '17 at 01:36
  • You need to watch the event stream via web3, event doesn't show up as the return value. And make sure to check the answer above, Rob has posted a more deep driven explanation. – Yuanfei Zhu Apr 07 '17 at 01:40
  • Excuse me. I still don't know how to watch the event in web3 console? Could you tell me ? – jiebang Apr 10 '17 at 08:14
  • Related: http://ethereum.stackexchange.com/questions/2024/how-to-access-event-log-by-knowing-the-contract-address-web3 – Yuanfei Zhu Apr 10 '17 at 12:09