Пример #1
0
        /// <summary>
        /// Serializes the block header into an RLP item for encoding.
        /// </summary>
        /// <returns>Returns a serialized RLP block header.</returns>
        private RLPItem Serialize(byte[] extraData, byte[] mixHash, byte[] nonce)
        {
            // We create a new RLP list that constitute this header.
            RLPList rlpBlockHeader = new RLPList();

            rlpBlockHeader.Items.Add(PreviousHash);
            rlpBlockHeader.Items.Add(UnclesHash);
            rlpBlockHeader.Items.Add(RLP.FromInteger(Coinbase, Address.ADDRESS_SIZE));
            rlpBlockHeader.Items.Add(StateRootHash);
            rlpBlockHeader.Items.Add(TransactionsRootHash);
            rlpBlockHeader.Items.Add(ReceiptsRootHash);
            rlpBlockHeader.Items.Add(RLP.FromInteger(Bloom, EVMDefinitions.BLOOM_FILTER_SIZE));
            rlpBlockHeader.Items.Add(RLP.FromInteger(Difficulty, EVMDefinitions.WORD_SIZE, true));
            rlpBlockHeader.Items.Add(RLP.FromInteger(BlockNumber, EVMDefinitions.WORD_SIZE, true));
            rlpBlockHeader.Items.Add(RLP.FromInteger(GasLimit, EVMDefinitions.WORD_SIZE, true));
            rlpBlockHeader.Items.Add(RLP.FromInteger(GasUsed, EVMDefinitions.WORD_SIZE, true));
            rlpBlockHeader.Items.Add(RLP.FromInteger(Timestamp, EVMDefinitions.WORD_SIZE, true));
            if (extraData != null)
            {
                rlpBlockHeader.Items.Add(extraData);
            }

            if (mixHash != null)
            {
                rlpBlockHeader.Items.Add(mixHash);
            }

            if (nonce != null)
            {
                rlpBlockHeader.Items.Add(nonce);
            }

            // Return our rlp header item.
            return(rlpBlockHeader);
        }
Пример #2
0
        /// <summary>
        /// Serializes the receipt into an RLP item for encoding.
        /// </summary>
        /// <returns>Returns a serialized RLP receipt.</returns>
        public RLPItem Serialize()
        {
            // We create a new RLP list that constitute this receipt.
            RLPList rlpReceipt = new RLPList();

            // Add our state root, gas used
            rlpReceipt.Items.Add(StateRoot);
            rlpReceipt.Items.Add(RLP.FromInteger(GasUsed, EVMDefinitions.WORD_SIZE, true));

            // Generate a new bloom filter, and add that.
            GenerateBloomFilter();
            rlpReceipt.Items.Add(RLP.FromInteger(Bloom, EVMDefinitions.BLOOM_FILTER_SIZE));

            // Add our RLP encoded logs.
            RLPList rlpLogs = new RLPList();

            foreach (Log log in Logs)
            {
                rlpLogs.Items.Add(log.Serialize());
            }

            rlpReceipt.Items.Add(rlpLogs);

            // Return our rlp receipt item.
            return(rlpReceipt);
        }
Пример #3
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);
        }
Пример #4
0
        /// <summary>
        /// Serializes the block into an RLP item for encoding.
        /// </summary>
        /// <returns>Returns a serialized RLP block.</returns>
        public RLPItem Serialize()
        {
            // We create a new RLP list that constitute this header.
            RLPList rlpBlock = new RLPList();

            // Add our header
            rlpBlock.Items.Add(Header.Serialize());

            // Add all of our transactions
            RLPList rlpTransactions = new RLPList();

            foreach (Transaction transaction in Transactions)
            {
                rlpTransactions.Items.Add(transaction.Serialize());
            }

            rlpBlock.Items.Add(rlpTransactions);

            // Add all of our uncle block headers
            RLPList rlpUncles = new RLPList();

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

            rlpBlock.Items.Add(rlpUncles);

            // Return our rlp header item.
            return(rlpBlock);
        }
Пример #5
0
        // Node types/prefixes/unpacking
        /// <summary>
        /// Given a direct trie node, obtains the type of node.
        /// </summary>
        /// <param name="node">The node to obtain the type of.</param>
        /// <returns>Returns the type of node for the provided node.</returns>
        private TrieNodeType GetNodeType(RLPList node)
        {
            // If this node is null or has no items, we say it's a blank type.
            if (node == null || node.Items.Count == 0)
            {
                return(TrieNodeType.Blank);
            }

            // If it has two items then it is either a leaf or an extension node. We can determine this based off of the use of a terminator.
            if (node.Items.Count == 2)
            {
                // Check our first nibble to obtain prefix, then return our type accordingly. This is faster than unpacking the whole nibble set.
                TrieNodePrefix prefix = (TrieNodePrefix)GetNibble(node.Items[0], 0);
                if (prefix == TrieNodePrefix.ExtensionNodeEven || prefix == TrieNodePrefix.ExtensionNodeOdd)
                {
                    return(TrieNodeType.Extension);
                }
                else
                {
                    return(TrieNodeType.Leaf);
                }
            }

            // Otherwise if it has 17 items, it's a branch node.
            else if (node.Items.Count == 17)
            {
                return(TrieNodeType.Branch);
            }

            // Otherwise this node is not formatted properly.
            throw new ArgumentException("Failed to interpret node type from node prefix in trie.");
        }
Пример #6
0
        public static Address MakeContractAddress(Address sender, BigInteger nonce)
        {
            // Create an RLP list with the address and nonce
            RLPList list = new RLPList(RLP.FromInteger(sender, ADDRESS_SIZE), RLP.FromInteger(nonce));
            var     hash = KeccakHash.ComputeHash(RLP.Encode(list)).Slice(EVMDefinitions.WORD_SIZE - ADDRESS_SIZE);

            return(new Address(hash));
        }
