Ejemplo n.º 1
0
        public override object ParseFromStorage(StorageManager storageManager, StorageLocation storageLocation, IJsonRpcClient rpcClient = null)
        {
            // Obtain our storage value for our given storage location.
            Memory <byte> storageData  = storageManager.ReadStorageSlot(storageLocation.SlotKey, storageLocation.DataOffset, SizeBytes);
            BigInteger    storageValue = BigIntegerConverter.GetBigInteger(storageData.Span, false, SizeBytes);

            // Define our element slot location to iterate over.
            StorageLocation elementLocation = new StorageLocation(storageLocation.SlotKey, 0);

            // If this is a dynamic sized type, our element's storage key will be the defining
            // array's key hashed, and all consecutive element items will have consecutive storage keys.

            // In any case, we'll want to grab our array length, which is either statically defined, or
            // is defined in the storage data obtained earlier from the given location.
            int length = 0;

            if (ArraySize.HasValue)
            {
                length = ArraySize.Value;
            }
            else
            {
                elementLocation.SlotKey = KeccakHash.ComputeHashBytes(elementLocation.SlotKey.Span);
                length = (int)storageValue;
            }

            // Create our resulting object array
            object[] arrayElements = new object[length];

            // Loop for every item.
            for (int i = 0; i < arrayElements.Length; i++)
            {
                // Decode our element at this index
                arrayElements[i] = ElementObject.ParseFromStorage(storageManager, elementLocation, rpcClient);

                // Determine how to iterate, dependent on if the array is dynamically sized or not, as described earlier,
                // (since it could compact multiple elements into a single storage slot).
                if (ElementObject.StorageEntryCount == 1 && storageLocation.DataOffset + ElementObject.SizeBytes <= UInt256.SIZE)
                {
                    // It was compacted with other data (or elligible to), so we advance data offset.
                    elementLocation.DataOffset += ElementObject.SizeBytes;

                    // If our data offset exceeded the size of a slot, we advance the slot.
                    if (elementLocation.DataOffset + ElementObject.SizeBytes > UInt256.SIZE)
                    {
                        elementLocation.SlotKeyInteger++;
                        elementLocation.DataOffset = 0;
                    }
                }
                else
                {
                    // We are not compacting our storage, so we advance for each element individually.
                    elementLocation.SlotKeyInteger += ElementObject.StorageEntryCount;
                    elementLocation.DataOffset      = 0;
                }
            }

            // Return our obtained array elements
            return(arrayElements);
        }
