-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Add random number to neo #2477
Add random number to neo #2477
Changes from all commits
b3680dc
6579546
192e553
bcb863a
a525e77
7b99fad
e47e565
1391184
80fff9d
cc1b9f7
1b2f912
10e7b6d
4ecdc48
d8f2bb2
d20bd0d
110db72
0126393
bb3e29d
cd23177
d58a3ce
99fa4b7
5ed2940
f52c2b2
60086e3
798cafc
91a8836
6a1ba3d
11ba5b7
1ba655f
9470311
728f479
62eda32
a0ee63e
e62b56b
303bbbb
e5aa633
d0c81f7
c48adbc
491df4a
2cec716
73b989c
3087243
e43c150
714bac5
3d3d3ed
4a508ae
9359dff
4360d00
8a059bb
2fab87f
866ffe4
bcd6b27
874e633
f5b3bf1
1ca4244
8f89321
654e37b
5258a1b
ef41ef2
daa642b
ac35197
460f0a6
77c7775
2c6be15
30f33d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
using System; | ||
using System.Buffers.Binary; | ||
using System.Runtime.CompilerServices; | ||
using System.Security.Cryptography; | ||
|
||
namespace Neo.Cryptography | ||
{ | ||
/// <summary> | ||
/// Computes the 128 bits murmur hash for the input data. | ||
/// </summary> | ||
public sealed class Murmur128 : HashAlgorithm | ||
{ | ||
private const ulong c1 = 0x87c37b91114253d5; | ||
private const ulong c2 = 0x4cf5ad432745937f; | ||
private const int r1 = 31; | ||
private const int r2 = 33; | ||
private const uint m = 5; | ||
private const uint n1 = 0x52dce729; | ||
private const uint n2 = 0x38495ab5; | ||
|
||
private readonly uint seed; | ||
private int length; | ||
|
||
public override int HashSize => 128; | ||
|
||
private ulong H1 { get; set; } | ||
private ulong H2 { get; set; } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="Murmur128"/> class with the specified seed. | ||
/// </summary> | ||
/// <param name="seed">The seed to be used.</param> | ||
public Murmur128(uint seed) | ||
{ | ||
this.seed = seed; | ||
Initialize(); | ||
} | ||
|
||
protected override void HashCore(byte[] array, int ibStart, int cbSize) | ||
{ | ||
length += cbSize; | ||
int remainder = cbSize & 15; | ||
int alignedLength = ibStart + (cbSize - remainder); | ||
for (int i = ibStart; i < alignedLength; i += 16) | ||
{ | ||
ulong k1 = BinaryPrimitives.ReadUInt64BigEndian(array.AsSpan(i)); | ||
k1 *= c1; | ||
k1 = RotateLeft(k1, r1); | ||
k1 *= c2; | ||
H1 ^= k1; | ||
H1 = RotateLeft(H1, 27); | ||
H1 += H2; | ||
H1 = H1 * m + n1; | ||
|
||
ulong k2 = BinaryPrimitives.ReadUInt64BigEndian(array.AsSpan(i + 8)); | ||
k2 *= c2; | ||
k2 = RotateLeft(k2, r2); | ||
k2 *= c1; | ||
H2 ^= k2; | ||
H2 = RotateLeft(H2, 31); | ||
H2 += H1; | ||
H2 = H2 * m + n2; | ||
} | ||
|
||
if (remainder > 0) | ||
{ | ||
ulong remainingBytesL = 0, remainingBytesH = 0; | ||
switch (remainder) | ||
{ | ||
case 15: remainingBytesH ^= (ulong)array[alignedLength + 14] << 48; goto case 14; | ||
case 14: remainingBytesH ^= (ulong)array[alignedLength + 13] << 40; goto case 13; | ||
case 13: remainingBytesH ^= (ulong)array[alignedLength + 12] << 32; goto case 12; | ||
case 12: remainingBytesH ^= (ulong)array[alignedLength + 11] << 24; goto case 11; | ||
case 11: remainingBytesH ^= (ulong)array[alignedLength + 10] << 16; goto case 10; | ||
case 10: remainingBytesH ^= (ulong)array[alignedLength + 9] << 8; goto case 9; | ||
case 9: remainingBytesH ^= (ulong)array[alignedLength + 8] << 0; goto case 8; | ||
case 8: remainingBytesL ^= (ulong)array[alignedLength + 7] << 56; goto case 7; | ||
case 7: remainingBytesL ^= (ulong)array[alignedLength + 6] << 48; goto case 6; | ||
case 6: remainingBytesL ^= (ulong)array[alignedLength + 5] << 40; goto case 5; | ||
case 5: remainingBytesL ^= (ulong)array[alignedLength + 4] << 32; goto case 4; | ||
case 4: remainingBytesL ^= (ulong)array[alignedLength + 3] << 24; goto case 3; | ||
case 3: remainingBytesL ^= (ulong)array[alignedLength + 2] << 16; goto case 2; | ||
case 2: remainingBytesL ^= (ulong)array[alignedLength + 1] << 8; goto case 1; | ||
case 1: remainingBytesL ^= (ulong)array[alignedLength] << 0; break; | ||
} | ||
|
||
H2 ^= RotateLeft(remainingBytesH * c2, r2) * c1; | ||
H1 ^= RotateLeft(remainingBytesL * c1, r1) * c2; | ||
} | ||
} | ||
|
||
protected override byte[] HashFinal() | ||
{ | ||
ulong len = (ulong)length; | ||
H1 ^= len; H2 ^= len; | ||
|
||
H1 += H2; | ||
H2 += H1; | ||
|
||
H1 = FMix(H1); | ||
H2 = FMix(H2); | ||
|
||
H1 += H2; | ||
H2 += H1; | ||
|
||
var buffer = new byte[16]; | ||
Array.Copy(BitConverter.GetBytes(H1), 0, buffer, 0, 8); | ||
Array.Copy(BitConverter.GetBytes(H2), 0, buffer, 8, 8); | ||
|
||
return buffer; | ||
} | ||
|
||
public override void Initialize() | ||
{ | ||
H1 = H2 = seed; | ||
length = 0; | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static ulong RotateLeft(ulong x, byte n) | ||
{ | ||
return (x << n) | (x >> (64 - n)); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static ulong FMix(ulong h) | ||
{ | ||
// pipelining friendly algorithm | ||
h = (h ^ (h >> 33)) * 0xff51afd7ed558ccd; | ||
h = (h ^ (h >> 33)) * 0xc4ceb9fe1a85ec53; | ||
|
||
return (h ^ (h >> 33)); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Numerics; | ||
using Array = Neo.VM.Types.Array; | ||
|
||
namespace Neo.SmartContract | ||
|
@@ -83,6 +84,12 @@ partial class ApplicationEngine | |
/// </summary> | ||
public static readonly InteropDescriptor System_Runtime_GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", nameof(GetInvocationCounter), 1 << 4, CallFlags.None); | ||
|
||
/// <summary> | ||
/// The <see cref="InteropDescriptor"/> of System.Runtime.GetRandom. | ||
/// Gets the random number generated from the VRF. | ||
/// </summary> | ||
public static readonly InteropDescriptor System_Runtime_GetRandom = Register("System.Runtime.GetRandom", nameof(GetRandom), 1 << 4, CallFlags.None); | ||
erikzhang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// <summary> | ||
/// The <see cref="InteropDescriptor"/> of System.Runtime.Log. | ||
/// Writes a log. | ||
|
@@ -244,6 +251,17 @@ protected internal int GetInvocationCounter() | |
return counter; | ||
} | ||
|
||
/// <summary> | ||
/// The implementation of System.Runtime.GetRandom. | ||
/// Gets the next random number. | ||
/// </summary> | ||
/// <returns>The next random number.</returns> | ||
protected internal BigInteger GetRandom() | ||
{ | ||
nonceData = Cryptography.Helper.Murmur128(nonceData, ProtocolSettings.Network); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still think that merkle tree should be used too, in this way at least the primary will require a brute force to add a transaction, no like now There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
this is a temperary solution, ngd is working on a bls solution. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We're planning to add a fulling BLS solution. However, since we'd like to launch mainnet next month, not much time left. So at least we must ensure |
||
return new BigInteger(nonceData, isUnsigned: true); | ||
} | ||
|
||
/// <summary> | ||
/// The implementation of System.Runtime.Log. | ||
/// Writes a log. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,6 +47,7 @@ public partial class ApplicationEngine : ExecutionEngine | |
private readonly Dictionary<ExecutionContext, ContractTaskAwaiter> contractTasks = new(); | ||
private readonly uint exec_fee_factor; | ||
internal readonly uint StoragePrice; | ||
private byte[] nonceData; | ||
|
||
/// <summary> | ||
/// Gets the descriptors of all interoperable services available in NEO. | ||
|
@@ -124,7 +125,7 @@ public partial class ApplicationEngine : ExecutionEngine | |
/// <param name="persistingBlock">The block being persisted. It should be <see langword="null"/> if the <paramref name="trigger"/> is <see cref="TriggerType.Verification"/>.</param> | ||
/// <param name="settings">The <see cref="Neo.ProtocolSettings"/> used by the engine.</param> | ||
/// <param name="gas">The maximum gas used in this execution. The execution will fail when the gas is exhausted.</param> | ||
protected ApplicationEngine(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas) | ||
protected unsafe ApplicationEngine(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas) | ||
{ | ||
this.Trigger = trigger; | ||
this.ScriptContainer = container; | ||
|
@@ -134,6 +135,14 @@ protected ApplicationEngine(TriggerType trigger, IVerifiable container, DataCach | |
this.gas_amount = gas; | ||
this.exec_fee_factor = snapshot is null || persistingBlock?.Index == 0 ? PolicyContract.DefaultExecFeeFactor : NativeContract.Policy.GetExecFeeFactor(Snapshot); | ||
this.StoragePrice = snapshot is null || persistingBlock?.Index == 0 ? PolicyContract.DefaultStoragePrice : NativeContract.Policy.GetStoragePrice(Snapshot); | ||
this.nonceData = container is Transaction tx ? tx.Hash.ToArray()[..16] : new byte[16]; | ||
if (persistingBlock is not null) | ||
{ | ||
fixed (byte* p = nonceData) | ||
{ | ||
*(ulong*)p ^= persistingBlock.Nonce; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When it's a block it has 8 bytes, and 8 zeros, but if it's temporal, I am not against to it |
||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
using FluentAssertions; | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
using Neo.Cryptography; | ||
using System.Text; | ||
|
||
namespace Neo.UnitTests.Cryptography | ||
{ | ||
[TestClass] | ||
public class UT_Murmur128 | ||
{ | ||
[TestMethod] | ||
public void TestGetHashSize() | ||
{ | ||
Murmur128 murmur128 = new Murmur128(1); | ||
murmur128.HashSize.Should().Be(128); | ||
} | ||
|
||
[TestMethod] | ||
public void TestHashCore() | ||
{ | ||
byte[] array = Encoding.ASCII.GetBytes("hello"); | ||
array.Murmur128(123u).ToHexString().ToString().Should().Be("0bc59d0ad25fde2982ed65af61227a0e"); | ||
|
||
array = Encoding.ASCII.GetBytes("world"); | ||
array.Murmur128(123u).ToHexString().ToString().Should().Be("3d3810fed480472bd214a14023bb407f"); | ||
|
||
array = Encoding.ASCII.GetBytes("hello world"); | ||
array.Murmur128(123u).ToHexString().ToString().Should().Be("e0a0632d4f51302c55e3b3e48d28795d"); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any meaning?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nop, we dont really need any meaningful nonce here, it is just a placeholder, so i picked the nonce from the bitcoin genesis block to show our respect to Satoshi Nakamoto.