Пример #7
0
        /// <summary>
        /// Given an trie node, traverses down all paths to obtain all key-value pairs in the trie.
        /// </summary>
        /// <param name="node">The trie node to recursively enumerate key-value pairs from.</param>
        /// <param name="currentNibbles">The current key nibbles that have been traversed up to this point.</param>
        /// <param name="result">The resulting dictionary created by obtaining all pairs from this node recursively downward.</param>
        private void NodeGetAllPairs(RLPList node, byte[] currentNibbles, Dictionary <Memory <byte>, byte[]> result)
        {
            // Obtain our node type
            TrieNodeType nodeType = GetNodeType(node);

            // Switch on node type
            switch (nodeType)
            {
            // If it's blank, return the blank node
            case TrieNodeType.Blank:
                return;

            case TrieNodeType.Branch:
                // Check we have data set
                Memory <byte> valueData = ((RLPByteArray)node.Items[16]).Data;
                if (valueData.Length > 0)
                {
                    // Set our key-value pair at this node in our results
                    result[NibbleArrayToByteArray(currentNibbles)] = valueData.ToArray();
                }

                // Loop for every branch in this branch node (every sub item is a branch except for the last one,
                // which is the value if no more nibbles are needed to index it).
                for (byte branchIndex = 0; branchIndex < node.Items.Count - 1; branchIndex++)
                {
                    // Obtain our node at the given index and obtain that item
                    RLPList branchNode = DecodeNode(node.Items[branchIndex]);

                    // Keep running down the branch recursively.
                    NodeGetAllPairs(branchNode, currentNibbles.Concat(new byte[] { branchIndex }), result);
                }

                return;

            case TrieNodeType.Extension:
                // If it's an extension, we'll unpack the whole nibble set of our first item (prefix + key remainder)
                byte[] sharedNibbles = UnpackPrefixedNode(node.Items[0]).nibbles;

                // The second item in this node will represent the "next node", so we recursively obtain from that, advancing our key position as well.
                RLPList nextNode = DecodeNode(node.Items[1]);
                NodeGetAllPairs(nextNode, currentNibbles.Concat(sharedNibbles), result);
                return;

            case TrieNodeType.Leaf:
                // If it's a leaf, we'll unpack the whole nibble set of our first item (prefix + key remainder)
                sharedNibbles = UnpackPrefixedNode(node.Items[0]).nibbles;

                // Otherwise we passed verification, so we return the "value" that is stored in this leaf.
                result[NibbleArrayToByteArray(currentNibbles.Concat(sharedNibbles))] = ((RLPByteArray)node.Items[1]).Data.ToArray();
                return;
            }

            // Throw an exception if we somehow end up here.
            throw new ArgumentException("Could not obtain key-value pairs, invalid node type detected!");
        }
Пример #8
0
        /// <summary>
        /// Given a node, duplicates it such that it is structurally the same, yet is a different object instance.
        /// </summary>
        /// <param name="node">The node to duplicate.</param>
        /// <returns>Returns a duplicate of the provided node.</returns>
        private RLPList NodeDuplicate(RLPList node)
        {
            // If the node is null, we return null.
            if (node == null)
            {
                return(null);
            }

            // Otherwise we serialize and deserialize it to clone it.
            return((RLPList)RLP.Decode(RLP.Encode(node)));
        }
Пример #9
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));
        }
Пример #10
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)));
        }
Пример #11
0
        /// <summary>
        /// Deserializes the given RLP serialized receipt and sets all values accordingly.
        /// </summary>
        /// <param name="item">The RLP item to deserialize and obtain values from.</param>
        public void Deserialize(RLPItem item)
        {
            // Verify this is a list
            if (!item.IsList)
            {
                throw new ArgumentException();
            }

            // Verify it has 4 items.
            RLPList rlpReceipt = (RLPList)item;

            if (rlpReceipt.Items.Count != 4)
            {
                throw new ArgumentException();
            }

            // Verify the types of all items
            if (!rlpReceipt.Items[0].IsByteArray ||
                !rlpReceipt.Items[1].IsByteArray ||
                !rlpReceipt.Items[2].IsByteArray ||
                !rlpReceipt.Items[3].IsList)
            {
                throw new ArgumentException();
            }

            // Set our state root
            RLPByteArray rlpStateRoot = (RLPByteArray)rlpReceipt.Items[0];

            StateRoot = rlpStateRoot.Data.ToArray();

            // Set our gas used
            RLPByteArray rlpGasUsed = (RLPByteArray)rlpReceipt.Items[1];

            GasUsed = RLP.ToInteger(rlpGasUsed, EVMDefinitions.WORD_SIZE);

            // Set our bloom
            RLPByteArray rlpBloom = (RLPByteArray)rlpReceipt.Items[2];

            Bloom = RLP.ToInteger(rlpBloom, EVMDefinitions.BLOOM_FILTER_SIZE);

            // Obtain our logs
            RLPList rlpLogs = (RLPList)rlpReceipt.Items[3];

            Logs = new List <Log>();
            foreach (RLPItem rlpLog in rlpLogs.Items)
            {
                // Add our log
                Logs.Add(new Log(rlpLog));
            }
        }
