示例#1
0
 public Chest(Dictionary serialized) : base(serialized)
 {
     if (serialized.TryGetValue((Text)"rewards", out var rewards))
     {
         Rewards = rewards.ToList(i => new RedeemRewardSheet.RewardInfo((Dictionary)i));
     }
     // ItemFactory.CreateMaterial로 생성된 케이스
     else
     {
         Rewards = new List <RedeemRewardSheet.RewardInfo>();
         ItemId  = Hashcash.Hash(Serialize().EncodeIntoChunks().SelectMany(b => b).ToArray());
     }
 }
示例#2
0
        public async Task ConstructDeterministic(int waitMilliseconds)
        {
            Address     avatarAddress = new PrivateKey().ToAddress();
            Address     agentAddress  = new PrivateKey().ToAddress();
            AvatarState avatarStateA  = GetNewAvatarState(avatarAddress, agentAddress);
            await Task.Delay(waitMilliseconds);

            AvatarState avatarStateB = GetNewAvatarState(avatarAddress, agentAddress);

            HashDigest <SHA256> Hash(AvatarState avatarState) => Hashcash.Hash(new Codec().Encode(avatarState.Serialize()));

            Assert.Equal(Hash(avatarStateA), Hash(avatarStateB));
        }
示例#3
0
        public void Commit(int addressCount)
        {
            IKeyValueStore keyValueStore = new MemoryKeyValueStore();
            var            codec         = new Codec();

            ITrie trieA = new MerkleTrie(keyValueStore);

            var addresses = new Address[addressCount];
            var states    = new IValue[addressCount];

            for (int i = 0; i < addressCount; ++i)
            {
                addresses[i] = new PrivateKey().ToAddress();
                states[i]    = (Binary)TestUtils.GetRandomBytes(128);

                trieA.Set(addresses[i].ToByteArray(), states[i]);
            }

            byte[] path = TestUtils.GetRandomBytes(32);
            trieA.Set(path, (Text)"foo");
            HashDigest <SHA256> rootHash = Hashcash.Hash(codec.Encode(trieA.Root.ToBencodex()));

            Assert.True(trieA.TryGet(path, out IValue stateA));
            Assert.Equal((Text)"foo", stateA);

            ITrie trieB = trieA.Commit();

            Assert.True(trieB.TryGet(path, out IValue stateB));
            Assert.Equal((Text)"foo", stateB);

            trieB.Set(path, (Text)"bar");

            Assert.True(trieA.TryGet(path, out stateA));
            Assert.Equal((Text)"foo", stateA);
            Assert.True(trieB.TryGet(path, out stateB));
            Assert.Equal((Text)"bar", stateB);

            ITrie trieC = trieB.Commit();
            ITrie trieD = trieC.Commit();

            Assert.NotEqual(trieA.Root.Hash(), trieB.Root.Hash());
            Assert.NotEqual(trieA.Root.Hash(), trieC.Root.Hash());
            Assert.NotEqual(trieB.Root.Hash(), trieC.Root.Hash());
            Assert.Equal(trieC.Root.Hash(), trieD.Root.Hash());
        }
示例#4
0
 public Block(
     long index,
     long difficulty,
     Nonce nonce,
     Address?miner,
     HashDigest <SHA256>?previousHash,
     DateTimeOffset timestamp,
     IEnumerable <Transaction <T> > transactions)
 {
     Index        = index;
     Difficulty   = difficulty;
     Nonce        = nonce;
     Miner        = miner;
     PreviousHash = previousHash;
     Timestamp    = timestamp;
     Transactions = transactions;
     Hash         = Hashcash.Hash(ToBencodex(false, false));
 }
示例#5
0
        private byte[] ToKey(byte[] key)
        {
            if (_secure)
            {
                key = Hashcash.Hash(key).ToByteArray();
            }

            var       res            = new byte[key.Length * 2];
            const int lowerBytesMask = 0b00001111;

            for (var i = 0; i < key.Length; ++i)
            {
                res[i * 2]     = (byte)(key[i] >> 4);
                res[i * 2 + 1] = (byte)(key[i] & lowerBytesMask);
            }

            return(res);
        }
