public static Transaction <T> CreateUnsigned( long nonce, PublicKey publicKey, BlockHash?genesisHash, IEnumerable <T> actions, IImmutableSet <Address> updatedAddresses = null, DateTimeOffset?timestamp = null ) { var signer = new Address(publicKey); if (ReferenceEquals(updatedAddresses, null)) { updatedAddresses = ImmutableHashSet <Address> .Empty; } DateTimeOffset ts = timestamp ?? DateTimeOffset.UtcNow; ImmutableArray <T> actionsArray = actions.ToImmutableArray(); if (!actionsArray.IsEmpty) { // FIXME: Although we are assuming all block hashes are SHA256 digest, we should // parametrize this in the future. BlockHash emptyBlockHash = BlockHash.FromHashDigest(default(HashDigest <SHA256>)); var evalUpdatedAddresses = ActionEvaluator <T> .GetUpdatedAddresses( new Transaction <T>( nonce, signer, publicKey, genesisHash, updatedAddresses, ts, actionsArray)); if (!updatedAddresses.IsSupersetOf(evalUpdatedAddresses)) { updatedAddresses = updatedAddresses.Union(evalUpdatedAddresses); } } return(new Transaction <T>( nonce, signer, publicKey, genesisHash, updatedAddresses, ts, actionsArray)); }
public void FromHashDigest() { byte[] b = { 0x28, 0x31, 0xd4, 0xc2, 0x4a, 0xe5, 0xd1, 0x93, 0x1a, 0x16, 0xde, 0x0a, 0x06, 0x6e, 0x23, 0x3e, 0x0e, 0xed, 0x1d, 0x3f, 0xdf, 0x6d, 0x57, 0x2a, 0xd5, 0x8d, 0x1c, 0x37, 0x05, 0xc8, 0xcb, 0xfc, }; var expected = new BlockHash(b); BlockHash actual = BlockHash.FromHashDigest(new HashDigest <SHA256>(b)); Assert.Equal(expected, actual); Assert.Equal( new BlockHash(new byte[32]), BlockHash.FromHashDigest(default)
/// <summary> /// A façade factory to create a new <see cref="Transaction{T}"/>. /// Unlike the <see cref="Transaction(long, Address, PublicKey, BlockHash?, /// IImmutableSet{Address}, DateTimeOffset, IEnumerable{T}, byte[])"/> /// constructor, it automatically fills the following values from: /// <list type="table"> /// <listheader> /// <term>Property</term> /// <description>Parameter the filled value derived from</description> /// </listheader> /// <item> /// <term><see cref="Signer"/></term> /// <description><paramref name="privateKey"/></description> /// </item> /// <item> /// <term><see cref="PublicKey"/></term> /// <description><paramref name="privateKey"/></description> /// </item> /// <item> /// <term><see cref="UpdatedAddresses"/></term> /// <description><paramref name="actions"/> and /// <paramref name="updatedAddresses"/></description> /// </item> /// </list> /// <para>Note that the <paramref name="privateKey"/> in itself is not /// included in the created <see cref="Transaction{T}"/>.</para> /// </summary> /// <remarks> /// This factory method tries its best to fill the <see /// cref="UpdatedAddresses"/> property by actually evaluating /// the given <paramref name="actions"/> (we call it “rehearsal /// mode”), but remember that its result /// is approximated in some degree, because the result of /// <paramref name="actions"/> are not deterministic until /// the <see cref="Transaction{T}"/> belongs to a <see /// cref="Libplanet.Blocks.Block{T}"/>. /// <para>If an <see cref="IAction"/> depends on previous states or /// some randomness to determine what <see cref="Address"/> to update, /// the automatically filled <see cref="UpdatedAddresses"/> became /// mismatched from the <see cref="Address"/>es /// <paramref name="actions"/> actually update after /// a <see cref="Libplanet.Blocks.Block{T}"/> is mined. /// Although such case would be rare, a programmer could manually give /// the <paramref name="updatedAddresses"/> parameter /// the <see cref="Address"/>es they predict to be updated.</para> /// <para>If an <see cref="IAction"/> oversimplifies the assumption /// about the <see cref="Libplanet.Blocks.Block{T}"/> it belongs to, /// runtime exceptions could be thrown from this factory method. /// The best solution to that is not to oversimplify things, /// there is an option to check <see cref="IActionContext"/>'s /// <see cref="IActionContext.Rehearsal"/> is <c>true</c> and /// a conditional logic for the case.</para> /// </remarks> /// <param name="nonce">The number of previous /// <see cref="Transaction{T}"/>s committed by the <see cref="Signer"/> /// of this transaction. This goes to the /// <see cref="Transaction{T}.Nonce"/> property.</param> /// <param name="privateKey">A <see cref="PrivateKey"/> of the account /// who creates and signs a new transaction. This key is used to fill /// the <see cref="Signer"/>, <see cref="PublicKey"/>, and /// <see cref="Signature"/> properties, but this in itself is not /// included in the transaction.</param> /// <param name="genesisHash">A <see cref="HashDigest{SHA256}"/> value /// of the genesis which this <see cref="Transaction{T}"/> is made from. /// This can be <c>null</c> iff the transaction is contained /// in the genesis block. /// </param> /// <param name="actions">A list of <see cref="IAction"/>s. This /// can be empty, but cannot be <c>null</c>. This goes to /// the <see cref="Actions"/> property, and <see cref="IAction"/>s /// are evaluated before a <see cref="Transaction{T}"/> is created /// in order to fill the <see cref="UpdatedAddresses"/>. See also /// <em>Remarks</em> section.</param> /// <param name="updatedAddresses"><see cref="Address"/>es whose /// states affected by <paramref name="actions"/>. /// These <see cref="Address"/>es are also included in /// the <see cref="UpdatedAddresses"/> property, besides /// <see cref="Address"/>es projected by evaluating /// <paramref name="actions"/>. See also <em>Remarks</em> section. /// </param> /// <param name="timestamp">The time this <see cref="Transaction{T}"/> /// is created and signed. This goes to the <see cref="Timestamp"/> /// property. If <c>null</c> (which is default) is passed this will /// be the current time.</param> /// <returns>A created new <see cref="Transaction{T}"/> signed by /// the given <paramref name="privateKey"/>.</returns> /// <exception cref="ArgumentNullException">Thrown when <c>null</c> /// is passed to <paramref name="privateKey"/> or /// or <paramref name="actions"/>. /// </exception> public static Transaction <T> Create( long nonce, PrivateKey privateKey, BlockHash?genesisHash, IEnumerable <T> actions, IImmutableSet <Address> updatedAddresses = null, DateTimeOffset?timestamp = null ) { if (ReferenceEquals(privateKey, null)) { throw new ArgumentNullException(nameof(privateKey)); } PublicKey publicKey = privateKey.PublicKey; var signer = new Address(publicKey); if (ReferenceEquals(updatedAddresses, null)) { updatedAddresses = ImmutableHashSet <Address> .Empty; } DateTimeOffset ts = timestamp ?? DateTimeOffset.UtcNow; ImmutableArray <T> actionsArray = actions.ToImmutableArray(); byte[] payload = new Transaction <T>( nonce, signer, publicKey, genesisHash, updatedAddresses, ts, actionsArray ).Serialize(false); if (!actionsArray.IsEmpty) { // FIXME: Although we are assuming all block hashes are SHA256 digest, we should // parametrize this in the future. BlockHash emptyBlockHash = BlockHash.FromHashDigest(default(HashDigest <SHA256>)); IAccountStateDelta delta = new Transaction <T>( nonce, signer, publicKey, genesisHash, updatedAddresses, ts, actionsArray ).EvaluateActions( emptyBlockHash, 0, new AccountStateDeltaImpl( _ => null, (_, c) => new FungibleAssetValue(c), signer ), signer, rehearsal: true ); if (!updatedAddresses.IsSupersetOf(delta.UpdatedAddresses)) { updatedAddresses = updatedAddresses.Union(delta.UpdatedAddresses); payload = new Transaction <T>( nonce, signer, publicKey, genesisHash, updatedAddresses, ts, actionsArray ).Serialize(false); } } byte[] sig = privateKey.Sign(payload); return(new Transaction <T>( nonce, signer, publicKey, genesisHash, updatedAddresses, ts, actionsArray, sig )); }
public static BlockHash Hash(byte[] bytes) => BlockHash.FromHashDigest(HashDigest <SHA256> .DeriveFrom(bytes));