Пример #12
0
        public override void Deserialize(byte[] data)
        {
            // Decode our RLP item from data.
            RLPList rlpList = (RLPList)RLP.Decode(data);

            // Verify the sizes of all components.
            if (!rlpList.Items[0].IsByteArray)
            {
                throw new ArgumentException("RLPx EIP8 auth packet's first item (signature) was not a byte array.");
            }
            else if (((byte[])rlpList.Items[0]).Length != EthereumEcdsa.SIGNATURE_RSV_SIZE)
            {
                throw new ArgumentException("RLPx EIP8 auth packet's first item (signature) was the incorrect size.");
            }
            else if (!rlpList.Items[1].IsByteArray)
            {
                throw new ArgumentException("RLPx EIP8 auth packet's second item (public key) was not a byte array.");
            }
            else if (((byte[])rlpList.Items[1]).Length != EthereumEcdsa.PUBLIC_KEY_SIZE)
            {
                throw new ArgumentException("RLPx EIP8 auth packet's second item (public key) was the incorrect size.");
            }
            else if (!rlpList.Items[2].IsByteArray)
            {
                throw new ArgumentException("RLPx EIP8 auth packet's third item (nonce) was not a byte array.");
            }
            else if (((byte[])rlpList.Items[2]).Length != RLPxSession.NONCE_SIZE)
            {
                throw new ArgumentException("RLPx EIP8 auth packet's third item (nonce) was the incorrect size.");
            }
            else if (rlpList.Items.Count >= 4 && !rlpList.Items[3].IsByteArray)
            {
                throw new ArgumentException("RLPx EIP8 auth packet's fourth item (version) was not a byte array.");
            }

            // Obtain all components.
            Memory <byte> signature = rlpList.Items[0];

            R         = signature.Slice(0, 32).ToArray();
            S         = signature.Slice(32, 32).ToArray();
            V         = signature.Span[64];
            PublicKey = rlpList.Items[1];
            Nonce     = rlpList.Items[2];

            // Decode version if it's available.
            if (rlpList.Items.Count >= 4)
            {
                Version = RLP.ToInteger((RLPByteArray)rlpList.Items[3], 32, false);
            }
        }
Пример #13
0
        /// <summary>
        /// Serializes the account into an RLP item for encoding.
        /// </summary>
        /// <returns>Returns a serialized RLP account.</returns>
        public RLPItem Serialize()
        {
            // We create a new RLP list that constitute this account.
            RLPList rlpAccount = new RLPList();

            // Add our nonce, balance, storage and code hash.
            rlpAccount.Items.Add(RLP.FromInteger(Nonce, EVMDefinitions.WORD_SIZE, true));
            rlpAccount.Items.Add(RLP.FromInteger(Balance, EVMDefinitions.WORD_SIZE, true));
            rlpAccount.Items.Add(StorageRoot);
            rlpAccount.Items.Add(CodeHash);

            // Return our rlp log item.
            return(rlpAccount);
        }
Пример #14
0
        /// <summary>
        /// Deserializes the given RLP serialized log and sets all values accordingly.
        /// </summary>
        /// <param name="item">The RLP item to deserialize and obtain values from.</param>
        public void Deserialize(RLPItem item)
        {
            // Verify this is a list
            if (!item.IsList)
            {
                throw new ArgumentException();
            }

            // Verify it has 3 items.
            RLPList rlpLog = (RLPList)item;

            if (rlpLog.Items.Count != 3)
            {
                throw new ArgumentException();
            }

            // Verify the types of all items
            if (!rlpLog.Items[0].IsByteArray ||
                !rlpLog.Items[1].IsList ||
                !rlpLog.Items[2].IsByteArray)
            {
                throw new ArgumentException();
            }

            // Set our address
            RLPByteArray rlpAddress = (RLPByteArray)rlpLog.Items[0];

            Address = new Address(rlpAddress.Data.Span);

            // Obtain our topics
            RLPList rlpTopicsList = (RLPList)rlpLog.Items[1];

            Topics = new List <BigInteger>();
            foreach (RLPItem rlpTopic in rlpTopicsList.Items)
            {
                // Verify all of our items are data
                if (rlpTopic.GetType() != typeof(RLPByteArray))
                {
                    throw new ArgumentException();
                }

                // Add our topic.
                Topics.Add(RLP.ToInteger((RLPByteArray)rlpTopic, EVMDefinitions.WORD_SIZE));
            }

            // Obtain our data
            RLPByteArray rlpData = (RLPByteArray)rlpLog.Items[2];

            Data = rlpData.Data.ToArray();
        }
Пример #15
0
        public override byte[] Serialize()
        {
            // Verify our underlying properties
            VerifyProperties();

            // Create an RLP item to contain all of our data
            RLPList rlpList = new RLPList();

            rlpList.Items.Add(EphemeralPublicKey);
            rlpList.Items.Add(Nonce);
            rlpList.Items.Add(RLP.FromInteger(Version, 32, true));

            // Serialize our RLP data
            return(RLP.Encode(rlpList));
        }
Пример #16
0
        // Underlying trie implementation / helpers
        /// <summary>
        /// Checks if two given nodes are equal in structure.
        /// </summary>
        /// <param name="first">The first node to check structural equality of.</param>
        /// <param name="second">The second node to check structural equality of.</param>
        /// <returns>Returns a boolean indicating if the provided nodes are equal in structure and underlying values.</returns>
        private bool NodeEquals(RLPList first, RLPList second)
        {
            // If both are null, they're equal. If only one is, they aren't.
            if (first == null && second == null)
            {
                return(true);
            }
            else if (first == null || second == null)
            {
                return(false);
            }

            // Otherwise we treat it as a real node, encode, and compare.
            byte[] encodedFirst  = RLP.Encode(first);
            byte[] encodedSecond = RLP.Encode(second);
            return(encodedFirst.ValuesEqual(encodedSecond));
        }
Пример #17
0
        /// <summary>
        /// Deserializes the given RLP serialized block and sets all values accordingly.
        /// </summary>
        /// <param name="item">The RLP item to deserialize and obtain values from.</param>
        public void Deserialize(RLPItem item)
        {
            // Verify this is a list
            if (!item.IsList)
            {
                throw new ArgumentException();
            }

            // Verify it has 3 items.
            RLPList rlpBlock = (RLPList)item;

            if (rlpBlock.Items.Count != 3)
            {
                throw new ArgumentException();
            }

            // Verify the types of all items (should all be lists)
            for (int i = 0; i < rlpBlock.Items.Count; i++)
            {
                if (!rlpBlock.Items[i].IsList)
                {
                    throw new ArgumentException();
                }
            }

            // Obtain the block header
            Header = new BlockHeader(rlpBlock.Items[0]);

            // Obtain the list of transactions
            RLPList rlpTransactions = (RLPList)rlpBlock.Items[1];

            Transactions = new Transaction[rlpTransactions.Items.Count];
            for (int i = 0; i < rlpTransactions.Items.Count; i++)
            {
                Transactions[i] = new Transaction(rlpTransactions.Items[i]);
            }

            // Obtain the list of uncles.
            RLPList rlpUncles = (RLPList)rlpBlock.Items[2];

            Uncles = new BlockHeader[rlpUncles.Items.Count];
            for (int i = 0; i < rlpUncles.Items.Count; i++)
            {
                Uncles[i] = new BlockHeader(rlpUncles.Items[i]);
            }
        }
