Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #946 #950

Merged
merged 15 commits into from
Jul 30, 2019
Merged

Fixes #946 #950

merged 15 commits into from
Jul 30, 2019

Conversation

erikzhang
Copy link
Member

@erikzhang erikzhang commented Jul 24, 2019

Close #947
Close #946

@codecov-io
Copy link

codecov-io commented Jul 24, 2019

Codecov Report

Merging #950 into master will increase coverage by 0.14%.
The diff coverage is 96.42%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #950      +/-   ##
==========================================
+ Coverage   52.59%   52.74%   +0.14%     
==========================================
  Files         179      180       +1     
  Lines       12707    12749      +42     
==========================================
+ Hits         6683     6724      +41     
- Misses       6024     6025       +1
Impacted Files Coverage Δ
neo/SmartContract/InteropService.NEO.cs 25.73% <0%> (ø) ⬆️
neo/IO/Helper.cs 100% <100%> (ø) ⬆️
neo/IO/Caching/DataCache.cs 100% <100%> (ø) ⬆️
neo/IO/ByteArrayComparer.cs 90% <90%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9e2faf5...ca51799. Read the comment docs.

@shargon
Copy link
Member

shargon commented Jul 24, 2019

Stranger things

image

@shargon
Copy link
Member

shargon commented Jul 24, 2019

Very stranger things ...

image

@shargon
Copy link
Member

shargon commented Jul 24, 2019

Grouping was playing with us 😆

@erikzhang
Copy link
Member Author

You can't remove grouping. Because it is stored with grouping and sorted with grouping in leveldb.

@shargon
Copy link
Member

shargon commented Jul 25, 2019

The last fix was wrong.

image

This will produce that contract destroy or other calls fail, we need to call Find without the grouping.

foreach (var pair in engine.Snapshot.Storages.Find(hash.ToArray()))

In fact, we should remove the grouping, is very confuse.

@erikzhang
Copy link
Member Author

Can you point out where it is wrong?

@shargon
Copy link
Member

shargon commented Jul 25, 2019

I think that we can remove the grouping like this

image

We know that this stream is fixed and is for the key, we could know the length.

@erikzhang
Copy link
Member Author

The BaseStream may be a NetworkStream, and we can't get the Length from it.

@shargon
Copy link
Member

shargon commented Jul 25, 2019

Is wrong because you need to pass the arguments in the unit test with the group

ac85174#diff-a3f0e31debacd97d5d593e04923f7f99R53

But in other calls within the code, it was called without grouping.

foreach (var pair in engine.Snapshot.Storages.Find(hash.ToArray()))

@shargon
Copy link
Member

shargon commented Jul 25, 2019

The BaseStream may be a NetworkStream, and we can't get the Length from it.

I know it, but only in this case always will come from a storage (file).

@erikzhang
Copy link
Member Author

But in other calls within the code, it was called without grouping.

Only StorageKey.Key is grouped. StorageKey.ScriptHash is not grouped. So it's okay.

@erikzhang
Copy link
Member Author

The only case that use Storage.ScriptHash in Find() is this:

private static bool Storage_Find(ApplicationEngine engine)
{
if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface)
{
StorageContext context = _interface.GetInterface<StorageContext>();
if (!CheckStorageContext(engine, context)) return false;
byte[] prefix = engine.CurrentContext.EvaluationStack.Pop().GetByteArray();
byte[] prefix_key;
using (MemoryStream ms = new MemoryStream())
{
int index = 0;
int remain = prefix.Length;
while (remain >= 16)
{
ms.Write(prefix, index, 16);
ms.WriteByte(0);
index += 16;
remain -= 16;
}
if (remain > 0)
ms.Write(prefix, index, remain);
prefix_key = context.ScriptHash.ToArray().Concat(ms.ToArray()).ToArray();
}
StorageIterator iterator = engine.AddDisposable(new StorageIterator(engine.Snapshot.Storages.Find(prefix_key).Where(p => p.Key.Key.Take(prefix.Length).SequenceEqual(prefix)).GetEnumerator()));
engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(iterator));
return true;
}
return false;
}

And it has processed the group correctly.

@shargon
Copy link
Member

shargon commented Jul 25, 2019

Only StorageKey.Key is grouped. StorageKey.ScriptHash is not grouped. So it's okay.

I understand you, but is very weird, if i use the class, and i insert 0x00, and i need to find it with new byte[21], i need to know how works the storage layer, and this is wrong.

@erikzhang
Copy link
Member Author

erikzhang commented Jul 25, 2019

I think you misunderstood. The first 20 bytes is the script hash. The last byte is the prefix.

Your original unit test missed scripthash, I just added it for you.

@shargon
Copy link
Member

shargon commented Jul 25, 2019

Now I catch you 😆 , i was thinking in the key, but Find include the scriptHash and the key, so you are right!

shargon
shargon previously approved these changes Jul 25, 2019
Copy link
Member

@vncoelho vncoelho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@erikzhang, it looks great to me.

@shargon, I am trying to complete understand these UTs.

Can we take 1-2 more days to check it out? I want to talk with @igormcoelho about this sorting strategy.

return x.Length.CompareTo(y.Length);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rodoufu can you review this?

if (pair.Value.State != TrackState.Deleted && (key_prefix == null || pair.Key.ToArray().Take(key_prefix.Length).SequenceEqual(key_prefix)))
yield return new KeyValuePair<TKey, TValue>(pair.Key, pair.Value.Item);
cached = dictionary
.Where(p => p.Value.State != TrackState.Deleted && (key_prefix == null || p.Key.ToArray().Take(key_prefix.Length).SequenceEqual(key_prefix)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, must do it in a deserialized format, to avoid different storage formats affecting Find.

.OrderBy(p => p.KeyBytes, ByteArrayComparer.Default)
.ToArray();
}
var uncached = FindInternal(key_prefix ?? new byte[0])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this FindInternal implementation-specific? This is a good moment to avoid specific decisions affecting the outcome of Find.

p.Key,
p.Value
));
using (var e1 = cached.GetEnumerator())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this "intercalation" strategy should be done in a separate function, so it's clear and also testable (perhaps useful in other situations too).

@igormcoelho
Copy link
Contributor

igormcoelho commented Jul 25, 2019

Just to make sure, this sort is not considering Grouping anymore, just raw key, correct? Because grouping looks like an implementation-specific strategy, not a protocol level one.
I don't think Find should consider ScriptHash and other prefixes... it is already processed on a selected contract, so all scripthashes and prefixes are supposed to be the same. Although I don't think this should affect any result, since they are the same prefixes 😂

@erikzhang
Copy link
Member Author

Just to make sure, this sort is not considering Grouping anymore, just raw key, correct?

I optimized it, and now our grouping algorithm will not affect the sorting.

@@ -92,17 +92,16 @@ public static byte[] ReadBytesWithGrouping(this BinaryReader reader)
{
using (MemoryStream ms = new MemoryStream())
{
int padding = 0;
int count;
do
{
byte[] group = reader.ReadBytes(GroupingSizeInBytes);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should reuse this buffer

byte[] group=new byte[GroupSizeInBytes];
do{
read.Read(group,0,GroupSizeInBytes);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read.Read(group,0,GroupSizeInBytes);

This may read data less than GroupSizeInBytes and use an extra loop, which is very troublesome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Storage.Find returns items out of order
5 participants