示例#6
0
        public void DecodeValidFullNode()
        {
            var hashA = Hashcash.Hash(TestUtils.GetRandomBytes(128));
            var hashB = Hashcash.Hash(TestUtils.GetRandomBytes(128));
            var list  = new List(new IValue[]
            {
                (Binary)hashA.ToByteArray(),
                default(Null),
                default(Null),
                default(Null),
                default(Null),
                default(Null),
                default(Null),
                default(Null),
                default(Null),
                default(Null),
                default(Null),
                default(Null),
                default(Null),
                default(Null),
                default(Null),
                default(Null),
                (Binary)hashB.ToByteArray(),
            });

            Assert.Equal(17, list.Count);

            INode node = NodeDecoder.Decode(list);

            Assert.IsType <FullNode>(node);
            var fullNode = (FullNode)node;

            Assert.Equal(new HashNode(hashB), fullNode.Value);
            Assert.Equal(new HashNode(hashA), fullNode.Children[0]);
            for (int i = 1; i < 16; ++i)
            {
                Assert.Null(fullNode.Children[i]);
            }
        }
示例#7
0
        internal void Validate(DateTimeOffset currentTime)
        {
            Header.Validate(currentTime);

            foreach (Transaction <T> tx in Transactions)
            {
                tx.Validate();
            }

            if (ProtocolVersion > 0)
            {
                HashDigest <SHA256> expectedPreEvaluationHash =
                    Hashcash.Hash(Header.SerializeForHash(includeStateRootHash: false));
                if (!expectedPreEvaluationHash.Equals(PreEvaluationHash))
                {
                    string message =
                        $"The expected pre evaluation hash of block {Hash} is " +
                        $"{expectedPreEvaluationHash}, but its pre evaluation hash is " +
                        $"{PreEvaluationHash}.";
                    throw new InvalidBlockPreEvaluationHashException(
                              PreEvaluationHash,
                              expectedPreEvaluationHash,
                              message);
                }
            }

            HashDigest <SHA256>?calculatedTxHash =
                CalcualteTxHashes(Transactions.OrderBy(tx => tx.Id));

            if (!calculatedTxHash.Equals(TxHash))
            {
                throw new InvalidBlockTxHashException(
                          $"Block #{Index} {Hash}'s TxHash doesn't match its content.",
                          TxHash,
                          calculatedTxHash
                          );
            }
        }
示例#8
0
        private static HashDigest <SHA256>?CalcualteTxHashes(IEnumerable <Transaction <T> > txs)
        {
            if (!txs.Any())
            {
                return(null);
            }

            byte[][] serializedTxs      = txs.Select(tx => tx.Serialize(true)).ToArray();
            int      txHashSourceLength = serializedTxs.Select(b => b.Length).Sum() + 2;
            var      txHashSource       = new byte[txHashSourceLength];

            // Bencodex lists look like: l...e
            txHashSource[0] = 0x6c;
            txHashSource[txHashSourceLength - 1] = 0x65;
            int offset = 1;

            foreach (byte[] serializedTx in serializedTxs)
            {
                serializedTx.CopyTo(txHashSource, offset);
                offset += serializedTx.Length;
            }

            return(Hashcash.Hash(txHashSource));
        }