Ejemplo n.º 2
0
        // RLP encoding/decoding trie nodes
        /// <summary>
        /// Given a trie node, encodes it into an RLP item such that if it is 32 bytes or less encoded, it is directly included, otherwise it will be a 32 byte reference to the data.
        /// </summary>
        /// <param name="node">The node to encode into an RLP item for storage in the trie.</param>
        /// <returns>Returns the RLP encoded trie node as it should be represented in our trie efficienctly.</returns>
        private RLPItem EncodeNode(RLPList node)
        {
            // If it's a blank node, we return blank.
            if (node == null || node.Items.Count == 0)
            {
                return(BLANK_NODE);
            }

            // Obtain our node type
            TrieNodeType type = GetNodeType(node);

            byte[] encoded = RLP.Encode(node);

            // If our RLP encoded node is less than 32 bytes, we'll include the node directly as an RLP list.
            if (encoded.Length < 32)
            {
                return(node);
            }

            // Otherwise if the data is 32 bytes or longer, we encode it as an RLP byte array with the hash to the node in the database.

            // Get our hash key
            byte[] hashKey = KeccakHash.ComputeHashBytes(encoded);

            // Set in our database.
            Database.Set(hashKey, encoded);

            // Return as an RLP byte array featuring the lookup hash/key for the encoded node.
            return(hashKey);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Obtains a public key Keccak hash, often used in Ethereum to identify a sender/signer of a transaction.
        /// </summary>
        /// <returns>Returns the Keccak hash of the public key.</returns>
        public byte[] GetPublicKeyHash()
        {
            // Obtain our Q.
            byte[] q = ToPublicKeyArray();

            // Get the hash of the public key without prefix
            return(KeccakHash.ComputeHashBytes(q));
        }
Ejemplo n.º 4
0
        public static byte[] GetUnsignedHash(BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, Address?to, BigInteger value, byte[] data, uint?chainID)
        {
            // Spurious Dragon introduced an update to deter replay attacks where v = CHAIN_ID * 2 + 35 or v = CHAIN_ID * 2 + 36.
            if (chainID != null)
            {
                // Ethereum uses this to deter replay attacks by embedding the network/chain ID in the hashed data.
                return(KeccakHash.ComputeHashBytes(RLP.Encode(Serialize((byte)chainID, 0, 0, nonce, gasPrice, gasLimit, to, value, data)))); // we serialize with our network ID.
            }

            // Pre-Spurious Dragon, we simply hash all main properties except v, r, s.
            return(KeccakHash.ComputeHashBytes(RLP.Encode(Serialize(null, null, null, nonce, gasPrice, gasLimit, to, value, data))));
        }
Ejemplo n.º 5
0
        public void UpdateUnclesHash(BlockHeader[] uncles)
        {
            // We create an RLP list with all of our uncle block headers
            RLPList rlpUncles = new RLPList();

            foreach (BlockHeader uncleHeader in uncles)
            {
                rlpUncles.Items.Add(uncleHeader.Serialize());
            }

            // RLP encode our uncles and hash them
            UnclesHash = KeccakHash.ComputeHashBytes(RLP.Encode(rlpUncles));
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Calculates the hash for the RLP encoded uncle array.
        /// </summary>
        /// <returns>Returns the hash of the RLP encoded uncle array.</returns>
        public byte[] CalculateUnclesHash()
        {
            // We create an RLP list with all of our uncle block headers
            RLPList rlpUncles = new RLPList();

            foreach (BlockHeader uncleHeader in Uncles)
            {
                rlpUncles.Items.Add(uncleHeader.Serialize());
            }

            // RLP encode our uncles and hash them
            return(KeccakHash.ComputeHashBytes(RLP.Encode(rlpUncles)));
        }
Ejemplo n.º 7
0
        public void GenerateSignRecoverVerify(bool useBouncyCastle)
        {
            // Generate ECDSA keypair, compute a hash, sign it, then recover the public key from the signature and verify it matches.
            EthereumEcdsa provider;

            if (useBouncyCastle)
            {
                provider = EthereumEcdsaBouncyCastle.Generate(new SystemRandomAccountDerivation());
            }
            else
            {
                provider = EthereumEcdsaNative.Generate(new SystemRandomAccountDerivation());
            }

            // Sign a hash.
            byte[] hash = KeccakHash.ComputeHashBytes(new byte[] { 11, 22, 33, 44 });
            (byte RecoveryID, BigInteger r, BigInteger s)signature = provider.SignData(hash);

            // Recover the public key for the signature we just made and verify it.
            EthereumEcdsa recovered = null;

            if (useBouncyCastle)
            {
                recovered = EthereumEcdsaBouncyCastle.Recover(hash, signature.RecoveryID, signature.r, signature.s);
            }
            else
            {
                recovered = EthereumEcdsaNative.Recover(hash, signature.RecoveryID, signature.r, signature.s);
            }

            Assert.True(provider.GetPublicKeyHash().ValuesEqual(recovered.GetPublicKeyHash()));

            // Verify the signature we just made.
            Assert.True(recovered.VerifyData(hash, signature.r, signature.s));

            // Generate a new ECDSA keypair (to verify other keypairs can't pass verification for signatures they didn't create).
            EthereumEcdsa provider2;

            if (useBouncyCastle)
            {
                provider2 = EthereumEcdsaBouncyCastle.Generate(new SystemRandomAccountDerivation());
            }
            else
            {
                provider2 = EthereumEcdsaNative.Generate(new SystemRandomAccountDerivation());
            }

            // Verify the prior signature cannot be verified with this new keypair.
            Assert.False(provider2.VerifyData(hash, signature.r, signature.s));
        }
Ejemplo n.º 8
0
        private void AssertHash256(string expectedHash, byte[] data, bool matches = true)
        {
            byte[] hash       = KeccakHash.ComputeHashBytes(data);
            string hashString = hash.ToHexString();

            if (matches)
            {
                Assert.Equal(expectedHash, hashString, true);
            }
            else
            {
                Assert.NotEqual <string>(expectedHash, hashString, StringComparer.InvariantCultureIgnoreCase);
            }
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Generates a bloom filter for a given item with a given byte count.
        /// </summary>
        /// <param name="item">The item to generate a bloom filter for.</param>
        /// <param name="byteCount">The amount of bytes that the item is made up of.</param>
        /// <returns>Returns the bloom filter generated for this item.</returns>
        public static BigInteger Generate(BigInteger item, int byteCount = EVMDefinitions.WORD_SIZE)
        {
            // Compute our hash for this item.
            byte[] hash = KeccakHash.ComputeHashBytes(BigIntegerConverter.GetBytes(item, byteCount));

            // Create our resulting bloom filter
            BigInteger bloomFilter = 0;

            // Out of the hash, for every 16-bit bit chunk we should cycle through, we use the 11-bits of the 16-bit integer as a bit index to set in our filter.
            for (int i = 0; i < BLOOM_CHUNK_COUNT * 2; i += 2)
            {
                int bitIndex = ((hash[i] << 8) | (hash[i + 1])) & 0x7FF; // mask into an 11-bit integer.
                bloomFilter |= ((BigInteger)1 << bitIndex);              // set the bit at that index
            }

            return(bloomFilter);
        }
Ejemplo n.º 10
0
        public void PerfTest()
        {
            Dictionary <byte[], int>        lookup1 = new Dictionary <byte[], int>(new ArrayComparer <byte[]>());
            Dictionary <Memory <byte>, int> lookup2 = new Dictionary <Memory <byte>, int>(new MemoryComparer <byte>());

            int rounds = 50_000;

            byte[][] keys = new byte[rounds][];

            for (var i = 0; i < rounds; i++)
            {
                keys[i] = KeccakHash.ComputeHashBytes(BitConverter.GetBytes(i));
            }

            Stopwatch sw = new Stopwatch();

            sw.Start();
            for (var i = 0; i < rounds; i++)
            {
                lookup1[keys[i]] = i;
            }

            foreach (var key in lookup1.Keys)
            {
                var item = lookup1[key];
            }

            sw.Stop();
            var took1 = sw.Elapsed.TotalMilliseconds;


            sw.Restart();
            for (var i = 0; i < rounds; i++)
            {
                lookup2[keys[i]] = i;
            }

            foreach (var key in lookup2.Keys)
            {
                var item = lookup2[key];
            }

            sw.Stop();
            var took2 = sw.Elapsed.TotalMilliseconds;
        }
Ejemplo n.º 11
0
        public override void Execute()
        {
            // Obtain the memory offset and size
            BigInteger offset = Stack.Pop();
            BigInteger size   = Stack.Pop();

            // Deduct our gas accordingly.
            GasState.Deduct(EVMDefinitions.GetWordCount(size) * GasDefinitions.GAS_SHA3_WORD);

            // Read our data to hash
            byte[] data = Memory.ReadBytes((long)offset, (int)size);

            // Hash our read data.
            byte[] hash = KeccakHash.ComputeHashBytes(data);

            // Push it onto our stack.
            Stack.Push(BigIntegerConverter.GetBigInteger(hash));
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Recalculates the root node hash and sets it in the database accordingly.
        /// </summary>
        private void RehashRootNode()
        {
            // If our root node is null, we set the hash as the blank node hash, and exit.
            if (RootNode == null)
            {
                RootNodeHash = BLANK_NODE_HASH;
                return;
            }

            // Otherwise we get our value, and calculate our key (the hash of the value)
            byte[] value = RLP.Encode(RootNode);
            byte[] key   = KeccakHash.ComputeHashBytes(value);

            // Update our root hash.
            RootNodeHash = key;

            // Set our value in our database.
            Database.Set(key, value);
        }
Ejemplo n.º 13
0
        public void SignAndVerify(bool useBouncyCastle)
        {
            // Generate ECDSA keypair, compute a hash, sign it, then recover the public key from the signature and verify it matches.
            EthereumEcdsa provider;

            if (useBouncyCastle)
            {
                provider = EthereumEcdsaBouncyCastle.Generate(new SystemRandomAccountDerivation());
            }
            else
            {
                provider = EthereumEcdsaNative.Generate(new SystemRandomAccountDerivation());
            }

            byte[] hash = KeccakHash.ComputeHashBytes(new byte[] { 11, 22, 33, 44 });
            (byte RecoveryID, BigInteger r, BigInteger s)signature = provider.SignData(hash);
            EthereumEcdsa recovered = EthereumEcdsa.Recover(hash, signature.RecoveryID, signature.r, signature.s);

            Assert.True(provider.GetPublicKeyHash().ValuesEqual(recovered.GetPublicKeyHash()));
        }
Ejemplo n.º 14
0
        public void ArrayComparerCollisionTest()
        {
            // Define how many rounds we'll test
            const int ROUNDS = 100;

            // Generate a random array to start.
            Random random = new Random();

            byte[] arrayA = new byte[EVMDefinitions.WORD_SIZE];
            random.NextBytes(arrayA);

            // Create a lookup, and make all our entries to test. Also create a duplicate array (different instance, same underlying data) to verify everything matches.
            Dictionary <Memory <byte>, string> lookup = new Dictionary <Memory <byte>, string>(new MemoryComparer <byte>());

            for (int i = 0; i < ROUNDS; i++)
            {
                // Generate a new array of data that is almost guarenteed to be unique from previous entries.
                arrayA = KeccakHash.ComputeHashBytes(arrayA);

                // Create a copy of the array.
                byte[] arrayB = arrayA.Slice(0);

                // Set our data in lookup. The second line should overwrite the value of the first.
                lookup[arrayA] = "0";
                lookup[arrayB] = "1";
            }

            // Assert every key we used set as 1 (generated same hashcodes for same data)
            foreach (var key in lookup.Keys)
            {
                Assert.Equal("1", lookup[key]);
            }

            // Verify we have the correct amount of entries (generated different hashcodes for different data).
            Assert.Equal(ROUNDS, lookup.Count);
        }
Ejemplo n.º 15
0
        public override object ParseFromStorage(StorageManager storageManager, StorageLocation storageLocation)
        {
            // Obtain our storage value for our given storage location.
            Memory <byte> storageData  = storageManager.ReadStorageSlot(storageLocation.SlotKey, storageLocation.DataOffset, SizeBytes);
            BigInteger    storageValue = BigIntegerConverter.GetBigInteger(storageData.Span, false, SizeBytes);

            // The lowest bit of our value signifies if it was stored in multiple slots, or if it fit in a single slot.
            bool requiresMultipleSlots = (storageValue & 1) != 0;

            if (requiresMultipleSlots)
            {
                // The length is shifted one bit to the left as a result of our flag encoded at bit 0.
                // So we shift to obtain length.
                int length = (int)(storageValue >> 1);

                // Calculate our slot count.
                int slotCount = (int)Math.Ceiling((double)length / UInt256.SIZE);

                // Define our result
                Memory <byte> result = new byte[length];

                // Calculate the slot key for our array data (dynamic array's data slot keys are
                // the array's slot key hashed, with subsequent slot keys being + 1 to the previous)
                Memory <byte> arrayDataSlotKey = KeccakHash.ComputeHashBytes(storageLocation.SlotKey.Span);

                // Define our slot location to iterate over.
                StorageLocation slotLocation = new StorageLocation(arrayDataSlotKey, 0);

                // Loop for every byte we wish to copy.
                for (int i = 0; i < length;)
                {
                    // Obtain the slot
                    Memory <byte> arrayDataSlotValue = storageManager.ReadStorageSlot(slotLocation.SlotKey);

                    // Calculate the remainder of our bytes
                    int remainder = length - i;

                    // Determine the remainder in this slot.
                    int remainderInSlot = Math.Min(remainder, UInt256.SIZE);

                    // Copy our data into our result.
                    arrayDataSlotValue.Slice(0, remainderInSlot).CopyTo(result.Slice(i));

                    // Increment our slot key
                    slotLocation.SlotKeyInteger++;

                    // Increment our byte index.
                    i += remainderInSlot;
                }

                // Return our result
                return(result);
            }
            else
            {
                // We did not require multiple storage slots, so it is embedded in this slot.
                // But the count for data size remains at the end of this storage slot, so we
                // first obtain the data size from that byte.
                int length = ((int)(storageValue & 0xFF)) >> 1;

                // Slice off the desired data from our storage slot data and return it.
                return(storageData.Slice(0, length));
            }
        }
Ejemplo n.º 16
0
 public byte[] GetHash()
 {
     return(KeccakHash.ComputeHashBytes(RLP.Encode(Serialize())));
 }
Ejemplo n.º 17
0
 public byte[] GetMiningHash()
 {
     return(KeccakHash.ComputeHashBytes(RLP.Encode(Serialize(ExtraData, null, null))));
 }
Ejemplo n.º 18
0
 public byte[] GetSigningHash()
 {
     return(KeccakHash.ComputeHashBytes(RLP.Encode(Serialize(null, MixHash, Nonce))));
 }
Ejemplo n.º 19
0
        public void TestKeccakUpdate()
        {
            // Create our random provider.
            Random random = new Random();

            // Loop for a few test rounds.
            for (int i = 0; i < 5; i++)
            {
                // Generate random data
                byte[]      bufferArray = new byte[random.Next(3, 1024 * 20)];
                Span <byte> buffer      = bufferArray;
                random.NextBytes(buffer);

                // Split threshold
                int splitThreshold = random.Next(33, 1024);

                // Compute the overall hash on the data
                byte[] singleStepHash = KeccakHash.ComputeHashBytes(buffer);

                // Create our keccak hash provider for multi step hash calculation.
                KeccakHash keccak = KeccakHash.Create();
                keccak.Update(bufferArray, 0, buffer.Length);

                // Assert the hashes are equal
                Assert.Equal(singleStepHash.ToHexString(), keccak.Hash.ToHexString());

                // Recompute on the same keccak instance to check if it's reusable.
                keccak.Reset();
                keccak.Update(bufferArray, 0, buffer.Length);
                Assert.Equal(singleStepHash.ToHexString(), keccak.Hash.ToHexString());

                // Recompute the hash but add empty array hashing.
                keccak.Reset();
                keccak.Update(bufferArray, 0, bufferArray.Length);
                keccak.Update(Array.Empty <byte>(), 0, 0);
                keccak.Update(Array.Empty <byte>(), 0, 0);

                // Assert the hashes are equal
                Assert.Equal(singleStepHash.ToHexString(), keccak.Hash.ToHexString());

                // Assert blank hashes
                keccak.Reset();
                keccak.Update(Array.Empty <byte>(), 0, 0);
                keccak.Update(Array.Empty <byte>(), 0, 0);
                byte[] blankHash = KeccakHash.ComputeHashBytes(Array.Empty <byte>());
                Assert.Equal(blankHash.ToHexString(), keccak.Hash.ToHexString());

                // Refresh our new keccak instance
                keccak.Reset();

                while (buffer.Length > 0)
                {
                    // Check if this is the last round
                    bool lastRound = buffer.Length <= splitThreshold;

                    // Obtain the data for this round.
                    byte[] roundData = buffer.Slice(0, lastRound ? buffer.Length : splitThreshold).ToArray();

                    if (lastRound)
                    {
                        // Obtain the final multistep hash.
                        keccak.Update(roundData, 0, roundData.Length);

                        // Assert the hashes are equal
                        Assert.Equal(singleStepHash.ToHexString(), keccak.Hash.ToHexString());

                        // Reset
                        keccak.Reset();
                        break;
                    }
                    else
                    {
                        keccak.Update(roundData, 0, roundData.Length);
                    }

                    // Advance our pointer.
                    buffer = buffer.Slice(roundData.Length);
                }
            }
        }
Ejemplo n.º 20
0
 /// <summary>
 /// Given a key, obtains the corresponding value from our trie.
 /// </summary>
 /// <param name="key">The key to grab the corresponding value for.</param>
 /// <returns>Returns the value which corresponds to the provided key.</returns>
 public override byte[] Get(byte[] key)
 {
     // Use the hash of our provided key as a key.
     return(base.Get(KeccakHash.ComputeHashBytes(key)));
 }
Ejemplo n.º 21
0
        public void DeriveEncryptionParameters()
        {
            // Verify the session state is correct.
            if (SessionState != RLPxSessionState.AcknowledgementCompleted)
            {
                throw new Exception("RLPx encryption parameter deriviation should only occur after authentication and acknowledgement was processed.");
            }

            // Verify we have all required information
            if (AuthData == null || AuthAckData == null || RemoteEphermalPublicKey == null || InitiatorNonce == null || ResponderNonce == null)
            {
                throw new Exception("RLPx deriving encryption information failed: Insufficient data collected from handshake.");
            }

            // Generate the ecdh key with both ephemeral keys
            byte[] ecdhEphemeralKey = LocalEphemeralPrivateKey.ComputeECDHKey(RemoteEphermalPublicKey);

            // Generate a shared secret: Keccak256(ecdhEphemeralKey || Keccak256(ResponderNonce || InitiatorNonce))
            byte[] combinedNonceHash = KeccakHash.ComputeHashBytes(ResponderNonce.Concat(InitiatorNonce));
            byte[] sharedSecret      = KeccakHash.ComputeHashBytes(ecdhEphemeralKey.Concat(combinedNonceHash));

            // Derive the token as a hash of the shared secret.
            Token = KeccakHash.ComputeHashBytes(sharedSecret);

            // Derive AES secret: Keccak256(ecdhEphemeralKey || sharedSecret)
            AesSecret = KeccakHash.ComputeHashBytes(ecdhEphemeralKey.Concat(sharedSecret));

            // Derive Mac secret: Keccak256(ecdhEphemeralKey || AesSecret)
            MacSecret = KeccakHash.ComputeHashBytes(ecdhEphemeralKey.Concat(AesSecret));

            // Create our AES providers for incoming and outgoing traffic/frames.
            // Counter is 0, so it doesn't need to be provided, default value will handle this.
            IngressAes = new AesCtr(AesSecret);
            EgressAes  = new AesCtr(AesSecret);

            // Next we'll want to derive our incoming (ingress) and outgoing (egress) traffic message authentication code ("MAC")
            // The initial state is based off of keccak((MacSecret ^ nonce) || auth/auth-ack). Later states update data from packet frames.

            // We begin by calculating the xor'd nonces
            byte[] initiatorTranformedNonce  = (byte[])InitiatorNonce.Clone();
            byte[] responderTransformedNonce = (byte[])ResponderNonce.Clone();
            int    loopSize = Math.Min(initiatorTranformedNonce.Length, MacSecret.Length);

            for (int i = 0; i < loopSize; i++)
            {
                initiatorTranformedNonce[i]  ^= MacSecret[i];
                responderTransformedNonce[i] ^= MacSecret[i];
            }

            // Next we'll want to hash the data with our hash providers.
            KeccakHash initiatorOutgoing = KeccakHash.Create();

            initiatorOutgoing.Update(responderTransformedNonce, 0, responderTransformedNonce.Length);
            initiatorOutgoing.Update(AuthData, 0, AuthData.Length);
            KeccakHash responderOutgoing = KeccakHash.Create();

            responderOutgoing.Update(initiatorTranformedNonce, 0, initiatorTranformedNonce.Length);
            responderOutgoing.Update(AuthAckData, 0, AuthAckData.Length);

            // Assign the correct hash providers based off of role
            if (Role == RLPxSessionRole.Initiator)
            {
                EgressMac  = initiatorOutgoing;
                IngressMac = responderOutgoing;
            }
            else
            {
                EgressMac  = responderOutgoing;
                IngressMac = initiatorOutgoing;
            }
        }