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); }
// 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); }
/// <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)); }
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)))); }
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)); }
/// <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))); }
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)); }
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); } }
/// <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); }
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; }
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)); }
/// <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); }
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())); }
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); }
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)); } }
public byte[] GetHash() { return(KeccakHash.ComputeHashBytes(RLP.Encode(Serialize()))); }
public byte[] GetMiningHash() { return(KeccakHash.ComputeHashBytes(RLP.Encode(Serialize(ExtraData, null, null)))); }
public byte[] GetSigningHash() { return(KeccakHash.ComputeHashBytes(RLP.Encode(Serialize(null, MixHash, Nonce)))); }
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); } } }
/// <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))); }
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; } }