示例#9
0
        /// <summary>
        /// Creates a <see cref="Block{T}"/> instance by manually filling all field values.
        /// For a more automated way, see also <see cref="Mine"/> method.
        /// </summary>
        /// <param name="index">The height of the block to create.  Goes to the <see cref="Index"/>.
        /// </param>
        /// <param name="difficulty">The mining difficulty that <paramref name="nonce"/> has to
        /// satisfy.  Goes to the <see cref="Difficulty"/>.</param>
        /// <param name="nonce">The nonce which satisfy the given <paramref name="difficulty"/> with
        /// any other field values.  Goes to the <see cref="Nonce"/>.</param>
        /// <param name="miner">An optional address refers to who mines this block.
        /// Goes to the <see cref="Miner"/>.</param>
        /// <param name="previousHash">The previous block's <see cref="Hash"/>.  If it's a genesis
        /// block (i.e., <paramref name="index"/> is 0) this should be <c>null</c>.
        /// Goes to the <see cref="PreviousHash"/>.</param>
        /// <param name="timestamp">The time this block is created.  Goes to
        /// the <see cref="Timestamp"/>.</param>
        /// <param name="transactions">The transactions to be mined together with this block.
        /// Transactions become sorted in an unpredicted-before-mined order and then go to
        /// the <see cref="Transactions"/> property.
        /// </param>
        /// <seealso cref="Mine"/>
        public Block(
            long index,
            long difficulty,
            Nonce nonce,
            Address?miner,
            HashDigest <SHA256>?previousHash,
            DateTimeOffset timestamp,
            IEnumerable <Transaction <T> > transactions)
        {
            Index        = index;
            Difficulty   = difficulty;
            Nonce        = nonce;
            Miner        = miner;
            PreviousHash = previousHash;
            Timestamp    = timestamp;
            Transactions = transactions.OrderBy(tx => tx.Id).ToArray();
            var codec = new Codec();

            TxHash = Transactions.Any()
                ? Hashcash.Hash(
                codec.Encode(
                    new Bencodex.Types.List(Transactions.Select(tx =>
                                                                (IValue)tx.ToBencodex(true)))))
                : (HashDigest <SHA256>?)null;

            // FIXME: This does not need to be computed every time?
            Hash = Hashcash.Hash(SerializeForHash());

            // As the order of transactions should be unpredictable until a block is mined,
            // the sorter key should be derived from both a block hash and a txid.
            var hashInteger = new BigInteger(Hash.ToByteArray());

            // If there are multiple transactions for the same signer these should be ordered by
            // their tx nonces.  So transactions of the same signer should have the same sort key.
            // The following logic "flattens" multiple tx ids having the same signer into a single
            // txid by applying XOR between them.
            IImmutableDictionary <Address, IImmutableSet <Transaction <T> > > signerTxs = Transactions
                                                                                          .GroupBy(tx => tx.Signer)
                                                                                          .ToImmutableDictionary(
                g => g.Key,
                g => (IImmutableSet <Transaction <T> >)g.ToImmutableHashSet()
                );
            IImmutableDictionary <Address, BigInteger> signerTxIds = signerTxs
                                                                     .ToImmutableDictionary(
                pair => pair.Key,
                pair => pair.Value
                .Select(tx => new BigInteger(tx.Id.ToByteArray()))
                .OrderBy(txid => txid)
                .Aggregate((a, b) => a ^ b)
                );

            // Order signers by values derivied from both block hash and their "flatten" txid:
            IImmutableList <Address> signers = signerTxIds
                                               .OrderBy(pair => pair.Value ^ hashInteger)
                                               .Select(pair => pair.Key)
                                               .ToImmutableArray();

            // Order transactions for each signer by their tx nonces:
            Transactions = signers
                           .SelectMany(signer => signerTxs[signer].OrderBy(tx => tx.Nonce))
                           .ToImmutableArray();
        }
示例#10
0
        /// <summary>
        /// Generate a block with given <paramref name="transactions"/>.
        /// </summary>
        /// <param name="index">Index of the block.</param>
        /// <param name="difficulty">Difficulty to find the <see cref="Block{T}"/>
        /// <see cref="Nonce"/>.</param>
        /// <param name="miner">The <see cref="Address"/> of miner that mined the block.</param>
        /// <param name="previousHash">
        /// The <see cref="HashDigest{SHA256}"/> of previous block.
        /// </param>
        /// <param name="timestamp">The <see cref="DateTimeOffset"/> when mining started.</param>
        /// <param name="transactions"><see cref="Transaction{T}"/>s that are going to be included
        /// in the block.</param>
        /// <param name="cancellationToken">
        /// A cancellation token used to propagate notification that this
        /// operation should be canceled.</param>
        /// <returns>A <see cref="Block{T}"/> that mined.</returns>
        public static Block <T> Mine(
            long index,
            long difficulty,
            Address miner,
            HashDigest <SHA256>?previousHash,
            DateTimeOffset timestamp,
            IEnumerable <Transaction <T> > transactions,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            var txs = transactions.OrderBy(tx => tx.Id).ToImmutableArray();

            Block <T> MakeBlock(Nonce n) => new Block <T>(
                index,
                difficulty,
                n,
                miner,
                previousHash,
                timestamp,
                txs);

            // Poor man' way to optimize stamp...
            // FIXME: We need to rather reorganize the serialization layout.
            byte[] emptyNonce   = MakeBlock(new Nonce(new byte[0])).SerializeForHash();
            byte[] oneByteNonce = MakeBlock(new Nonce(new byte[1])).SerializeForHash();
            int    offset       = 0;

            while (offset < emptyNonce.Length && emptyNonce[offset].Equals(oneByteNonce[offset]))
            {
                offset++;
            }

            const int nonceLength = 2;  // In Bencodex, empty bytes are represented as "0:".

            byte[] stampPrefix = new byte[offset];
            Array.Copy(emptyNonce, stampPrefix, stampPrefix.Length);
            byte[] stampSuffix = new byte[emptyNonce.Length - offset - nonceLength];
            Array.Copy(emptyNonce, offset + nonceLength, stampSuffix, 0, stampSuffix.Length);

            Nonce nonce = Hashcash.Answer(
                n =>
            {
                int nLen       = n.ByteArray.Length;
                byte[] nLenStr = Encoding.ASCII.GetBytes(
                    nLen.ToString(CultureInfo.InvariantCulture));
                int totalLen =
                    stampPrefix.Length + nLenStr.Length + 1 + nLen + stampSuffix.Length;
                byte[] stamp = new byte[totalLen];
                Array.Copy(stampPrefix, stamp, stampPrefix.Length);
                int pos = stampPrefix.Length;
                Array.Copy(nLenStr, 0, stamp, pos, nLenStr.Length);
                pos       += nLenStr.Length;
                stamp[pos] = 0x3a;      // ':'
                pos++;
                n.ByteArray.CopyTo(stamp, pos);
                pos += nLen;
                Array.Copy(stampSuffix, 0, stamp, pos, stampSuffix.Length);
                return(stamp);
            },
                difficulty,
                cancellationToken
                );

            return(MakeBlock(nonce));
        }
