neo: Storage.Find duplicate values

The Neo.Storage.Find SYSCALL returns duplicate results causing the invoker to be charged for unnecessary extra Neo.Iterator.Key and Neo.Iterator.Next calls.

Take this contract

using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Services.Neo;


namespace NeoContract1
{
    public class Contract1 : SmartContract
    {
        public static void Main(int op)
        {
            if (op == 1)
            {
                Storage.Put(Storage.CurrentContext, "AA_1", "1");
                Storage.Put(Storage.CurrentContext, "AA_2", "2");
                Storage.Put(Storage.CurrentContext, "AA_3", "3");
            } else if (op == 2)
            {
                Iterator<string, byte[]> si = Storage.Find(Storage.CurrentContext, "AA_");
                notify_results(si);
            }
            else if (op == 3)
            {
                Storage.Get(Storage.CurrentContext, "AA_1");
                Iterator<string, byte[]> si = Storage.Find(Storage.CurrentContext, "AA_");
                notify_results(si);
            }
        }

        private static void notify_results(Iterator<string, byte[]> si)
        {
            Runtime.Notify("Starting");
            while (si.Next())
            {
                Runtime.Notify(si.Key);
            }
            Runtime.Notify("Done");
        }
    }
}

Invoke the contract once with the contract op argument being 1 to persist the contents to the DB. Next invoke the contract with op being 2 and 3.

  • 2 gives the expected: AA_1, AA_2, AA_3,
  • 3 however gives: AA_1, AA_2, AA_3, AA_1, AA_1 The last 2 values should not be there. I believe this is a caching issue.

Take a secondary contract that allows us to call Contract 1 multiple times in a single DB snapshot

using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Services.Neo;
using System;
using System.Numerics;

namespace NeoContract2
{
    public class Contract2 : SmartContract
    {
        [Appcall("f4032cfb5505c05d935db8bd41b608005eca0b00")]
        public static extern void OtherContract(int op);

        public static void Main(int op)
        {
            if (op == 1)
            {
                OtherContract(2);
                OtherContract(2);
            }
            else if (op == 2)
            {
                OtherContract(2);
                OtherContract(3);
            }
            else
            {
                OtherContract(3);
                OtherContract(2);
            }
        }
    }
}
  • op is 1 is still correct and produces: AA_1, AA_2, AA_3 followed by another AA_1, AA_2, AA_3
  • op is 2 is wrong in the same way it was wrong when calling contract 1 directly. We first get AA_1, AA_2, AA_3 followed by AA_1, AA_2, AA_3, AA_1, AA_1
  • op is 3 is where it gets really interesting. Given the previous results you’d expect: AA_1, AA_2, AA_3, AA_1, AA_1 followed by AA_1, AA_2, AA_3. Instead you now get 2 times the sequence AA_1, AA_2, AA_3, AA_1, AA_1

Contract AVM files included in the zip file for easy deploy/reproducing contracts.zip

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 15 (15 by maintainers)

Most upvoted comments

I should not report issues after an extra long day of debugging. Made a rookie mistake by looking at neo-python’s interpretation 😅

Ricarrrdo, @lock9, here is our flag! 🗡️

But all issues need to be tagged, it has been like this since the old days. We have 96 issues open, 95 have tags, why this one is different? I just didn’t use the “discussion” tag, because this doesn’t look like a discussion. This looks more like a “support request”, I agree that bug may not be the best tag, but we need to tag it.