Пример #18
0
        /// <summary>
        /// Deserializes the given RLP serialized account and sets all values accordingly.
        /// </summary>
        /// <param name="item">The RLP item to deserialize and obtain values from.</param>
        public void Deserialize(RLPItem item)
        {
            // Verify this is a list
            if (!item.IsList)
            {
                throw new ArgumentException();
            }

            // Verify it has 4 items.
            RLPList rlpAccount = (RLPList)item;

            if (rlpAccount.Items.Count != 4)
            {
                throw new ArgumentException();
            }

            // Verify the types of all items
            if (!rlpAccount.Items[0].IsByteArray ||
                !rlpAccount.Items[1].IsByteArray ||
                !rlpAccount.Items[2].IsByteArray ||
                !rlpAccount.Items[3].IsByteArray)
            {
                throw new ArgumentException();
            }

            // Set our nonce, balance, storage, and code hash.
            Nonce       = RLP.ToInteger((RLPByteArray)rlpAccount.Items[0]);
            Balance     = RLP.ToInteger((RLPByteArray)rlpAccount.Items[1]);
            StorageRoot = rlpAccount.Items[2];
            CodeHash    = rlpAccount.Items[3];

            // Verify the length of our storage root and code hash.
            if (StorageRoot.Length != KeccakHash.HASH_SIZE || CodeHash.Length != KeccakHash.HASH_SIZE)
            {
                throw new ArgumentException();
            }

            // Initialize our storage change cache
            StorageCache = new Dictionary <Memory <byte>, byte[]>(new MemoryComparer <byte>());

            // Load our trie given our storage root
            StorageTrie = new Trie(Configuration.Database, StorageRoot);
        }
Пример #19
0
        /// <summary>
        /// Serializes the transaction into an RLP item for encoding.
        /// </summary>
        /// <returns>Returns a serialized RLP transaction.</returns>
        public static RLPItem Serialize(byte?v, BigInteger?r, BigInteger?s, BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, Address?to, BigInteger value, byte[] data)
        {
            // We create a new RLP list that constitute this transaction.
            RLPList rlpTransaction = new RLPList();

            // Add our values
            rlpTransaction.Items.Add(RLP.FromInteger(nonce, 32, true));
            rlpTransaction.Items.Add(RLP.FromInteger(gasPrice, 32, true));
            rlpTransaction.Items.Add(RLP.FromInteger(gasLimit, 32, true));

            if (to != null)
            {
                rlpTransaction.Items.Add(new RLPByteArray(to.Value.GetBytes()));
            }
            else
            {
                rlpTransaction.Items.Add(new RLPByteArray(Array.Empty <byte>()));
            }

            rlpTransaction.Items.Add(RLP.FromInteger(value, 32, true));
            rlpTransaction.Items.Add(data);
            if (v != null)
            {
                rlpTransaction.Items.Add(v);
            }

            if (r != null)
            {
                rlpTransaction.Items.Add(RLP.FromInteger(r.Value, 32, true));
            }

            if (s != null)
            {
                rlpTransaction.Items.Add(RLP.FromInteger(s.Value, 32, true));
            }

            // Return our rlp log item.
            return(rlpTransaction);
        }
Пример #20
0
        public override void Deserialize(byte[] data)
        {
            // Decode our RLP item from data.
            RLPList rlpList = (RLPList)RLP.Decode(data);

            // Verify the sizes of all components.
            if (!rlpList.Items[0].IsByteArray)
            {
                throw new ArgumentException("RLPx EIP8 auth-ack packet's first item (ephemeral public key) was not a byte array.");
            }
            else if (((byte[])rlpList.Items[0]).Length != EthereumEcdsa.PUBLIC_KEY_SIZE)
            {
                throw new ArgumentException("RLPx EIP8 auth-ack packet's first item (ephemeral public key) was not the correct size.");
            }
            else if (!rlpList.Items[1].IsByteArray)
            {
                throw new ArgumentException("RLPx EIP8 auth-ack packet's second item (nonce) was not a byte array.");
            }
            else if (((byte[])rlpList.Items[1]).Length != RLPxSession.NONCE_SIZE)
            {
                throw new ArgumentException("RLPx EIP8 auth-ack packet's second item (nonce) was not the correct size.");
            }
            else if (rlpList.Items.Count >= 3 && !rlpList.Items[2].IsByteArray)
            {
                throw new ArgumentException("RLPx EIP8 auth-ack packet's third item (version) was not a byte array.");
            }

            // Obtain all components.
            EphemeralPublicKey = rlpList.Items[0];
            Nonce = rlpList.Items[1];

            // Decode version if it's available.
            if (rlpList.Items.Count >= 3)
            {
                Version = RLP.ToInteger((RLPByteArray)rlpList.Items[2], 32, false);
            }
        }
