1#include "cement.h"
2
3
4Cementer cement;
5static CCriticalSection cs_cement;
6
7
8bool Cementer::generate(int start_height, int stop_height, string filename)
9{
10 // Check that we actually have blocks to this height with safety margin:
11 if (stop_height > nBestHeight - SAFE_CEMENT_MARGIN)
12 throw runtime_error(strprintf(_("Requested cement exceeds nBestHeight - %d!\n"),
13 SAFE_CEMENT_MARGIN));
14
15 // Open destination cement file
16 FILE *fileout = fopen(filename.c_str(), "w");
17 if (!fileout)
18 throw runtime_error(strprintf(_("Cannot write cement to file: '%s'\n"), filename.c_str()));
19
20 // Starting at the genesis:
21 int height = 0;
22 CBlockIndex *pindex = pindexGenesisBlock;
23
24 // Up through the desired height:
25 while (height < stop_height) {
26 // Get current block height
27 height = pindex->nHeight;
28
29 // Main-chain blocks strictly, at or above the start height:
30 if (pindex->IsInMainChain() && height >= start_height) {
31 // Get the block's hash
32 uint256 hash = pindex->GetBlockHash();
33
34 // Write block's height and header hash to the cement file
35 fprintf(fileout, "%d %s\n", height, hash.ToString().c_str());
36 }
37
38 // Advance to next block
39 pindex = pindex->pnext;
40 }
41
42 fclose(fileout);
43 // We're done:
44 printf("Cement written to %s.\n", filename.c_str());
45 return true;
46}
47
48
49void Cementer::permitCement()
50{
51 mayLoadCement = true;
52}
53
54
55bool Cementer::cementPermitted()
56{
57 return mayLoadCement;
58}
59
60
61void Cementer::unload()
62{
63 CRITICAL_BLOCK(cs_cement)
64 {
65 entries.clear();
66 }
67}
68
69
70bool Cementer::load(string filename)
71{
72 if (!mayLoadCement)
73 throw runtime_error("'loadcement' is only permitted if bitcoind was started with -cement flag!");
74
75 int foundEntries = 0;
76 ifstream cementFile(filename.c_str());
77
78 if (cementFile.bad())
79 throw runtime_error(strprintf(_("Cannot open cement file: '%s'\n"), filename.c_str()));
80
81 CRITICAL_BLOCK(cs_cement)
82 {
83 string line;
84 while(getline(cementFile, line)) {
85 stringstream lineStream(line);
86 int height;
87 string hexHash;
88 lineStream >> height;
89 bool validEntry = lineStream.good();
90 lineStream >> hexHash;
91 int hexHashLen = hexHash.length();
92 if (hexHashLen != 64)
93 validEntry = false;
94 for (size_t i = 0, n = hexHashLen; i < n; ++i) {
95 if (!isxdigit(hexHash[i])) validEntry = false;
96 }
97 // Sanity check: height must always be 1 plus that of prev. entry:
98 if (((!entries.empty()) && (height != entries.back().height + 1)) || !validEntry) {
99 printf("Invalid cement entry: '%s'\n", line.c_str());
100 } else {
101 // Entry is valid, add it to current cement:
102 uint256 hash = uint256(hexHash);
103 CementEntry e;
104 e.height = height;
105 e.hash = hash;
106 entries.push_back(e);
107 foundEntries++;
108 }
109 }
110 }
111 printf("%d cement entries loaded from %s.\n", foundEntries, filename.c_str());
112 return (foundEntries != 0);
113}
114
115
116int Cementer::getCementStart()
117{
118 CRITICAL_BLOCK(cs_cement)
119 {
120 if (!entries.empty()) return entries.front().height;
121 return 0;
122 }
123}
124
125
126int Cementer::getCementEnd()
127{
128 CRITICAL_BLOCK(cs_cement)
129 {
130 if (!entries.empty()) return entries.back().height;
131 return 0;
132 }
133}
134
135
136void Cementer::fastForward(int nHeight)
137{
138 if (entries.empty()) return;
139
140 // If we're already above the cement, discard all of it:
141 if (nHeight > entries.back().height)
142 entries.clear();
143
144 // Discard any cement entries below given height:
145 while (!entries.empty() && (entries.front().height < nHeight))
146 entries.pop_front();
147}
148
149
150bool Cementer::expectCement(int nHeight)
151{
152 CRITICAL_BLOCK(cs_cement)
153 {
154 // Discard any exhausted cement:
155 fastForward(nHeight);
156
157 // If there is no cement:
158 if (entries.empty()) return false;
159
160 // True if the current entry's height is the given one:
161 return (entries.front().height == nHeight);
162 }
163}
164
165
166// Note: must call expectCement(height) first!
167uint256 Cementer::getExpected()
168{
169 CRITICAL_BLOCK(cs_cement)
170 {
171 return entries.front().hash;
172 }
173}
174
175
176bool Cementer::verify(int nHeight, const uint256& hash)
177{
178 bool result = false;
179
180 CRITICAL_BLOCK(cs_cement)
181 {
182 // Discard any exhausted cement:
183 fastForward(nHeight);
184
185 // If there is no cement loaded:
186 if (entries.empty()) return true;
187
188 CementEntry current = entries.front();
189
190 if (current.height > nHeight) {
191 // Gap in cement:
192 result = true;
193 printf("Warning: gap in cement at height %d !\n", nHeight);
194 } else {
195 // The current cement is of nHeight:
196 result = (current.hash == hash);
197 printf("Proposed block %s (%d) %s by cement.\n", hash.ToString().c_str(), nHeight,
198 result ? "ACCEPTED" : "REJECTED");
199 }
200 }
201
202 return result;
203}