github.com/MetalBlockchain/metalgo@v1.11.9/snow/engine/avalanche/state/unique_vertex.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package state 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "strings" 11 12 "github.com/MetalBlockchain/metalgo/cache" 13 "github.com/MetalBlockchain/metalgo/ids" 14 "github.com/MetalBlockchain/metalgo/snow/choices" 15 "github.com/MetalBlockchain/metalgo/snow/consensus/avalanche" 16 "github.com/MetalBlockchain/metalgo/snow/consensus/snowstorm" 17 "github.com/MetalBlockchain/metalgo/snow/engine/avalanche/vertex" 18 "github.com/MetalBlockchain/metalgo/utils/formatting" 19 "github.com/MetalBlockchain/metalgo/utils/hashing" 20 ) 21 22 var ( 23 _ cache.Evictable[ids.ID] = (*uniqueVertex)(nil) 24 _ avalanche.Vertex = (*uniqueVertex)(nil) 25 26 errGetParents = errors.New("failed to get parents for vertex") 27 errGetHeight = errors.New("failed to get height for vertex") 28 errGetTxs = errors.New("failed to get txs for vertex") 29 ) 30 31 // uniqueVertex acts as a cache for vertices in the database. 32 // 33 // If a vertex is loaded, it will have one canonical uniqueVertex. The vertex 34 // will eventually be evicted from memory, when the uniqueVertex is evicted from 35 // the cache. If the uniqueVertex has a function called again after this 36 // eviction, the vertex will be re-loaded from the database. 37 type uniqueVertex struct { 38 serializer *Serializer 39 40 id ids.ID 41 v *vertexState 42 } 43 44 // newUniqueVertex returns a uniqueVertex instance from [b] by checking the cache 45 // and then parsing the vertex bytes on a cache miss. 46 func newUniqueVertex(ctx context.Context, s *Serializer, b []byte) (*uniqueVertex, error) { 47 vtx := &uniqueVertex{ 48 id: hashing.ComputeHash256Array(b), 49 serializer: s, 50 } 51 vtx.shallowRefresh() 52 53 // If the vtx exists, then the vertex is already known 54 if vtx.v.vtx != nil { 55 return vtx, nil 56 } 57 58 // If it wasn't in the cache parse the vertex and set it 59 innerVertex, err := s.parseVertex(b) 60 if err != nil { 61 return nil, err 62 } 63 if err := innerVertex.Verify(); err != nil { 64 return nil, err 65 } 66 67 unparsedTxs := innerVertex.Txs() 68 txs := make([]snowstorm.Tx, len(unparsedTxs)) 69 for i, txBytes := range unparsedTxs { 70 tx, err := vtx.serializer.VM.ParseTx(ctx, txBytes) 71 if err != nil { 72 return nil, err 73 } 74 txs[i] = tx 75 } 76 77 vtx.v.vtx = innerVertex 78 vtx.v.txs = txs 79 80 // If the vertex has already been fetched, 81 // skip persisting the vertex. 82 if vtx.v.status.Fetched() { 83 return vtx, nil 84 } 85 86 // The vertex is newly parsed, so set the status 87 // and persist it. 88 vtx.v.status = choices.Processing 89 return vtx, vtx.persist() 90 } 91 92 func (vtx *uniqueVertex) refresh() { 93 vtx.shallowRefresh() 94 95 if vtx.v.vtx == nil && vtx.v.status.Fetched() { 96 vtx.v.vtx = vtx.serializer.state.Vertex(vtx.ID()) 97 } 98 } 99 100 // shallowRefresh checks the cache for the uniqueVertex and gets the 101 // most up-to-date status for [vtx] 102 // ensures that the status is up-to-date for this vertex 103 // inner vertex may be nil after calling shallowRefresh 104 func (vtx *uniqueVertex) shallowRefresh() { 105 if vtx.v == nil { 106 vtx.v = &vertexState{} 107 } 108 if vtx.v.latest { 109 return 110 } 111 112 latest := vtx.serializer.state.UniqueVertex(vtx) 113 prevVtx := vtx.v.vtx 114 if latest == vtx { 115 vtx.v.status = vtx.serializer.state.Status(vtx.ID()) 116 vtx.v.latest = true 117 } else { 118 // If someone is in the cache, they must be up-to-date 119 *vtx = *latest 120 } 121 122 if vtx.v.vtx == nil { 123 vtx.v.vtx = prevVtx 124 } 125 } 126 127 func (vtx *uniqueVertex) Evict() { 128 if vtx.v != nil { 129 vtx.v.latest = false 130 // make sure the parents can be garbage collected 131 vtx.v.parents = nil 132 } 133 } 134 135 func (vtx *uniqueVertex) setVertex(ctx context.Context, innerVtx vertex.StatelessVertex) error { 136 vtx.shallowRefresh() 137 vtx.v.vtx = innerVtx 138 139 if vtx.v.status.Fetched() { 140 return nil 141 } 142 143 if _, err := vtx.Txs(ctx); err != nil { 144 return err 145 } 146 147 vtx.v.status = choices.Processing 148 return vtx.persist() 149 } 150 151 func (vtx *uniqueVertex) persist() error { 152 if err := vtx.serializer.state.SetVertex(vtx.v.vtx); err != nil { 153 return err 154 } 155 if err := vtx.serializer.state.SetStatus(vtx.ID(), vtx.v.status); err != nil { 156 return err 157 } 158 return vtx.serializer.versionDB.Commit() 159 } 160 161 func (vtx *uniqueVertex) setStatus(status choices.Status) error { 162 vtx.shallowRefresh() 163 if vtx.v.status == status { 164 return nil 165 } 166 vtx.v.status = status 167 return vtx.serializer.state.SetStatus(vtx.ID(), status) 168 } 169 170 func (vtx *uniqueVertex) ID() ids.ID { 171 return vtx.id 172 } 173 174 func (vtx *uniqueVertex) Key() ids.ID { 175 return vtx.id 176 } 177 178 func (vtx *uniqueVertex) Accept(ctx context.Context) error { 179 if err := vtx.setStatus(choices.Accepted); err != nil { 180 return err 181 } 182 183 vtx.serializer.edge.Add(vtx.id) 184 parents, err := vtx.Parents() 185 if err != nil { 186 return err 187 } 188 189 for _, parent := range parents { 190 vtx.serializer.edge.Remove(parent.ID()) 191 } 192 193 if err := vtx.serializer.state.SetEdge(vtx.serializer.Edge(ctx)); err != nil { 194 return fmt.Errorf("failed to set edge while accepting vertex %s due to %w", vtx.id, err) 195 } 196 197 // Should never traverse into parents of a decided vertex. Allows for the 198 // parents to be garbage collected 199 vtx.v.parents = nil 200 201 return vtx.serializer.versionDB.Commit() 202 } 203 204 func (vtx *uniqueVertex) Reject(context.Context) error { 205 if err := vtx.setStatus(choices.Rejected); err != nil { 206 return err 207 } 208 209 // Should never traverse into parents of a decided vertex. Allows for the 210 // parents to be garbage collected 211 vtx.v.parents = nil 212 213 return vtx.serializer.versionDB.Commit() 214 } 215 216 // TODO: run performance test to see if shallow refreshing 217 // (which will mean that refresh must be called in Bytes and Verify) 218 // improves performance 219 func (vtx *uniqueVertex) Status() choices.Status { 220 vtx.refresh() 221 return vtx.v.status 222 } 223 224 func (vtx *uniqueVertex) Parents() ([]avalanche.Vertex, error) { 225 vtx.refresh() 226 227 if vtx.v.vtx == nil { 228 return nil, fmt.Errorf("%w with status: %s", errGetParents, vtx.v.status) 229 } 230 231 parentIDs := vtx.v.vtx.ParentIDs() 232 if len(vtx.v.parents) != len(parentIDs) { 233 vtx.v.parents = make([]avalanche.Vertex, len(parentIDs)) 234 for i, parentID := range parentIDs { 235 vtx.v.parents[i] = &uniqueVertex{ 236 serializer: vtx.serializer, 237 id: parentID, 238 } 239 } 240 } 241 242 return vtx.v.parents, nil 243 } 244 245 func (vtx *uniqueVertex) Height() (uint64, error) { 246 vtx.refresh() 247 248 if vtx.v.vtx == nil { 249 return 0, fmt.Errorf("%w with status: %s", errGetHeight, vtx.v.status) 250 } 251 252 return vtx.v.vtx.Height(), nil 253 } 254 255 func (vtx *uniqueVertex) Txs(ctx context.Context) ([]snowstorm.Tx, error) { 256 vtx.refresh() 257 258 if vtx.v.vtx == nil { 259 return nil, fmt.Errorf("%w with status: %s", errGetTxs, vtx.v.status) 260 } 261 262 txs := vtx.v.vtx.Txs() 263 if len(txs) != len(vtx.v.txs) { 264 vtx.v.txs = make([]snowstorm.Tx, len(txs)) 265 for i, txBytes := range txs { 266 tx, err := vtx.serializer.VM.ParseTx(ctx, txBytes) 267 if err != nil { 268 return nil, err 269 } 270 vtx.v.txs[i] = tx 271 } 272 } 273 274 return vtx.v.txs, nil 275 } 276 277 func (vtx *uniqueVertex) Bytes() []byte { 278 return vtx.v.vtx.Bytes() 279 } 280 281 func (vtx *uniqueVertex) String() string { 282 sb := strings.Builder{} 283 284 parents, err := vtx.Parents() 285 if err != nil { 286 sb.WriteString(fmt.Sprintf("Vertex(ID = %s, Error=error while retrieving vertex parents: %s)", vtx.ID(), err)) 287 return sb.String() 288 } 289 txs, err := vtx.Txs(context.Background()) 290 if err != nil { 291 sb.WriteString(fmt.Sprintf("Vertex(ID = %s, Error=error while retrieving vertex txs: %s)", vtx.ID(), err)) 292 return sb.String() 293 } 294 295 sb.WriteString(fmt.Sprintf( 296 "Vertex(ID = %s, Status = %s, Number of Dependencies = %d, Number of Transactions = %d)", 297 vtx.ID(), 298 vtx.Status(), 299 len(parents), 300 len(txs), 301 )) 302 303 parentFormat := fmt.Sprintf("\n Parent[%s]: ID = %%s, Status = %%s", //nolint:perfsprint 304 formatting.IntFormat(len(parents)-1)) 305 for i, parent := range parents { 306 sb.WriteString(fmt.Sprintf(parentFormat, i, parent.ID(), parent.Status())) 307 } 308 309 txFormat := fmt.Sprintf("\n Transaction[%s]: ID = %%s, Status = %%s", //nolint:perfsprint 310 formatting.IntFormat(len(txs)-1)) 311 for i, tx := range txs { 312 sb.WriteString(fmt.Sprintf(txFormat, i, tx.ID(), tx.Status())) 313 } 314 315 return sb.String() 316 } 317 318 type vertexState struct { 319 latest bool 320 321 vtx vertex.StatelessVertex 322 status choices.Status 323 324 parents []avalanche.Vertex 325 txs []snowstorm.Tx 326 }