1use std::path::{Path, PathBuf};
25use std::time::{SystemTime, UNIX_EPOCH};
26
27use bitcoin::consensus::encode::deserialize;
28use bitcoin::secp256k1::SecretKey;
29use bitcoin::BlockHash;
30
31use crate::block::{HeaderFamily, SidestrBlock, Stock};
32use crate::blockfile::{append_block, read_block, read_index, write_index, Index};
33use crate::document::ChainDocument;
34use crate::error::{Error, Result};
35use crate::state::{Applied, ClaimRequest, NextBlock, StateOf, Submitted};
36
37#[derive(Debug)]
39pub struct ChainOf<F: HeaderFamily> {
40 state: StateOf<F>,
41 dir: PathBuf,
42 index: Index,
43}
44
45pub type Chain = ChainOf<Stock>;
47
48pub fn now() -> u32 {
50 SystemTime::now()
51 .duration_since(UNIX_EPOCH)
52 .map(|d| d.as_secs() as u32)
53 .unwrap_or(0)
54}
55
56impl<F: HeaderFamily> ChainOf<F> {
57 pub fn open(
62 doc: ChainDocument,
63 dir: impl AsRef<Path>,
64 key: Option<&SecretKey>,
65 ) -> Result<Self> {
66 doc.validate()?;
67 let federated = doc.signers.is_some();
68 Self::open_with(doc, dir, |doc| {
69 let key = key.ok_or_else(|| {
70 Error::Chain("no chain on disk and no key to make the genesis".into())
71 })?;
72 if federated {
73 return Err(Error::Federation(
74 "a federated chain's genesis needs k signatures: open_sealed(doc, dir, seal)"
75 .into(),
76 ));
77 }
78 StateOf::<F>::genesis_block_for(doc, key)
79 })
80 }
81
82 pub fn open_sealed(
88 doc: ChainDocument,
89 dir: impl AsRef<Path>,
90 seal: impl FnOnce(&F::Block) -> Result<F::Block>,
91 ) -> Result<Self> {
92 doc.validate()?;
93 Self::open_with(doc, dir, |doc| seal(&StateOf::<F>::build_genesis_for(doc)?))
94 }
95
96 fn open_with(
97 doc: ChainDocument,
98 dir: impl AsRef<Path>,
99 genesis: impl FnOnce(&ChainDocument) -> Result<F::Block>,
100 ) -> Result<Self> {
101 StateOf::<F>::family_of(&doc)?;
102 let dir = dir.as_ref().to_path_buf();
103 std::fs::create_dir_all(&dir)?;
104 let dat = dir.join("blocks.dat");
105 let idx = dir.join("blocks.json");
106 let (state, index) = match read_index(&idx)? {
107 None => {
108 let genesis = genesis(&doc)?;
109 let state = StateOf::<F>::from_genesis(doc, &genesis, None)?;
110 let mut index = Index::new(&state.document().id);
111 append_block(
112 &dat,
113 &mut index,
114 0,
115 &state.genesis_hash().to_string(),
116 &genesis.encode(),
117 )?;
118 write_index(&idx, &index)?;
119 (state, index)
120 }
121 Some(index) => {
122 let first = index
123 .blocks
124 .first()
125 .ok_or_else(|| Error::Chain("the block file has no genesis".into()))?;
126 if first.height != 0 {
127 return Err(Error::Chain(format!(
128 "the block file starts at {}, not the genesis",
129 first.height
130 )));
131 }
132 let genesis = F::Block::decode(&read_block(&dat, first)?)?;
133 let mut state = StateOf::<F>::from_genesis(
134 doc,
135 &genesis,
136 Some(
137 first
138 .hash
139 .parse()
140 .map_err(|_| Error::Encoding("bad hash in the index".into()))?,
141 ),
142 )?;
143 for e in &index.blocks[1..] {
144 let expect: BlockHash = e
145 .hash
146 .parse()
147 .map_err(|_| Error::Encoding("bad hash in the index".into()))?;
148 let block = F::Block::decode(&read_block(&dat, e)?)?;
149 state.apply(e.height, &block, Some(expect), Some(now()))?;
150 }
151 (state, index)
152 }
153 };
154 Ok(Self { state, dir, index })
155 }
156
157 pub fn state(&self) -> &StateOf<F> {
159 &self.state
160 }
161 pub fn index(&self) -> &Index {
163 &self.index
164 }
165 pub fn dat_path(&self) -> PathBuf {
167 self.dir.join("blocks.dat")
168 }
169 pub fn index_path(&self) -> PathBuf {
171 self.dir.join("blocks.json")
172 }
173
174 fn write(&mut self, height: u32, hash: BlockHash, bytes: &[u8]) -> Result<()> {
175 append_block(
176 self.dat_path(),
177 &mut self.index,
178 height,
179 &hash.to_string(),
180 bytes,
181 )?;
182 write_index(self.index_path(), &self.index)
183 }
184
185 pub fn add_block(&mut self, bytes: &[u8], expect: Option<BlockHash>) -> Result<Applied> {
187 let r = self.state.add_block_bytes(bytes, expect, Some(now()))?;
188 self.write(r.height, r.hash, bytes)?;
189 Ok(r)
190 }
191
192 pub fn submit(&mut self, tx_bytes: &[u8]) -> Result<Submitted> {
194 let tx = deserialize(tx_bytes).map_err(|e| Error::Encoding(e.to_string()))?;
195 self.state.submit(tx)
196 }
197
198 pub fn produce(&mut self, key: &SecretKey, claims: Vec<ClaimRequest>) -> Result<Applied> {
201 let t = now();
202 let (r, block) = self
203 .state
204 .produce(key, &NextBlock { time: t, claims }, Some(t))?;
205 self.write(r.height, r.hash, &block.encode())?;
206 Ok(r)
207 }
208}