示例#11
0
        internal void Validate(DateTimeOffset currentTime)
        {
            if (ProtocolVersion < 0)
            {
                throw new InvalidBlockProtocolVersionException(
                          ProtocolVersion,
                          $"A block's protocol version cannot be less than zero: {ProtocolVersion}."
                          );
            }
            else if (ProtocolVersion > CurrentProtocolVersion)
            {
                string message =
                    $"Unknown protocol version: {ProtocolVersion}; " +
                    $"the highest known version is {CurrentProtocolVersion}.";
                throw new InvalidBlockProtocolVersionException(ProtocolVersion, message);
            }

            DateTimeOffset ts = DateTimeOffset.ParseExact(
                Timestamp,
                TimestampFormat,
                CultureInfo.InvariantCulture
                );

            HashDigest <SHA256> hash = new HashDigest <SHA256>(Hash);

            if (currentTime + TimestampThreshold < ts)
            {
                throw new InvalidBlockTimestampException(
                          $"The block #{Index} {hash}'s timestamp ({Timestamp}) is " +
                          $"later than now ({currentTime}, threshold: {TimestampThreshold})."
                          );
            }

            if (Index < 0)
            {
                throw new InvalidBlockIndexException(
                          $"Block #{Index} {hash}'s index must be 0 or more."
                          );
            }

            if (Difficulty > TotalDifficulty)
            {
                var msg = $"Block #{Index} {hash}'s difficulty ({Difficulty}) " +
                          $"must be less than its TotalDifficulty ({TotalDifficulty}).";
                throw new InvalidBlockTotalDifficultyException(
                          Difficulty,
                          TotalDifficulty,
                          msg
                          );
            }

            if (Index == 0)
            {
                if (Difficulty != 0)
                {
                    throw new InvalidBlockDifficultyException(
                              $"Difficulty must be 0 for the genesis block {hash}, " +
                              $"but its difficulty is {Difficulty}."
                              );
                }

                if (TotalDifficulty != 0)
                {
                    var msg = "Total difficulty must be 0 for the genesis block " +
                              $"{hash}, but its total difficulty is " +
                              $"{TotalDifficulty}.";
                    throw new InvalidBlockTotalDifficultyException(
                              Difficulty,
                              TotalDifficulty,
                              msg
                              );
                }

                if (!PreviousHash.IsEmpty)
                {
                    throw new InvalidBlockPreviousHashException(
                              $"Previous hash must be empty for the genesis block " +
                              $"{hash}, but its value is {ByteUtil.Hex(PreviousHash)}."
                              );
                }
            }
            else
            {
                if (Difficulty < 1)
                {
                    throw new InvalidBlockDifficultyException(
                              $"Block #{Index} {hash}'s difficulty must be more than 0 " +
                              $"(except of the genesis block), but its difficulty is {Difficulty}."
                              );
                }

                if (PreviousHash.IsEmpty)
                {
                    throw new InvalidBlockPreviousHashException(
                              $"Block #{Index} {hash}'s previous hash " +
                              "must be present since it's not the genesis block."
                              );
                }
            }

            if (!new HashDigest <SHA256>(PreEvaluationHash.ToArray()).Satisfies(Difficulty))
            {
                throw new InvalidBlockNonceException(
                          $"Block #{Index} {hash}'s pre-evaluation hash " +
                          $"({ByteUtil.Hex(PreEvaluationHash)}) with the nonce " +
                          $"({ByteUtil.Hex(Nonce)}) does not satisfy its difficulty level {Difficulty}."
                          );
            }

            HashDigest <SHA256> calculatedHash = Hashcash.Hash(SerializeForHash());

            if (!hash.Equals(calculatedHash))
            {
                throw new InvalidBlockHashException(
                          $"The block #{Index} {hash}'s isn't matched its content, " +
                          $"caculcated: {calculatedHash}");
            }
        }
