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}