Пример #21
0
        private void RLPListEncodeDecodeTest(int items)
        {
            RLPList list = new RLPList();

            for (int i = 0; i < items; i++)
            {
                if (i % 2 == 0)
                {
                    list.Items.Add(new byte[] { (byte)(i % 256) });
                }
                else
                {
                    list.Items.Add(new RLPList());
                }
            }

            byte[]  encoded = RLP.Encode(list);
            RLPList list2   = (RLPList)RLP.Decode(encoded);

            Assert.Equal(list.Items.Count, list2.Items.Count);

            for (int i = 0; i < list.Items.Count; i++)
            {
                if (i % 2 == 0)
                {
                    Assert.IsType <RLPByteArray>(list2.Items[i]);
                    RLPByteArray b1 = ((RLPByteArray)list.Items[i]);
                    RLPByteArray b2 = ((RLPByteArray)list2.Items[i]);
                    Assert.True(MemoryExtensions.SequenceEqual(b1.Data.Span, b2.Data.Span));
                }
                else
                {
                    Assert.IsType <RLPList>(list2.Items[i]);
                }
            }
        }
Пример #22
0
        /// <summary>
        /// Serializes the log into an RLP item for encoding.
        /// </summary>
        /// <returns>Returns a serialized RLP log.</returns>
        public RLPItem Serialize()
        {
            // We create a new RLP list that constitute this log.
            RLPList rlpLog = new RLPList();

            // Add our address
            rlpLog.Items.Add(RLP.FromInteger(Address, Address.ADDRESS_SIZE));

            // Add our topics, a list of 32-bit integers.
            RLPList rlpTopicsList = new RLPList();

            foreach (BigInteger topic in Topics)
            {
                rlpTopicsList.Items.Add(RLP.FromInteger(topic, EVMDefinitions.WORD_SIZE));
            }

            rlpLog.Items.Add(rlpTopicsList);

            // Add our data
            rlpLog.Items.Add(Data);

            // Return our rlp log item.
            return(rlpLog);
        }
Пример #23
0
        /// <summary>
        /// Given a trie node, traverses down the appropriate node path, updating the value for a given key and the current nibble index for our key in the indexing process.
        /// </summary>
        /// <param name="node">The current trie node to traverse down to update our value, given our key and current nibble index.</param>
        /// <param name="key">The key for the value which we wish to update.</param>
        /// <param name="currentNibbleIndex">The index of the current nibble in our key which we are at in our indexing process.</param>
        /// <param name="value">The value which we wish to set for the provided key in our trie.</param>
        /// <returns>Returns a node with the given update which is to be used as the replacement for the provided node.</returns>
        private RLPList NodeUpdateValue(RLPList node, Memory <byte> key, int currentNibbleIndex, byte[] value)
        {
            // Obtain the node type
            TrieNodeType nodeType = GetNodeType(node);

            // Switch on our node type
            switch (nodeType)
            {
            case TrieNodeType.Blank:
                // Obtain the remainder of our key as a nibble sequence.
                byte[] keyNibbles = ByteArrayToNibbleArray(key, currentNibbleIndex);
                // Since this node is blank, we turn it into a leaf with the key remainder in it.
                return(new RLPList(PackPrefixedNode(TrieNodeType.Leaf, keyNibbles), value));

            case TrieNodeType.Branch:
                // If we reached the end of key, this node is the destination to set the value in.
                if (currentNibbleIndex == key.Length * 2)
                {
                    // The key ended, so we set the value here.
                    node.Items[16] = value;
                    return(node);
                }
                else
                {
                    // Obtain our branch index and node
                    int     branchIndex = GetNibble(key, currentNibbleIndex);
                    RLPList branchNode  = DecodeNode(node.Items[branchIndex]);

                    // Update the value down this path, and obtain the new branch node.
                    RLPList newBranchNode = NodeUpdateValue(branchNode, key, currentNibbleIndex + 1, value);

                    // Set the branch node
                    node.Items[branchIndex] = EncodeNode(newBranchNode);
                    return(node);
                }

            case TrieNodeType.Leaf:
            case TrieNodeType.Extension:

                // Obtain the key nibbles from the node.
                byte[] nodeNibbles = UnpackPrefixedNode(node.Items[0]).nibbles;
                keyNibbles = ByteArrayToNibbleArray(key, currentNibbleIndex);

                // Next we'll want to count all of the shared nibbles among the node nibbles and our key. We'll count all common nibbles.
                int sharedNibbles = 0;
                int nibbleCount   = Math.Min(nodeNibbles.Length, keyNibbles.Length);
                for (int i = 0; i < nibbleCount; i++)
                {
                    // If any nibble doesn't match, we can stop counting.
                    if (keyNibbles[i] != nodeNibbles[i])
                    {
                        break;
                    }

                    // Otherwise keep counting all shared nibbles.
                    sharedNibbles++;
                }

                // We'll create our node here to handle specific cases.
                RLPList newNode = null;

                // If our node and key nibbles are all shared
                if (sharedNibbles == keyNibbles.Length && nodeNibbles.Length == keyNibbles.Length)
                {
                    // And if our node type was a leaf, then we simply return a leaf with the value we provided (instead of any existing value)
                    if (nodeType == TrieNodeType.Leaf)
                    {
                        return(new RLPList(node.Items[0], value));
                    }

                    // Otherwise this is an extension, so we decode our next node and update that.
                    RLPList nextNode = DecodeNode(node.Items[1]);
                    newNode = NodeUpdateValue(nextNode, key, currentNibbleIndex + sharedNibbles, value);
                }

                // If all of our node nibbles are done, yet we still have key nibbles
                else if (sharedNibbles == nodeNibbles.Length)
                {
                    if (nodeType == TrieNodeType.Leaf)
                    {
                        // If our node type is a leaf and we still have key nibbles, our new node type will be a branch node.
                        newNode = new RLPList();
                        for (int i = 0; i < 16; i++)
                        {
                            newNode.Items.Add(BLANK_NODE);
                        }

                        // We add the leaf's value as the branch's value.
                        newNode.Items.Add(node.Items[1]);

                        // We create a new leaf that this branch node will connect to with the remainder of our key, and store our value there.
                        RLPList newLeafNode = new RLPList(PackPrefixedNode(TrieNodeType.Leaf, keyNibbles.Slice(sharedNibbles + 1)), value);

                        // Set our branch to our new leaf node
                        int branchIndex = keyNibbles[sharedNibbles];
                        newNode.Items[branchIndex] = EncodeNode(newLeafNode);
                    }
                    else
                    {
                        // This is an extension, we simply update the next node
                        RLPList nextNode = DecodeNode(node.Items[1]);
                        newNode = NodeUpdateValue(nextNode, key, currentNibbleIndex + sharedNibbles, value);
                    }
                }
                else
                {
                    // Both the node and the key have nibbles they didn't agree on.
                    newNode = new RLPList();
                    for (int i = 0; i < 17; i++)
                    {
                        newNode.Items.Add(BLANK_NODE);
                    }

                    // Obtain our branch index for our node's nibble
                    int branchNodeIndex = nodeNibbles[sharedNibbles];

                    // If this is an extension and there's one node nibble left.
                    if (nodeType == TrieNodeType.Extension && nodeNibbles.Length - sharedNibbles == 1)
                    {
                        // We set this branch to the extension's next node.
                        newNode.Items[branchNodeIndex] = node.Items[1];
                    }
                    else
                    {
                        // Whatever type of node this was before, we put it into our branch slot and advance it a key/strip a key nibble for the new subnode.
                        RLPList subNodeReplacement = new RLPList(PackPrefixedNode(nodeType, nodeNibbles.Slice(sharedNibbles + 1)), node.Items[1]);
                        newNode.Items[branchNodeIndex] = EncodeNode(subNodeReplacement);
                    }

                    // If our key nibbles are empty, we can set our value in this branch.
                    if (keyNibbles.Length - sharedNibbles == 0)
                    {
                        newNode.Items[16] = value;
                    }
                    else
                    {
                        // Otherwise we take our next nibble from our key to decide the branch index for our other path.
                        int branchKeyIndex = keyNibbles[sharedNibbles];

                        // Create our new leaf node to store the remainder of our key and value.
                        RLPList keyLeafNode = new RLPList(PackPrefixedNode(TrieNodeType.Leaf, keyNibbles.Slice(sharedNibbles + 1)), value);
                        newNode.Items[branchKeyIndex] = EncodeNode(keyLeafNode);
                    }
                }

                // If we had any shared nibbles between our node's nibbles and our current key's nibbles, we create an extension for that accordingly and link it our created node below it.
                if (sharedNibbles > 0)
                {
                    return(new RLPList(PackPrefixedNode(TrieNodeType.Extension, nodeNibbles.Slice(0, sharedNibbles)), EncodeNode(newNode)));
                }
                else
                {
                    // Otherwise we just return the created node directly.
                    return(newNode);
                }
            }

            // If we somehow end up here, throw an exception.
            throw new ArgumentException("Unexpected node type while updating trie nodes.");
        }