示例#12
0
        public static Block <T> Mine(
            long index,
            long difficulty,
            Address miner,
            HashDigest <SHA256>?previousHash,
            DateTimeOffset timestamp,
            IEnumerable <Transaction <T> > transactions)
        {
            // FIXME: We need to fix this to solve
            // https://github.com/planetarium/libplanet/issues/244.
            Transaction <T>[] orderedTxs = transactions.OrderBy(tx => tx.Nonce).ToArray();
            Block <T> MakeBlock(Nonce n) => new Block <T>(
                index,
                difficulty,
                n,
                miner,
                previousHash,
                timestamp,
                orderedTxs
                );

            // Poor man' way to optimize stamp...
            // FIXME: We need to rather reorganize the serialization layout.
            byte[] emptyNonce   = MakeBlock(new Nonce(new byte[0])).ToBencodex(false, false);
            byte[] oneByteNonce = MakeBlock(new Nonce(new byte[1])).ToBencodex(false, false);
            int    offset       = 0;

            while (offset < emptyNonce.Length && emptyNonce[offset].Equals(oneByteNonce[offset]))
            {
                offset++;
            }

            const int nonceLength = 2;  // In Bencodex, empty bytes are represented as "0:".

            byte[] stampPrefix = new byte[offset];
            Array.Copy(emptyNonce, stampPrefix, stampPrefix.Length);
            byte[] stampSuffix = new byte[emptyNonce.Length - offset - nonceLength];
            Array.Copy(emptyNonce, offset + nonceLength, stampSuffix, 0, stampSuffix.Length);

            Nonce nonce = Hashcash.Answer(
                n =>
            {
                int nLen       = n.ByteArray.Length;
                byte[] nLenStr = Encoding.ASCII.GetBytes(nLen.ToString());
                int totalLen   =
                    stampPrefix.Length + nLenStr.Length + 1 + nLen + stampSuffix.Length;
                byte[] stamp = new byte[totalLen];
                Array.Copy(stampPrefix, stamp, stampPrefix.Length);
                int pos = stampPrefix.Length;
                Array.Copy(nLenStr, 0, stamp, pos, nLenStr.Length);
                pos       += nLenStr.Length;
                stamp[pos] = 0x3a;      // ':'
                pos++;
                n.ByteArray.CopyTo(stamp, pos);
                pos += nLen;
                Array.Copy(stampSuffix, 0, stamp, pos, stampSuffix.Length);
                return(stamp);
            },
                difficulty
                );

            return(MakeBlock(nonce));
        }
示例#13
0
文件: Chest.cs 项目: GunnerJnr/lib9c
 public Chest(MaterialItemSheet.Row data, List <RedeemRewardSheet.RewardInfo> rewards) : base(data)
 {
     Rewards = rewards ?? new List <RedeemRewardSheet.RewardInfo>();
     ItemId  = Hashcash.Hash(Serialize().EncodeIntoChunks().SelectMany(b => b).ToArray());
 }
示例#14
0
 internal static HashDigest <SHA256> Hash(this INode node)
 {
     return(node is HashNode hashNode
         ? hashNode.HashDigest
         : Hashcash.Hash(node.Serialize()));
 }
示例#15
0
 public override void Set(IReadOnlyList <string> fields)
 {
     base.Set(fields);
     ItemId = Hashcash.Hash(Serialize().EncodeIntoChunks().SelectMany(b => b).ToArray());
 }
示例#16
0
 public Row(Bencodex.Types.Dictionary serialized) : base(serialized)
 {
     ItemId = Hashcash.Hash(serialized.EncodeIntoChunks().SelectMany(b => b).ToArray());
 }