-
Notifications
You must be signed in to change notification settings - Fork 479
/
Copy pathTransaction.cs
333 lines (288 loc) · 11.6 KB
/
Transaction.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only
using System;
using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json.Serialization;
using Microsoft.Extensions.ObjectPool;
using Nethermind.Core.Crypto;
using Nethermind.Core.Eip2930;
using Nethermind.Core.Extensions;
using Nethermind.Int256;
[assembly: InternalsVisibleTo("Nethermind.Consensus")]
namespace Nethermind.Core
{
[DebuggerDisplay("{Hash}, Value: {Value}, To: {To}, Gas: {GasLimit}")]
public class Transaction
{
public const int BaseTxGasCost = 21000;
public ulong? ChainId { get; set; }
/// <summary>
/// EIP-2718 transaction type
/// </summary>
public TxType Type { get; set; }
// Optimism deposit transaction fields
// SourceHash uniquely identifies the source of the deposit
public Hash256? SourceHash { get; set; }
// Mint is minted on L2, locked on L1, nil if no minting.
public UInt256 Mint { get; set; }
// Field indicating if this transaction is exempt from the L2 gas limit.
public bool IsOPSystemTransaction { get; set; }
public UInt256 Nonce { get; set; }
public UInt256 GasPrice { get; set; }
public UInt256? GasBottleneck { get; set; }
public UInt256 MaxPriorityFeePerGas => GasPrice;
public UInt256 DecodedMaxFeePerGas { get; set; }
public UInt256 MaxFeePerGas => Supports1559 ? DecodedMaxFeePerGas : GasPrice;
public bool SupportsAccessList => Type >= TxType.AccessList && Type != TxType.DepositTx;
public bool Supports1559 => Type >= TxType.EIP1559 && Type != TxType.DepositTx;
public bool SupportsBlobs => Type == TxType.Blob && Type != TxType.DepositTx;
public long GasLimit { get; set; }
public Address? To { get; set; }
public UInt256 Value { get; set; }
public Memory<byte>? Data { get; set; }
public Address? SenderAddress { get; set; }
public Signature? Signature { get; set; }
public bool IsSigned => Signature is not null;
public bool IsContractCreation => To is null;
public bool IsMessageCall => To is not null;
private Hash256? _hash;
[JsonIgnore]
internal bool IsHashCalculated => _hash is not null;
internal Hash256 CalculateHashInternal()
{
Hash256? hash = _hash;
if (hash is not null) return hash;
lock (this)
{
hash = _hash;
if (hash is not null) return hash;
if (_preHash.Length > 0)
{
_hash = hash = Keccak.Compute(_preHash.Span);
ClearPreHashInternal();
}
}
return hash!;
}
public Hash256? Hash
{
get
{
Hash256? hash = _hash;
if (hash is not null) return hash;
return CalculateHashInternal();
}
set
{
lock (this)
{
ClearPreHash();
_hash = value;
}
}
}
private Memory<byte> _preHash;
private IMemoryOwner<byte>? _preHashMemoryOwner;
public void SetPreHash(ReadOnlySpan<byte> transactionSequence)
{
lock (this)
{
SetPreHashNoLock(transactionSequence);
}
}
public void SetPreHashNoLock(ReadOnlySpan<byte> transactionSequence)
{
// Used to delay hash generation, as may be filtered as having too low gas etc
_hash = null;
int size = transactionSequence.Length;
_preHashMemoryOwner = MemoryPool<byte>.Shared.Rent(size);
_preHash = _preHashMemoryOwner.Memory[..size];
transactionSequence.CopyTo(_preHash.Span);
}
public void SetPreHashMemoryNoLock(Memory<byte> transactionSequence, IMemoryOwner<byte>? preHashMemoryOwner = null)
{
// Used to delay hash generation, as may be filtered as having too low gas etc
_hash = null;
_preHash = transactionSequence;
_preHashMemoryOwner = preHashMemoryOwner;
}
public void ClearPreHash()
{
if (_preHash.Length > 0)
{
lock (this)
{
ClearPreHashInternal();
}
}
}
private void ClearPreHashInternal()
{
if (_preHash.Length > 0)
{
_preHashMemoryOwner?.Dispose();
_preHashMemoryOwner = null;
_preHash = default;
}
}
public UInt256 Timestamp { get; set; }
public int DataLength => Data?.Length ?? 0;
public AccessList? AccessList { get; set; } // eip2930
public UInt256? MaxFeePerBlobGas { get; set; } // eip4844
public byte[]?[]? BlobVersionedHashes { get; set; } // eip4844
public object? NetworkWrapper { get; set; }
/// <summary>
/// Service transactions are free. The field added to handle baseFee validation after 1559
/// </summary>
/// <remarks>Used for AuRa consensus.</remarks>
public bool IsServiceTransaction { get; set; }
/// <summary>
/// In-memory only property, representing order of transactions going to TxPool.
/// </summary>
/// <remarks>Used for sorting in edge cases.</remarks>
public ulong PoolIndex { get; set; }
protected int? _size = null;
/// <summary>
/// Encoded transaction length
/// </summary>
public int GetLength(ITransactionSizeCalculator sizeCalculator)
{
return _size ??= sizeCalculator.GetLength(this);
}
public string ToShortString()
{
string gasPriceString =
Supports1559 ? $"maxPriorityFeePerGas: {MaxPriorityFeePerGas}, MaxFeePerGas: {MaxFeePerGas}" : $"gas price {GasPrice}";
return $"[TX: hash {Hash} from {SenderAddress} to {To} with data {Data.AsArray()?.ToHexString()}, {gasPriceString} and limit {GasLimit}, nonce {Nonce}]";
}
public string ToString(string indent)
{
StringBuilder builder = new();
builder.AppendLine($"{indent}Hash: {Hash}");
builder.AppendLine($"{indent}From: {SenderAddress}");
builder.AppendLine($"{indent}To: {To}");
builder.AppendLine($"{indent}TxType: {Type}");
if (Supports1559)
{
builder.AppendLine($"{indent}MaxPriorityFeePerGas: {MaxPriorityFeePerGas}");
builder.AppendLine($"{indent}MaxFeePerGas: {MaxFeePerGas}");
}
else
{
builder.AppendLine($"{indent}Gas Price: {GasPrice}");
}
builder.AppendLine($"{indent}SourceHash: {SourceHash}");
builder.AppendLine($"{indent}Mint: {Mint}");
builder.AppendLine($"{indent}OpSystem: {IsOPSystemTransaction}");
builder.AppendLine($"{indent}Gas Limit: {GasLimit}");
builder.AppendLine($"{indent}Nonce: {Nonce}");
builder.AppendLine($"{indent}Value: {Value}");
builder.AppendLine($"{indent}Data: {(Data.AsArray() ?? Array.Empty<byte>()).ToHexString()}");
builder.AppendLine($"{indent}Signature: {(Signature?.Bytes ?? Array.Empty<byte>()).ToHexString()}");
builder.AppendLine($"{indent}V: {Signature?.V}");
builder.AppendLine($"{indent}ChainId: {Signature?.ChainId}");
builder.AppendLine($"{indent}Timestamp: {Timestamp}");
if (SupportsBlobs)
{
builder.AppendLine($"{indent}{nameof(MaxFeePerBlobGas)}: {MaxFeePerBlobGas}");
builder.AppendLine($"{indent}{nameof(BlobVersionedHashes)}: {BlobVersionedHashes?.Length}");
}
return builder.ToString();
}
public override string ToString() => ToString(string.Empty);
public bool MayHaveNetworkForm => Type is TxType.Blob;
public class PoolPolicy : IPooledObjectPolicy<Transaction>
{
public Transaction Create()
{
return new Transaction();
}
public bool Return(Transaction obj)
{
obj.ClearPreHash();
obj.Hash = default;
obj.ChainId = default;
obj.Type = default;
obj.Nonce = default;
obj.GasPrice = default;
obj.GasBottleneck = default;
obj.DecodedMaxFeePerGas = default;
obj.GasLimit = default;
obj.To = default;
obj.Value = default;
obj.Data = default;
obj.SenderAddress = default;
obj.Signature = default;
obj.Timestamp = default;
obj.AccessList = default;
obj.MaxFeePerBlobGas = default;
obj.BlobVersionedHashes = default;
obj.NetworkWrapper = default;
obj.IsServiceTransaction = default;
obj.PoolIndex = default;
obj._size = default;
return true;
}
}
public void CopyTo(Transaction tx)
{
tx.ChainId = ChainId;
tx.Type = Type;
tx.SourceHash = SourceHash;
tx.Mint = Mint;
tx.IsOPSystemTransaction = IsOPSystemTransaction;
tx.Nonce = Nonce;
tx.GasPrice = GasPrice;
tx.GasBottleneck = GasBottleneck;
tx.DecodedMaxFeePerGas = DecodedMaxFeePerGas;
tx.GasLimit = GasLimit;
tx.To = To;
tx.Value = Value;
tx.Data = Data;
tx.SenderAddress = SenderAddress;
tx.Signature = Signature;
tx.Timestamp = Timestamp;
tx.AccessList = AccessList;
tx.MaxFeePerBlobGas = MaxFeePerBlobGas;
tx.BlobVersionedHashes = BlobVersionedHashes;
tx.NetworkWrapper = NetworkWrapper;
tx.IsServiceTransaction = IsServiceTransaction;
tx.PoolIndex = PoolIndex;
tx._size = _size;
}
}
/// <summary>
/// Transaction that is generated by the node to be included in future block. After included in the block can be handled as regular <see cref="Transaction"/>.
/// </summary>
public class GeneratedTransaction : Transaction { }
/// <summary>
/// System transaction that is to be executed by the node without including in the block.
/// </summary>
public class SystemTransaction : Transaction { }
/// <summary>
/// Used inside Transaction::GetSize to calculate encoded transaction size
/// </summary>
/// <remarks>Created because of cyclic dependencies between Core and Rlp modules</remarks>
public interface ITransactionSizeCalculator
{
int GetLength(Transaction tx);
}
/// <summary>
/// Holds network form fields for <see cref="TxType.Blob" /> transactions
/// </summary>
public class ShardBlobNetworkWrapper
{
public ShardBlobNetworkWrapper(byte[][] blobs, byte[][] commitments, byte[][] proofs)
{
Blobs = blobs;
Commitments = commitments;
Proofs = proofs;
}
public byte[][] Commitments { get; set; }
public byte[][] Blobs { get; set; }
public byte[][] Proofs { get; set; }
}
}