Пример #24
0
        /// <summary>
        /// Given an trie node, traverses down the appropriate node path to obtain the value for a given key and the current nibble index in our search.
        /// </summary>
        /// <param name="node">The current trie node to traverse through for our value, given our key and current nibble index.</param>
        /// <param name="key">The key for the value which we wish to obtain.</param>
        /// <param name="currentNibbleIndex">The index of the current nibble in our key which we are at in our indexing process.</param>
        /// <returns>Returns the value stored in the trie for the provided key, or null if it does not exist.</returns>
        private byte[] NodeGetValue(RLPList node, Memory <byte> key, int currentNibbleIndex = 0)
        {
            // Obtain our node type
            TrieNodeType nodeType = GetNodeType(node);

            // Switch on node type
            switch (nodeType)
            {
            // If it's blank, return the blank node
            case TrieNodeType.Blank:
                return(null);

            case TrieNodeType.Branch:
                // If it's a branch and the key to search for has ended, then we've found our result in this node.
                if (key.Length * 2 == currentNibbleIndex)
                {
                    // Return the "value" portion of our node, the 17th item.
                    return(((RLPByteArray)node.Items[16]).Data.ToArray());
                }

                // Obtain our next nibble which constitutes branch index.
                byte branchIndex = GetNibble(key, currentNibbleIndex);

                // Obtain our node at the given index and obtain that item
                RLPList branchNode = DecodeNode(node.Items[branchIndex]);

                // Keep running down the branch recursively.
                return(NodeGetValue(branchNode, key, currentNibbleIndex + 1));

            case TrieNodeType.Extension:
                // If it's an extension, we'll unpack the whole nibble set of our first item (prefix + key remainder)
                byte[] nibbles = UnpackPrefixedNode(node.Items[0]).nibbles;

                // If there's less of the key than nibbles in this extension, it's not here.
                if ((key.Length * 2) - currentNibbleIndex < nibbles.Length)
                {
                    return(null);
                }

                // Any "shared nibbles" we find in this nibble set should match the current length of our key.
                for (int i = 0; i < nibbles.Length; i++)
                {
                    if (nibbles[i] != GetNibble(key, currentNibbleIndex + i))
                    {
                        return(null);
                    }
                }

                // The second item in this node will represent the "next node", so we recursively obtain from that, advancing our key position as well.
                RLPList nextNode = DecodeNode(node.Items[1]);
                return(NodeGetValue(nextNode, key, currentNibbleIndex + nibbles.Length));

            case TrieNodeType.Leaf:
                // If it's a leaf, we'll unpack the whole nibble set of our first item (prefix + key remainder)
                nibbles = UnpackPrefixedNode(node.Items[0]).nibbles;

                // And we'll want to verify the remainder of our key, first we verify length.
                if ((key.Length * 2) - currentNibbleIndex != nibbles.Length)
                {
                    return(null);
                }

                // Then we make sure the rest of the key sequence matches
                for (int i = 0; i < nibbles.Length; i++)
                {
                    if (nibbles[i] != GetNibble(key, currentNibbleIndex + i))
                    {
                        return(null);
                    }
                }

                // Otherwise we passed verification, so we return the "value" that is stored in this leaf.
                return(node.Items[1]);
            }

            // Throw an exception if we somehow end up here.
            throw new ArgumentException("Could not obtain node, invalid node type!");
        }
