/
BlockChain.TxExecution.cs
129 lines (123 loc) · 5.42 KB
/
BlockChain.TxExecution.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using Libplanet.Action;
using Libplanet.Assets;
using Libplanet.Blocks;
using Libplanet.Tx;
namespace Libplanet.Blockchain
{
public partial class BlockChain<T>
{
/// <summary>
/// Makes <see cref="TxExecution"/> instances from the given <paramref name="evaluations"/>.
/// </summary>
/// <param name="block">The block that evaluated actions belong to.</param>
/// <param name="evaluations">The result of evaluated actions.</param>
/// <returns>The corresponding <see cref="TxExecution"/>s.</returns>
internal IEnumerable<TxExecution> MakeTxExecutions(
Block<T> block,
IReadOnlyList<ActionEvaluation> evaluations
)
{
IEnumerable<IGrouping<TxId, ActionEvaluation>> evaluationsPerTxs = evaluations
.Where(e => e.InputContext.TxId is { })
.GroupBy(e => e.InputContext.TxId!.Value);
int count = 0;
foreach (IGrouping<TxId, ActionEvaluation> txEvals in evaluationsPerTxs)
{
TxId txid = txEvals.Key;
IAccountStateDelta prevStates = txEvals.First().InputContext.PreviousStates;
ActionEvaluation evalSum = txEvals.Last();
TxExecution txExecution;
if (evalSum.Exception is { } e)
{
txExecution = new TxFailure(block.Hash, txid, e.InnerException ?? e);
}
else
{
IAccountStateDelta outputStates = evalSum.OutputStates;
txExecution = new TxSuccess(
block.Hash,
txid,
outputStates.GetUpdatedStates(),
outputStates.UpdatedFungibleAssets.ToImmutableDictionary(
kv => kv.Key,
kv => (IImmutableDictionary<Currency, FungibleAssetValue>)kv.Value
.ToImmutableDictionary(
currency => currency,
currency => outputStates.GetBalance(kv.Key, currency) -
prevStates.GetBalance(kv.Key, currency)
)
),
outputStates.UpdatedFungibleAssets.ToImmutableDictionary(
kv => kv.Key,
kv => (IImmutableDictionary<Currency, FungibleAssetValue>)kv.Value
.ToImmutableDictionary(
currency => currency,
currency => outputStates.GetBalance(kv.Key, currency)
)
)
);
}
yield return txExecution;
count++;
}
_logger.Verbose(
"Prepared " + nameof(TxExecution) +
"s for {Txs} transactions within the block #{BlockIndex} {BlockHash}.",
count,
block.Index,
block.Hash
);
}
internal void UpdateTxExecutions(IEnumerable<TxExecution> txExecutions)
{
int count = 0;
foreach (TxExecution txExecution in txExecutions)
{
// Note that there are two overloaded methods of the same name PutTxExecution()
// in IStore. As those two have different signatures, run-time polymorphism
// does not work. Instead, we need the following hard-coded branch:
switch (txExecution)
{
case TxSuccess s:
Store.PutTxExecution(s); // IStore.PutTxExecution(TxSuccess)
_logger.Verbose(
"Updated " + nameof(TxSuccess) +
" for tx {TxId} within block {BlockHash}.",
s.TxId,
s.BlockHash
);
break;
case TxFailure f:
Store.PutTxExecution(f); // IStore.PutTxExecution(TxFailure)
_logger.Verbose(
"Updated " + nameof(TxFailure) +
" for tx {TxId} within block {BlockHash}.",
f.TxId,
f.BlockHash
);
break;
default:
// In theory, this case must not happen. The following case is for just in
// case. (For example, we might add a new subtype for TxExecution.)
const string msg = "Unexpected subtype of " + nameof(TxExecution) + ": {0}";
_logger.Fatal(msg, txExecution);
Trace.Assert(
false,
string.Format(CultureInfo.InvariantCulture, msg, txExecution)
);
break;
}
count++;
}
_logger.Verbose(
"Updated " + nameof(TxExecution) + "s for {Txs} transactions.",
count
);
}
}
}