1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
4+ using Blockcore . Consensus . ScriptInfo ;
5+ using Blockcore . Networks . Strax . ScriptTemplates ;
6+ using NBitcoin ;
7+
8+ namespace Blockcore . Networks . Strax . Federation
9+ {
10+ public interface IFederation
11+ {
12+ Script MultisigScript { get ; }
13+ FederationId Id { get ; }
14+
15+ ( PubKey [ ] transactionSigningKeys , int signaturesRequired ) GetFederationDetails ( ) ;
16+ }
17+
18+ /// <summary>
19+ /// Compares two byte arrays for equality.
20+ /// </summary>
21+ public sealed class ByteArrayComparer : IEqualityComparer < byte [ ] > , IComparer < byte [ ] >
22+ {
23+ public int Compare ( byte [ ] first , byte [ ] second )
24+ {
25+ int firstLen = first ? . Length ?? - 1 ;
26+ int secondLen = second ? . Length ?? - 1 ;
27+ int commonLen = Math . Min ( firstLen , secondLen ) ;
28+
29+ for ( int i = 0 ; i < commonLen ; i ++ )
30+ {
31+ if ( first [ i ] == second [ i ] )
32+ continue ;
33+
34+ return ( first [ i ] < second [ i ] ) ? - 1 : 1 ;
35+ }
36+
37+ return firstLen . CompareTo ( secondLen ) ;
38+ }
39+
40+ public bool Equals ( byte [ ] first , byte [ ] second )
41+ {
42+ return this . Compare ( first , second ) == 0 ;
43+ }
44+
45+ public int GetHashCode ( byte [ ] obj )
46+ {
47+ ulong hash = 17 ;
48+
49+ foreach ( byte objByte in obj )
50+ {
51+ hash = ( hash << 5 ) - hash + objByte ;
52+ }
53+
54+ return ( int ) hash ;
55+ }
56+ }
57+
58+ public class FederationId : IBitcoinSerializable
59+ {
60+ byte [ ] federationId ;
61+ ByteArrayComparer comparer ;
62+
63+ public FederationId ( )
64+ {
65+ }
66+
67+ public FederationId ( byte [ ] value )
68+ {
69+ this . federationId = value ;
70+ this . comparer = new ByteArrayComparer ( ) ;
71+ }
72+
73+ public void ReadWrite ( BitcoinStream s )
74+ {
75+ s . ReadWrite ( ref this . federationId ) ;
76+ }
77+
78+ public override bool Equals ( object obj )
79+ {
80+ return this . comparer . Equals ( ( ( FederationId ) obj ) . federationId , this . federationId ) ;
81+ }
82+
83+ public override int GetHashCode ( )
84+ {
85+ return this . comparer . GetHashCode ( this . federationId ) ;
86+ }
87+ }
88+
89+ public class Federation : IFederation
90+ {
91+ private PubKey [ ] transactionSigningKeys ;
92+
93+ private int signaturesRequired ;
94+
95+ public Script MultisigScript { get ; private set ; }
96+
97+ public FederationId Id { get ; private set ; }
98+
99+ /// <summary>
100+ /// Creates a new federation from a set of transaction signing keys.
101+ /// </summary>
102+ /// <param name="transactionSigningPubKeys">A list of transaction signing PubKeys.</param>
103+ /// <param name="signaturesRequired">The amount of signatures required to ensure that the transaction is fully signed.</param>
104+ public Federation ( IEnumerable < PubKey > transactionSigningPubKeys , int ? signaturesRequired = null )
105+ {
106+ // Ensures that the federation id will always map to the same members in the same order.
107+ this . transactionSigningKeys = transactionSigningPubKeys . OrderBy ( k => k . ToHex ( ) ) . ToArray ( ) ;
108+ this . signaturesRequired = signaturesRequired ?? ( this . transactionSigningKeys . Length + 1 ) / 2 ;
109+
110+ // The federationId is derived by XOR'ing all the genesis federation members.
111+ byte [ ] federationId = this . transactionSigningKeys . First ( ) . ToBytes ( ) ;
112+ foreach ( PubKey pubKey in this . transactionSigningKeys . Skip ( 1 ) )
113+ {
114+ byte [ ] pubKeyBytes = pubKey . ToBytes ( ) ;
115+ for ( int i = 0 ; i < federationId . Length ; i ++ )
116+ federationId [ i ] ^= pubKeyBytes [ i ] ;
117+ }
118+
119+ this . Id = new FederationId ( federationId ) ;
120+ this . MultisigScript = PayToFederationTemplate . Instance . GenerateScriptPubKey ( this . Id ) ;
121+ }
122+
123+ public ( PubKey [ ] transactionSigningKeys , int signaturesRequired ) GetFederationDetails ( )
124+ {
125+ // Until dynamic membership is implemented we just return the genesis members.
126+ return ( this . transactionSigningKeys , this . signaturesRequired ) ;
127+ }
128+ }
129+
130+ public interface IFederations
131+ {
132+ /// <summary>
133+ /// Registers a new federation with transaction signing keys.
134+ /// </summary>
135+ /// <param name="federation">The federation to be registered.</param>
136+ void RegisterFederation ( IFederation federation ) ;
137+
138+ IFederation GetFederation ( FederationId federationId ) ;
139+
140+ IFederation GetFederation ( byte [ ] federationId ) ;
141+
142+ IFederation GetOnlyFederation ( ) ;
143+ }
144+
145+ public class Federations : IFederations
146+ {
147+ private readonly Dictionary < FederationId , IFederation > federations ;
148+
149+ public Federations ( )
150+ {
151+ this . federations = new Dictionary < FederationId , IFederation > ( ) ;
152+ }
153+
154+ public IFederation GetFederation ( FederationId federationId )
155+ {
156+ return this . federations . TryGetValue ( federationId , out IFederation federation ) ? federation : null ;
157+ }
158+
159+ public IFederation GetFederation ( byte [ ] federationId )
160+ {
161+ return this . federations . TryGetValue ( new FederationId ( federationId ) , out IFederation federation ) ? federation : null ;
162+ }
163+
164+ // TODO: Deprectate this method when multiple federations are supported.
165+ public IFederation GetOnlyFederation ( )
166+ {
167+ return this . federations . First ( ) . Value ;
168+ }
169+
170+ public void RegisterFederation ( IFederation federation )
171+ {
172+ // TODO: Remove this when multiple federations are supported.
173+ this . federations . Clear ( ) ;
174+
175+ this . federations [ federation . Id ] = federation ;
176+ }
177+ }
178+ }
0 commit comments