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()); } }
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)); }
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()); }
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)); }
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); }
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]); } }
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 ); } }
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)); }
/// <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(); }
/// <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)); }
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}"); } }
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)); }
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()); }
internal static HashDigest <SHA256> Hash(this INode node) { return(node is HashNode hashNode ? hashNode.HashDigest : Hashcash.Hash(node.Serialize())); }
public override void Set(IReadOnlyList <string> fields) { base.Set(fields); ItemId = Hashcash.Hash(Serialize().EncodeIntoChunks().SelectMany(b => b).ToArray()); }
public Row(Bencodex.Types.Dictionary serialized) : base(serialized) { ItemId = Hashcash.Hash(serialized.EncodeIntoChunks().SelectMany(b => b).ToArray()); }