1+ using System ;
2+ using System . Threading . Tasks ;
3+ using Blockcore . AsyncWork ;
4+ using Blockcore . Interfaces ;
5+ using Blockcore . P2P . Peer ;
6+ using Blockcore . P2P . Protocol ;
7+ using Blockcore . P2P . Protocol . Behaviors ;
8+ using Blockcore . Utilities ;
9+ using Microsoft . Extensions . Logging ;
10+ using NBitcoin ;
11+ using NBitcoin . Protocol ;
12+
13+ namespace Blockcore . Features . MemoryPool . FeeFilter
14+ {
15+ /// <summary>
16+ /// Implementing FeeFilter as described in BIP0133
17+ /// https://github.com/bitcoin/bips/blob/master/bip-0133.mediawiki
18+ /// </summary>
19+ public class FeeFilterBehavior : NetworkPeerBehavior
20+ {
21+ private readonly ILogger logger ;
22+ private readonly Network network ;
23+ private readonly MempoolSettings settings ;
24+ private readonly MempoolManager mempoolManager ;
25+ private readonly IInitialBlockDownloadState initialBlockDownloadState ;
26+ private readonly ILoggerFactory loggerFactory ;
27+ private readonly INodeLifetime nodeLifetime ;
28+ private readonly IAsyncProvider asyncProvider ;
29+ private MempoolBehavior mempoolBehavior ;
30+ private IAsyncLoop asyncLoop ;
31+
32+ private Money lastSendFilter ;
33+
34+ public FeeFilterBehavior (
35+ Network network ,
36+ MempoolSettings settings ,
37+ MempoolManager mempoolManager ,
38+ IInitialBlockDownloadState initialBlockDownloadState ,
39+ ILoggerFactory loggerFactory ,
40+ INodeLifetime nodeLifetime ,
41+ IAsyncProvider asyncProvider )
42+ {
43+ this . network = network ;
44+ this . settings = settings ;
45+ this . mempoolManager = mempoolManager ;
46+ this . initialBlockDownloadState = initialBlockDownloadState ;
47+ this . loggerFactory = loggerFactory ;
48+ this . nodeLifetime = nodeLifetime ;
49+ this . asyncProvider = asyncProvider ;
50+
51+ this . logger = loggerFactory . CreateLogger ( this . GetType ( ) . FullName ) ;
52+ }
53+
54+ private async Task ProcessFeeFilterAsync ( INetworkPeer peer , FeeFilterPayload feeFilter )
55+ {
56+ if ( peer . PeerVersion . Relay )
57+ {
58+ if ( feeFilter . NewFeeFilter > 0 )
59+ {
60+ if ( this . mempoolBehavior == null )
61+ this . mempoolBehavior = peer . Behavior < MempoolBehavior > ( ) ;
62+
63+ this . mempoolBehavior . MinFeeFilter = feeFilter . NewFeeFilter ;
64+
65+ this . logger . LogDebug ( "Received feefilter of `{0}` from peer id: {1}" , feeFilter . NewFeeFilter , peer . Connection . Id ) ;
66+ }
67+ }
68+ }
69+
70+ private void StartFeeFilterBroadcast ( INetworkPeer sender )
71+ {
72+ if ( this . settings . FeeFilter )
73+ {
74+ INetworkPeer peer = sender ;
75+
76+ if ( peer . PeerVersion != null
77+ && peer . PeerVersion . Relay
78+ && peer . PeerVersion . Version >= ProtocolVersion . FEEFILTER_VERSION )
79+ {
80+ if ( this . asyncLoop != null )
81+ {
82+ this . asyncLoop = this . asyncProvider . CreateAndRunAsyncLoop ( $ "MemoryPool.FeeFilter:{ peer . Connection . Id } ", async token =>
83+ {
84+ if ( this . initialBlockDownloadState . IsInitialBlockDownload ( ) )
85+ {
86+ return ;
87+ }
88+
89+ var feeRate = await this . mempoolManager . GetMempoolMinFeeAsync ( MempoolValidator . DefaultMaxMempoolSize * 1000000 ) . ConfigureAwait ( false ) ;
90+ var currentFilter = feeRate . FeePerK ;
91+
92+ // We always have a fee filter of at least minRelayTxFee
93+ Money filterToSend = Math . Max ( currentFilter , new FeeRate ( this . network . MinRelayTxFee ) . FeePerK ) ;
94+
95+ if ( filterToSend != this . lastSendFilter )
96+ {
97+ INetworkPeer peer = this . AttachedPeer ;
98+
99+ if ( peer != null && peer . IsConnected )
100+ {
101+ this . logger . LogDebug ( "Sending for transaction data from peer '{0}'." , peer . RemoteSocketEndpoint ) ;
102+ var filterPayload = new FeeFilterPayload ( ) { NewFeeFilter = filterToSend } ;
103+ await peer . SendMessageAsync ( filterPayload ) . ConfigureAwait ( false ) ;
104+ this . lastSendFilter = filterToSend ;
105+ }
106+ }
107+ } ,
108+ this . nodeLifetime . ApplicationStopping ,
109+ repeatEvery : TimeSpan . FromMinutes ( 10 ) ,
110+ startAfter : TimeSpans . TenSeconds ) ;
111+ }
112+ }
113+ }
114+ }
115+
116+ private async Task ProcessMessageAsync ( INetworkPeer peer , IncomingMessage message )
117+ {
118+ try
119+ {
120+ switch ( message . Message . Payload )
121+ {
122+ case FeeFilterPayload feeFilter :
123+ await this . ProcessFeeFilterAsync ( peer , feeFilter ) . ConfigureAwait ( false ) ;
124+ break ;
125+ }
126+ }
127+ catch ( OperationCanceledException )
128+ {
129+ this . logger . LogTrace ( "(-)[CANCELED_EXCEPTION]" ) ;
130+ return ;
131+ }
132+ }
133+
134+ private async Task OnMessageReceivedAsync ( INetworkPeer peer , IncomingMessage message )
135+ {
136+ try
137+ {
138+ await this . ProcessMessageAsync ( peer , message ) . ConfigureAwait ( false ) ;
139+ }
140+ catch ( OperationCanceledException )
141+ {
142+ this . logger . LogTrace ( "(-)[CANCELED_EXCEPTION]" ) ;
143+ return ;
144+ }
145+ catch ( Exception ex )
146+ {
147+ this . logger . LogError ( "Exception occurred: {0}" , ex . ToString ( ) ) ;
148+ throw ;
149+ }
150+ }
151+
152+ public override object Clone ( )
153+ {
154+ return new FeeFilterBehavior (
155+ this . network ,
156+ this . settings ,
157+ this . mempoolManager ,
158+ this . initialBlockDownloadState ,
159+ this . loggerFactory ,
160+ this . nodeLifetime ,
161+ this . asyncProvider ) ;
162+ }
163+
164+ protected override void AttachCore ( )
165+ {
166+ this . AttachedPeer . StateChanged . Register ( this . OnStateChangedAsync ) ;
167+ this . AttachedPeer . MessageReceived . Register ( this . OnMessageReceivedAsync ) ;
168+ }
169+
170+ private async Task OnStateChangedAsync ( INetworkPeer sender , NetworkPeerState arg )
171+ {
172+ if ( arg == NetworkPeerState . HandShaked )
173+ {
174+ this . StartFeeFilterBroadcast ( sender ) ;
175+ }
176+
177+ if ( arg == NetworkPeerState . Disconnecting )
178+ {
179+ if ( this . asyncLoop != null )
180+ {
181+ this . asyncLoop ? . Dispose ( ) ;
182+ this . asyncLoop = null ;
183+ }
184+ }
185+ }
186+
187+ protected override void DetachCore ( )
188+ {
189+ this . AttachedPeer . StateChanged . Unregister ( this . OnStateChangedAsync ) ;
190+ this . AttachedPeer . MessageReceived . Unregister ( this . OnMessageReceivedAsync ) ;
191+ }
192+ }
193+ }
0 commit comments