BitShares-Core  5.0.0
BitShares blockchain implementation and command-line interface software
fork_database.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Cryptonomex, Inc., and contributors.
3  *
4  * The MIT License
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
26 
27 namespace graphene { namespace chain {
29 {
30 }
32 {
33  _head.reset();
34  _index.clear();
35 }
36 
38 {
39  FC_ASSERT( _head, "no block to pop" );
40  auto prev = _head->prev.lock();
41  FC_ASSERT( prev, "popping block would leave head block null" );
42  _head = prev;
43 }
44 
46 {
47  auto item = std::make_shared<fork_item>(std::move(b));
48  _index.insert(item);
49  _head = item;
50 }
51 
56 shared_ptr<fork_item> fork_database::push_block(const signed_block& b)
57 {
58  auto item = std::make_shared<fork_item>(b);
59  try {
60  _push_block(item);
61  }
62  catch ( const unlinkable_block_exception& e )
63  {
64  wlog( "Pushing block to fork database that failed to link: ${id}, ${num}", ("id",b.id())("num",b.block_num()) );
65  wlog( "Head: ${num}, ${id}", ("num",_head->data.block_num())("id",_head->data.id()) );
66  throw;
67  }
68  return _head;
69 }
70 
71 void fork_database::_push_block(const item_ptr& item)
72 {
73  if( _head ) // make sure the block is within the range that we are caching
74  {
75  FC_ASSERT( item->num > std::max<int64_t>( 0, int64_t(_head->num) - (_max_size) ),
76  "attempting to push a block that is too old",
77  ("item->num",item->num)("head",_head->num)("max_size",_max_size));
78  }
79 
80  if( _head && item->previous_id() != block_id_type() )
81  {
82  auto& index = _index.get<block_id>();
83  auto itr = index.find(item->previous_id());
84  GRAPHENE_ASSERT(itr != index.end(), unlinkable_block_exception, "block does not link to known chain");
85  item->prev = *itr;
86  }
87 
88  _index.insert(item);
89  if( !_head ) _head = item;
90  else if( item->num > _head->num )
91  {
92  _head = item;
93  uint32_t min_num = _head->num - std::min( _max_size, _head->num );
94  auto& num_idx = _index.get<block_num>();
95  while( num_idx.size() && (*num_idx.begin())->num < min_num )
96  num_idx.erase( num_idx.begin() );
97  }
98 }
99 
100 void fork_database::set_max_size( uint32_t s )
101 {
102  _max_size = s;
103  if( !_head ) return;
104 
105  auto& by_num_idx = _index.get<block_num>();
106  auto itr = by_num_idx.begin();
107  while( itr != by_num_idx.end() )
108  {
109  if( (*itr)->num < std::max(int64_t(0),int64_t(_head->num) - _max_size) )
110  by_num_idx.erase(itr);
111  else
112  break;
113  itr = by_num_idx.begin();
114  }
115 }
116 
118 {
119  auto& index = _index.get<block_id>();
120  auto itr = index.find(id);
121  return itr != index.end();
122 }
123 
125 {
126  auto& index = _index.get<block_id>();
127  auto itr = index.find(id);
128  if( itr != index.end() )
129  return *itr;
130  return item_ptr();
131 }
132 
133 vector<item_ptr> fork_database::fetch_block_by_number(uint32_t num)const
134 {
135  vector<item_ptr> result;
136  auto itr = _index.get<block_num>().find(num);
137  while( itr != _index.get<block_num>().end() )
138  {
139  if( (*itr)->num == num )
140  result.push_back( *itr );
141  else
142  break;
143  ++itr;
144  }
145  return result;
146 }
147 
148 pair<fork_database::branch_type,fork_database::branch_type>
150 { try {
151  // This function gets a branch (i.e. vector<fork_item>) leading
152  // back to the most recent common ancestor.
153  pair<branch_type,branch_type> result;
154  auto first_branch_itr = _index.get<block_id>().find(first);
155  FC_ASSERT(first_branch_itr != _index.get<block_id>().end());
156  auto first_branch = *first_branch_itr;
157 
158  auto second_branch_itr = _index.get<block_id>().find(second);
159  FC_ASSERT(second_branch_itr != _index.get<block_id>().end());
160  auto second_branch = *second_branch_itr;
161 
162 
163  while( first_branch->data.block_num() > second_branch->data.block_num() )
164  {
165  result.first.push_back(first_branch);
166  first_branch = first_branch->prev.lock();
167  FC_ASSERT(first_branch);
168  }
169  while( second_branch->data.block_num() > first_branch->data.block_num() )
170  {
171  result.second.push_back( second_branch );
172  second_branch = second_branch->prev.lock();
173  FC_ASSERT(second_branch);
174  }
175  while( first_branch->data.previous != second_branch->data.previous )
176  {
177  result.first.push_back(first_branch);
178  result.second.push_back(second_branch);
179  first_branch = first_branch->prev.lock();
180  FC_ASSERT(first_branch);
181  second_branch = second_branch->prev.lock();
182  FC_ASSERT(second_branch);
183  }
184  if( first_branch && second_branch )
185  {
186  result.first.push_back(first_branch);
187  result.second.push_back(second_branch);
188  }
189  return result;
190 } FC_CAPTURE_AND_RETHROW( (first)(second) ) }
191 
192 void fork_database::set_head(shared_ptr<fork_item> h)
193 {
194  _head = h;
195 }
196 
198 {
199  _index.get<block_id>().erase(id);
200  // If we're removing head, try to pop it
201  if( _head && _head->id == id )
202  {
203  try
204  {
205  pop_block();
206  }
207  catch( fc::exception& e ) // If unable to pop normally, E.G. if head's prev is null, reset it
208  {
209  _head.reset();
210  }
211  }
212 }
213 
214 } } // graphene::chain
shared_ptr< fork_item > item_ptr
shared_ptr< fork_item > fetch_block(const block_id_type &id) const
pair< branch_type, branch_type > fetch_branch_from(block_id_type first, block_id_type second) const
Definition: api.cpp:56
void start_block(signed_block b)
Used to generate a useful error report when an exception is thrown.At each level in the stack where t...
Definition: exception.hpp:56
#define wlog(FORMAT,...)
Definition: logger.hpp:123
vector< item_ptr > fetch_block_by_number(uint32_t n) const
fc::ripemd160 block_id_type
Definition: types.hpp:242
virtual const object * find(object_id_type id) const =0
void set_head(shared_ptr< fork_item > h)
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:478
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
shared_ptr< fork_item > push_block(const signed_block &b)
at< List, 0 > first
Get the type at the beginning of the list.
Definition: typelist.hpp:190
void remove(block_id_type b)
uint32_t block_num() const
Definition: block.hpp:34
abstract base class for accessing objects indexed in various ways.
Definition: index.hpp:71
#define GRAPHENE_ASSERT(expr, exc_type, FORMAT,...)
Definition: exceptions.hpp:28
const block_id_type & id() const
Definition: block.cpp:41
bool is_known_block(const block_id_type &id) const