1- using System . Collections . Generic ;
1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
24using Microsoft . Extensions . Logging ;
35using NBitcoin ;
4- using Stratis . Bitcoin . Persistence ;
6+ using Stratis . Bitcoin . Utilities ;
57
68namespace Stratis . Bitcoin . Features . PoA . Voting
79{
810 public class WhitelistedHashesRepository : IWhitelistedHashesRepository
911 {
10- private const string dbKey = "hashesList" ;
11-
12- private readonly IKeyValueRepository kvRepository ;
13-
1412 /// <summary>Protects access to <see cref="whitelistedHashes"/>.</summary>
1513 private readonly object locker ;
1614
1715 private readonly ILogger logger ;
1816
19- private List < uint256 > whitelistedHashes ;
17+ private readonly PoAConsensusOptions poaConsensusOptions ;
2018
21- public WhitelistedHashesRepository ( ILoggerFactory loggerFactory , IKeyValueRepository kvRepository )
19+ // Dictionary of hash histories. Even list entries are additions and odd entries are removals.
20+ private Dictionary < uint256 , int [ ] > whitelistedHashes ;
21+
22+ public WhitelistedHashesRepository ( ILoggerFactory loggerFactory , Network network )
2223 {
23- this . kvRepository = kvRepository ;
2424 this . locker = new object ( ) ;
2525
2626 this . logger = loggerFactory . CreateLogger ( this . GetType ( ) . FullName ) ;
27+ this . poaConsensusOptions = network . Consensus . Options as PoAConsensusOptions ;
28+ }
2729
28- // Load this before initialize to ensure its available to when the Mempool feature initializes.
29- lock ( this . locker )
30+ public class PollComparer : IComparer < ( int height , int id ) >
31+ {
32+ public int Compare ( ( int height , int id ) poll1 , ( int height , int id ) poll2 )
3033 {
31- this . whitelistedHashes = this . kvRepository . LoadValueJson < List < uint256 > > ( dbKey ) ?? new List < uint256 > ( ) ;
34+ int cmp = poll1 . height . CompareTo ( poll2 . height ) ;
35+ if ( cmp != 0 )
36+ return cmp ;
37+
38+ return poll1 . id . CompareTo ( poll2 . id ) ;
3239 }
3340 }
3441
35- public void Initialize ( )
42+ static PollComparer pollComparer = new PollComparer ( ) ;
43+
44+ private void GetWhitelistedHashesFromExecutedPolls ( VotingManager votingManager )
3645 {
46+ lock ( this . locker )
47+ {
48+ var federation = new List < IFederationMember > ( this . poaConsensusOptions . GenesisFederationMembers ) ;
49+
50+ IEnumerable < Poll > executedPolls = votingManager . GetExecutedPolls ( ) . WhitelistPolls ( ) ;
51+ foreach ( Poll poll in executedPolls . OrderBy ( a => ( a . PollExecutedBlockData . Height , a . Id ) , pollComparer ) )
52+ {
53+ var hash = new uint256 ( poll . VotingData . Data ) ;
54+
55+ if ( poll . VotingData . Key == VoteKey . WhitelistHash )
56+ {
57+ this . AddHash ( hash , poll . PollExecutedBlockData . Height ) ;
58+ }
59+ else if ( poll . VotingData . Key == VoteKey . RemoveHash )
60+ {
61+ this . RemoveHash ( hash , poll . PollExecutedBlockData . Height ) ;
62+ }
63+ }
64+ }
3765 }
3866
39- private void SaveHashes ( )
67+ public void Initialize ( VotingManager votingManager )
4068 {
69+ // TODO: Must call Initialize before the Mempool rules try to use this class.
4170 lock ( this . locker )
4271 {
43- this . kvRepository . SaveValueJson ( dbKey , this . whitelistedHashes ) ;
72+ this . whitelistedHashes = new Dictionary < uint256 , int [ ] > ( ) ;
73+ this . GetWhitelistedHashesFromExecutedPolls ( votingManager ) ;
4474 }
4575 }
4676
47- public void AddHash ( uint256 hash )
77+ public void AddHash ( uint256 hash , int executionHeight )
4878 {
4979 lock ( this . locker )
5080 {
51- if ( this . whitelistedHashes . Contains ( hash ) )
81+ // Retrieve the whitelist history for this hash.
82+ if ( ! this . whitelistedHashes . TryGetValue ( hash , out int [ ] history ) )
5283 {
53- this . logger . LogTrace ( "(-)[ALREADY_EXISTS]" ) ;
84+ this . whitelistedHashes [ hash ] = new int [ ] { executionHeight } ;
5485 return ;
5586 }
5687
57- this . whitelistedHashes . Add ( hash ) ;
88+ // Keep all history up to and including the executionHeight.
89+ int keep = BinarySearch . BinaryFindFirst ( ( k ) => k == history . Length || history [ k ] > executionHeight , 0 , history . Length + 1 ) ;
90+ Array . Resize ( ref history , keep | 1 ) ;
91+ this . whitelistedHashes [ hash ] = history ;
92+
93+ // If the history is an even length then add the addition height to signify addition.
94+ if ( ( keep % 2 ) == 0 )
95+ {
96+ // Add an even indexed entry to signify an addition.
97+ history [ keep ] = executionHeight ;
98+ return ;
99+ }
100+
101+ this . logger . LogTrace ( "(-)[HASH_ALREADY_EXISTS]" ) ;
102+ return ;
103+ }
104+ }
105+
106+ public void RemoveHash ( uint256 hash , int executionHeight )
107+ {
108+ lock ( this . locker )
109+ {
110+ // Retrieve the whitelist history for this hash.
111+ if ( this . whitelistedHashes . TryGetValue ( hash , out int [ ] history ) )
112+ {
113+ // Keep all history up to and including the executionHeight.
114+ int keep = BinarySearch . BinaryFindFirst ( ( k ) => k == history . Length || history [ k ] >= executionHeight , 0 , history . Length + 1 ) ;
115+ Array . Resize ( ref history , ( keep + 1 ) & ~ 1 ) ;
116+ this . whitelistedHashes [ hash ] = history ;
117+
118+ // If the history is an odd length then add the removal height to signify removal.
119+ if ( ( keep % 2 ) != 0 )
120+ {
121+ history [ keep ] = executionHeight ;
122+ return ;
123+ }
124+ }
58125
59- this . SaveHashes ( ) ;
126+ this . logger . LogTrace ( "(-)[HASH_DOESNT_EXIST]" ) ;
127+ return ;
60128 }
61129 }
62130
63- public void RemoveHash ( uint256 hash )
131+ private bool ExistsHash ( uint256 hash , int blockHeight )
64132 {
65133 lock ( this . locker )
66134 {
67- bool removed = this . whitelistedHashes . Remove ( hash ) ;
135+ // Retrieve the whitelist history for this hash.
136+ if ( ! this . whitelistedHashes . TryGetValue ( hash , out int [ ] history ) )
137+ return false ;
68138
69- if ( removed )
70- this . SaveHashes ( ) ;
139+ int keep = BinarySearch . BinaryFindFirst ( ( k ) => k == history . Length || history [ k ] > blockHeight , 0 , history . Length + 1 ) ;
140+ return ( keep % 2 ) != 0 ;
71141 }
72142 }
73143
74- public List < uint256 > GetHashes ( )
144+ public List < uint256 > GetHashes ( int blockHeight = int . MaxValue )
75145 {
76146 lock ( this . locker )
77147 {
78- return new List < uint256 > ( this . whitelistedHashes ) ;
148+ return this . whitelistedHashes . Where ( k => ExistsHash ( k . Key , blockHeight ) ) . Select ( k => k . Key ) . ToList ( ) ;
79149 }
80150 }
81151 }
82152
83153 public interface IWhitelistedHashesRepository
84154 {
85- void AddHash ( uint256 hash ) ;
155+ void AddHash ( uint256 hash , int executionHeight ) ;
86156
87- void RemoveHash ( uint256 hash ) ;
157+ void RemoveHash ( uint256 hash , int executionHeight ) ;
88158
89- List < uint256 > GetHashes ( ) ;
159+ List < uint256 > GetHashes ( int blockHeight = int . MaxValue ) ;
90160
91- void Initialize ( ) ;
161+ void Initialize ( VotingManager votingManager ) ;
92162 }
93- }
163+ }
0 commit comments