Пример #25
0
        /// <summary>
        /// Given a branch node that has had a branch path removed, reviews the node to see if it should be another type, and returns a replacement node for the provided node.
        /// </summary>
        /// <param name="node">The branch node to clean up and replace with an updated/reformatted node.</param>
        /// <returns>Returns an updated/reformatted node which accounts for a deleted branch path, possibly homogenizing the downward path, such that it should be a leaf instead of a branch, etc.</returns>
        private RLPList CleanupBranch(RLPList node)
        {
            // If the node is null or does not have 17 items, then it is not an extension and we do not change it.
            if (node == null || node.Items.Count != 17)
            {
                return(node);
            }

            // After deletion, we need to clean up adjacent branches which may be unnecessary and could possibly be shortened.
            int nonBlankCount     = 0;
            int lastNonBlankIndex = -1;

            for (int i = 0; i < node.Items.Count; i++)
            {
                if (DecodeNode(node.Items[i]) != null)
                {
                    nonBlankCount++;
                    lastNonBlankIndex = i;
                }
            }

            // If we have more than one subnode referenced, this branch node is necessary.
            if (nonBlankCount > 1)
            {
                return(node);
            }

            // If only the value item is non blank, then we can convert this to a leaf with a blank key remainder, and the same value slot.
            if (lastNonBlankIndex == 16)
            {
                return(new RLPList(PackPrefixedNode(TrieNodeType.Leaf, Array.Empty <byte>()), node.Items[16]));
            }

            // Otherwise it was a branch that was changed, so we obtain the branch subnode
            RLPList      subNode     = DecodeNode(node.Items[lastNonBlankIndex]);
            TrieNodeType subNodeType = GetNodeType(subNode);

            // If our sub node type is also a branch, we convert this into an extension since its a straight path with shared nibbles. We convert the branch nibble to an extension shared nibble.
            if (subNodeType == TrieNodeType.Branch)
            {
                // Pack our prefix nibble set (using branch nibble as a shared nibble) and return our extension node which links to our branch subnode.
                return(new RLPList(PackPrefixedNode(TrieNodeType.Extension, new byte[] { (byte)lastNonBlankIndex }), EncodeNode(subNode)));
            }

            // If it's a leaf or extension (a node with key and value), the subnode takes the place of this one, and we prefix our subnode's key nibbles with our branch key nibble.
            else if (subNodeType == TrieNodeType.Leaf || subNodeType == TrieNodeType.Extension)
            {
                // Obtain our unpacked prefix/nibble set.
                var subNodePrefixUnpacked = UnpackPrefixedNode(subNode.Items[0]);

                // Add our branch nibble, and pack it back into the packed prefix node
                subNodePrefixUnpacked.nibbles = new byte[] { (byte)lastNonBlankIndex }.Concat(subNodePrefixUnpacked.nibbles);

                // Create our modified leaf/extension with it's longer "key" portion and the same "value" and return it
                return(new RLPList(PackPrefixedNode(subNodePrefixUnpacked.type, subNodePrefixUnpacked.nibbles), subNode.Items[1]));
            }
            else
            {
                // Otherwise if we somehow end up here, throw an error.
                throw new ArgumentException("Could not clean up branch node because it has unexpected conditions!");
            }
        }
Пример #26
0
        /// <summary>
        /// Deserializes the given RLP serialized block header and sets all values accordingly.
        /// </summary>
        /// <param name="item">The RLP item to block header and obtain values from.</param>
        public void Deserialize(RLPItem item)
        {
            // Verify this is a list
            if (!item.IsList)
            {
                throw new ArgumentException();
            }

            // Verify it has 15 items.
            RLPList rlpBlockHeader = (RLPList)item;

            if (rlpBlockHeader.Items.Count != 15)
            {
                throw new ArgumentException();
            }

            // Verify the types of all items
            for (int i = 0; i < rlpBlockHeader.Items.Count; i++)
            {
                if (!rlpBlockHeader.Items[i].IsByteArray)
                {
                    throw new ArgumentException();
                }
            }

            // Verify our items are the correct length
            if (((RLPByteArray)rlpBlockHeader.Items[0]).Data.Length != KeccakHash.HASH_SIZE)
            {
                throw new ArgumentException();
            }

            if (((RLPByteArray)rlpBlockHeader.Items[1]).Data.Length != KeccakHash.HASH_SIZE)
            {
                throw new ArgumentException();
            }

            if (((RLPByteArray)rlpBlockHeader.Items[2]).Data.Length != Address.ADDRESS_SIZE)
            {
                throw new ArgumentException();
            }

            if (((RLPByteArray)rlpBlockHeader.Items[3]).Data.Length != KeccakHash.HASH_SIZE)
            {
                throw new ArgumentException();
            }

            if (((RLPByteArray)rlpBlockHeader.Items[4]).Data.Length != KeccakHash.HASH_SIZE)
            {
                throw new ArgumentException();
            }

            if (((RLPByteArray)rlpBlockHeader.Items[5]).Data.Length != KeccakHash.HASH_SIZE)
            {
                throw new ArgumentException();
            }

            if (((RLPByteArray)rlpBlockHeader.Items[6]).Data.Length != EVMDefinitions.BLOOM_FILTER_SIZE)
            {
                throw new ArgumentException();
            }


            // Obtain our items
            PreviousHash         = rlpBlockHeader.Items[0];
            UnclesHash           = rlpBlockHeader.Items[1];
            Coinbase             = RLP.ToInteger((RLPByteArray)rlpBlockHeader.Items[2], Address.ADDRESS_SIZE);
            StateRootHash        = rlpBlockHeader.Items[3];
            TransactionsRootHash = rlpBlockHeader.Items[4];
            ReceiptsRootHash     = rlpBlockHeader.Items[5];
            Bloom       = RLP.ToInteger((RLPByteArray)rlpBlockHeader.Items[6], EVMDefinitions.BLOOM_FILTER_SIZE);
            Difficulty  = RLP.ToInteger((RLPByteArray)rlpBlockHeader.Items[7]);
            BlockNumber = RLP.ToInteger((RLPByteArray)rlpBlockHeader.Items[8]);
            GasLimit    = RLP.ToInteger((RLPByteArray)rlpBlockHeader.Items[9]);
            GasUsed     = RLP.ToInteger((RLPByteArray)rlpBlockHeader.Items[10]);
            Timestamp   = RLP.ToInteger((RLPByteArray)rlpBlockHeader.Items[11]);
            ExtraData   = rlpBlockHeader.Items[12];
            MixHash     = rlpBlockHeader.Items[13];
            Nonce       = rlpBlockHeader.Items[14];
        }
