1#![warn(unused_crate_dependencies)]
20
21pub mod auto_id_domain_chain_spec;
22pub mod chain_spec;
23pub mod evm_domain_chain_spec;
24
25use futures::StreamExt;
26use futures::executor::block_on;
27use sc_client_api::{BlockBackend, HeaderBackend};
28use sc_consensus_subspace::archiver::encode_block;
29use sc_consensus_subspace::notification::SubspaceNotificationStream;
30use sc_consensus_subspace::slot_worker::{NewSlotNotification, RewardSigningNotification};
31use sp_api::ProvideRuntimeApi;
32use sp_consensus_subspace::SubspaceApi;
33use sp_core::{Decode, Encode};
34use std::num::{NonZeroU64, NonZeroUsize};
35use std::slice;
36use std::sync::Arc;
37use subspace_core_primitives::objects::BlockObjectMapping;
38use subspace_core_primitives::pieces::Record;
39use subspace_core_primitives::pos::PosSeed;
40use subspace_core_primitives::segments::{HistorySize, SegmentIndex};
41use subspace_core_primitives::solutions::{RewardSignature, Solution};
42use subspace_core_primitives::{PublicKey, REWARD_SIGNING_CONTEXT};
43use subspace_erasure_coding::ErasureCoding;
44use subspace_farmer_components::FarmerProtocolInfo;
45use subspace_farmer_components::auditing::audit_sector_sync;
46use subspace_farmer_components::plotting::{
47 CpuRecordsEncoder, PlotSectorOptions, PlottedSector, plot_sector,
48};
49use subspace_farmer_components::reading::ReadSectorRecordChunksMode;
50use subspace_kzg::Kzg;
51use subspace_proof_of_space::{Table, TableGenerator};
52use subspace_runtime_primitives::opaque::Block;
53use subspace_service::{FullClient, NewFull};
54use zeroize::Zeroizing;
55
56const MAX_PIECES_IN_SECTOR: u16 = 32;
58
59pub type Client = FullClient<subspace_test_runtime::RuntimeApi>;
61
62pub type Backend = sc_service::TFullBackend<Block>;
64
65pub fn start_farmer<PosTable>(new_full: &NewFull<Client>)
67where
68 PosTable: Table,
69{
70 let client = new_full.client.clone();
71 let new_slot_notification_stream = new_full.new_slot_notification_stream.clone();
72 let reward_signing_notification_stream = new_full.reward_signing_notification_stream.clone();
73
74 let keypair = schnorrkel::Keypair::generate();
75 let subspace_farming =
76 start_farming::<PosTable, _>(keypair.clone(), client, new_slot_notification_stream);
77 new_full
78 .task_manager
79 .spawn_essential_handle()
80 .spawn_blocking("subspace-farmer", Some("farming"), subspace_farming);
81
82 new_full
83 .task_manager
84 .spawn_essential_handle()
85 .spawn_blocking("subspace-farmer", Some("block-signing"), async move {
86 let substrate_ctx = schnorrkel::context::signing_context(REWARD_SIGNING_CONTEXT);
87 let signing_pair: Zeroizing<schnorrkel::Keypair> = Zeroizing::new(keypair);
88
89 let mut reward_signing_notification_stream =
90 reward_signing_notification_stream.subscribe();
91
92 while let Some(RewardSigningNotification {
93 hash: header_hash,
94 signature_sender,
95 ..
96 }) = reward_signing_notification_stream.next().await
97 {
98 let header_hash: [u8; 32] = header_hash.into();
99 let signature = RewardSignature::from(
100 signing_pair
101 .sign(substrate_ctx.bytes(&header_hash))
102 .to_bytes(),
103 );
104 signature_sender.unbounded_send(signature).unwrap();
105 }
106 });
107}
108
109async fn start_farming<PosTable, Client>(
110 keypair: schnorrkel::Keypair,
111 client: Arc<Client>,
112 new_slot_notification_stream: SubspaceNotificationStream<NewSlotNotification>,
113) where
114 PosTable: Table,
115 Client: ProvideRuntimeApi<Block>
116 + BlockBackend<Block>
117 + HeaderBackend<Block>
118 + Send
119 + Sync
120 + 'static,
121 Client::Api: SubspaceApi<Block, PublicKey>,
122{
123 let (plotting_result_sender, plotting_result_receiver) = futures::channel::oneshot::channel();
124
125 let kzg = Kzg::new();
126 let erasure_coding = ErasureCoding::new(
127 NonZeroUsize::new(Record::NUM_S_BUCKETS.next_power_of_two().ilog2() as usize)
128 .expect("Not zero; qed"),
129 )
130 .unwrap();
131
132 let table_generator = PosTable::generator();
133
134 std::thread::spawn({
135 let keypair = keypair.clone();
136 let erasure_coding = erasure_coding.clone();
137
138 move || {
139 let (sector, sector_metadata, table_generator) =
140 block_on(plot_one_segment::<PosTable, _>(
141 client.as_ref(),
142 &keypair,
143 MAX_PIECES_IN_SECTOR,
144 &erasure_coding,
145 table_generator,
146 ));
147 plotting_result_sender
148 .send((sector, sector_metadata, table_generator))
149 .unwrap();
150 }
151 });
152
153 let (sector, plotted_sector, mut table_generator) = plotting_result_receiver.await.unwrap();
154 let public_key = PublicKey::from(keypair.public.to_bytes());
155
156 let mut new_slot_notification_stream = new_slot_notification_stream.subscribe();
157
158 while let Some(NewSlotNotification {
159 new_slot_info,
160 mut solution_sender,
161 }) = new_slot_notification_stream.next().await
162 {
163 if u64::from(new_slot_info.slot) % 2 == 0 {
164 let global_challenge = new_slot_info
165 .proof_of_time
166 .derive_global_randomness()
167 .derive_global_challenge(new_slot_info.slot.into());
168 let audit_result = audit_sector_sync(
169 &public_key,
170 &global_challenge,
171 new_slot_info.solution_range,
172 §or,
173 &plotted_sector.sector_metadata,
174 );
175
176 let solution = audit_result
177 .unwrap()
178 .unwrap()
179 .solution_candidates
180 .into_solutions(
181 &public_key,
182 &kzg,
183 &erasure_coding,
184 ReadSectorRecordChunksMode::ConcurrentChunks,
185 |seed: &PosSeed| table_generator.generate_parallel(seed),
186 )
187 .unwrap()
188 .next()
189 .expect("With max solution range there must be a solution; qed")
190 .unwrap();
191 let solution = Solution::decode(&mut solution.encode().as_slice()).unwrap();
193 let _ = solution_sender.try_send(solution);
194 }
195 }
196}
197
198async fn plot_one_segment<PosTable, Client>(
199 client: &Client,
200 keypair: &schnorrkel::Keypair,
201 pieces_in_sector: u16,
202 erasure_coding: &ErasureCoding,
203 mut table_generator: PosTable::Generator,
204) -> (Vec<u8>, PlottedSector, PosTable::Generator)
205where
206 PosTable: Table,
207 Client: BlockBackend<Block> + HeaderBackend<Block>,
208{
209 let kzg = Kzg::new();
210 let mut archiver =
211 subspace_archiving::archiver::Archiver::new(kzg.clone(), erasure_coding.clone());
212
213 let genesis_block = client.block(client.info().genesis_hash).unwrap().unwrap();
214 let archived_segment = archiver
215 .add_block(
216 encode_block(genesis_block),
217 BlockObjectMapping::default(),
218 true,
219 )
220 .archived_segments
221 .into_iter()
222 .next()
223 .expect("First block is always producing one segment; qed");
224 let history_size = HistorySize::from(SegmentIndex::ZERO);
225 let mut sector = Vec::new();
226 let sector_index = 0;
227 let public_key = PublicKey::from(keypair.public.to_bytes());
228 let farmer_protocol_info = FarmerProtocolInfo {
229 history_size,
230 max_pieces_in_sector: pieces_in_sector,
231 recent_segments: HistorySize::from(NonZeroU64::new(5).unwrap()),
232 recent_history_fraction: (
233 HistorySize::from(NonZeroU64::new(1).unwrap()),
234 HistorySize::from(NonZeroU64::new(10).unwrap()),
235 ),
236 min_sector_lifetime: HistorySize::from(NonZeroU64::new(4).unwrap()),
237 };
238
239 let plotted_sector = plot_sector(PlotSectorOptions {
240 public_key: &public_key,
241 sector_index,
242 piece_getter: &archived_segment,
243 farmer_protocol_info,
244 kzg: &kzg,
245 erasure_coding,
246 pieces_in_sector,
247 sector_output: &mut sector,
248 downloading_semaphore: None,
249 encoding_semaphore: None,
250 records_encoder: &mut CpuRecordsEncoder::<PosTable>::new(
251 slice::from_mut(&mut table_generator),
252 erasure_coding,
253 &Default::default(),
254 ),
255 abort_early: &Default::default(),
256 })
257 .await
258 .expect("Plotting one sector in memory must not fail");
259
260 (sector, plotted_sector, table_generator)
261}