Пример #27
0
        /// <summary>
        /// Given a trie node, traverses down the appropriate node path, removing the key/value for a given key and the current nibble index for our key in the indexing process.
        /// </summary>
        /// <param name="node">The current trie node to traverse down to remove our key/value, given our key and current nibble index.</param>
        /// <param name="key">The key for the key/value which we wish to remove.</param>
        /// <param name="currentNibbleIndex">The index of the current nibble in our key which we are at in our indexing process.</param>
        /// <returns>Returns a node with the given removal which is to be used as the replacement for the provided node.</returns>
        private RLPList NodeRemoveValue(RLPList node, Memory <byte> key, int currentNibbleIndex = 0)
        {
            // Obtain the node type
            TrieNodeType nodeType = GetNodeType(node);

            // Switch on our node type
            switch (nodeType)
            {
            // If it's a blank node, we return the blank node.
            case TrieNodeType.Blank:
                return(null);

            case TrieNodeType.Branch:

                // If we reached the end of key, we remove the value from here (since our value for this key resides here), and we cleanup/reformat this branch.
                if (currentNibbleIndex == key.Length * 2)
                {
                    node.Items[16] = EncodeNode(null);
                    return(CleanupBranch(node));
                }

                // Otherwise we obtain delete down the branch path, so we obtain the branch node.
                byte    branchNodeIndex = GetNibble(key, currentNibbleIndex);
                RLPList branchNode      = DecodeNode(node.Items[branchNodeIndex]);

                // Delete by going down the branch node, and obtain the resulting branch node.
                RLPList newBranchNode = NodeRemoveValue(NodeDuplicate(branchNode), key, currentNibbleIndex + 1);

                // If the new node is the same as the old, we can return our node as it is. This node only changes if the path below changed.
                if (NodeEquals(branchNode, newBranchNode))
                {
                    return(node);
                }

                // Otherwise we set our updated now in our branch index.
                node.Items[branchNodeIndex] = EncodeNode(newBranchNode);

                // If our new node is null, then should check if this node might need to be fixed up, otherwise we can return the node as it is.
                if (newBranchNode == null)
                {
                    return(CleanupBranch(node));
                }
                else
                {
                    return(node);
                }

            case TrieNodeType.Leaf:

                // Obtain our key remainder nibbles from our leaf.
                byte[] nibbles = UnpackPrefixedNode(node.Items[0]).nibbles;

                // If the remainder of our key doesnt match the key remainder on the leaf, return the node as it is, it isn't the leaf we were searching for.
                if ((key.Length * 2) - currentNibbleIndex != nibbles.Length)
                {
                    return(node);
                }

                // Any key remainder nibbles we find in this nibble set should match the remainder of our key.
                for (int i = 0; i < nibbles.Length; i++)
                {
                    if (nibbles[i] != GetNibble(key, currentNibbleIndex + i))
                    {
                        return(node);
                    }
                }

                // We can now confirm the key has matched, hence this is the leaf we're searching for, and we return a blank node to replace it.
                return(null);

            case TrieNodeType.Extension:
                // Obtain our key shared nibbles from our extension.
                nibbles = UnpackPrefixedNode(node.Items[0]).nibbles;

                // If the remainder of our key is smaller than the shared nibbles, return the node as it is, the key doesn't exist down this path.
                if ((key.Length * 2) - currentNibbleIndex < nibbles.Length)
                {
                    return(node);
                }

                // Any shared nibbles we find in this nibble set should match the remainder of our key.
                for (int i = 0; i < nibbles.Length; i++)
                {
                    if (nibbles[i] != GetNibble(key, currentNibbleIndex + i))
                    {
                        return(node);
                    }
                }

                // Obtain our decoded next node.
                RLPList nextNode    = DecodeNode(node.Items[1]);
                RLPList newNextNode = NodeRemoveValue(NodeDuplicate(nextNode), key, currentNibbleIndex + nibbles.Length);

                // If the next node didn't change at all, we can simply keep this node as it is.
                if (NodeEquals(nextNode, newNextNode))
                {
                    return(node);
                }

                // If our new next node is null, then an extension shouldn't exist here, so we return null, changes should propogate changes upwards.
                if (newNextNode == null)
                {
                    return(null);
                }

                // Get our new next node type
                TrieNodeType newNodeType = GetNodeType(newNextNode);

                // If the new node type is a branch..
                if (newNodeType == TrieNodeType.Branch)
                {
                    // We return a fixed up extension, with our new encoded next node.
                    return(new RLPList(PackPrefixedNode(TrieNodeType.Extension, nibbles), EncodeNode(newNextNode)));
                }
                else
                {
                    // Obtain the subnodes prefix/nibbles
                    var subNodePrefixNibbles = UnpackPrefixedNode(newNextNode.Items[0]);

                    // If the new node type is an extension or leaf, then we instead join it with this extension.
                    return(new RLPList(PackPrefixedNode(subNodePrefixNibbles.type, nibbles.Concat(subNodePrefixNibbles.nibbles)), newNextNode.Items[1]));
                }
            }

            // If we somehow end up here, throw an exception.
            throw new ArgumentException("Unexpected node type while removing trie nodes.");
        }