github.com/MetalBlockchain/metalgo@v1.11.9/vms/example/xsvm/chain/block.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package chain 5 6 import ( 7 "context" 8 "errors" 9 "time" 10 11 "github.com/MetalBlockchain/metalgo/database" 12 "github.com/MetalBlockchain/metalgo/database/versiondb" 13 "github.com/MetalBlockchain/metalgo/ids" 14 "github.com/MetalBlockchain/metalgo/snow" 15 "github.com/MetalBlockchain/metalgo/snow/choices" 16 "github.com/MetalBlockchain/metalgo/snow/consensus/snowman" 17 "github.com/MetalBlockchain/metalgo/utils/set" 18 "github.com/MetalBlockchain/metalgo/vms/example/xsvm/execute" 19 "github.com/MetalBlockchain/metalgo/vms/example/xsvm/state" 20 21 smblock "github.com/MetalBlockchain/metalgo/snow/engine/snowman/block" 22 xsblock "github.com/MetalBlockchain/metalgo/vms/example/xsvm/block" 23 ) 24 25 const maxClockSkew = 10 * time.Second 26 27 var ( 28 _ Block = (*block)(nil) 29 30 errMissingParent = errors.New("missing parent block") 31 errMissingChild = errors.New("missing child block") 32 errParentNotVerified = errors.New("parent block has not been verified") 33 errMissingState = errors.New("missing state") 34 errFutureTimestamp = errors.New("future timestamp") 35 errTimestampBeforeParent = errors.New("timestamp before parent") 36 errWrongHeight = errors.New("wrong height") 37 ) 38 39 type Block interface { 40 snowman.Block 41 smblock.WithVerifyContext 42 43 // State intends to return the new chain state following this block's 44 // acceptance. The new chain state is built (but not persisted) following a 45 // block's verification to allow block's descendants verification before 46 // being accepted. 47 State() (database.Database, error) 48 } 49 50 type block struct { 51 *xsblock.Stateless 52 53 chain *chain 54 55 id ids.ID 56 status choices.Status 57 bytes []byte 58 59 state *versiondb.Database 60 verifiedChildrenIDs set.Set[ids.ID] 61 } 62 63 func (b *block) ID() ids.ID { 64 return b.id 65 } 66 67 func (b *block) Status() choices.Status { 68 if !b.status.Decided() { 69 b.status = b.calculateStatus() 70 } 71 return b.status 72 } 73 74 func (b *block) Parent() ids.ID { 75 return b.ParentID 76 } 77 78 func (b *block) Bytes() []byte { 79 return b.bytes 80 } 81 82 func (b *block) Height() uint64 { 83 return b.Stateless.Height 84 } 85 86 func (b *block) Timestamp() time.Time { 87 return b.Time() 88 } 89 90 func (b *block) Verify(ctx context.Context) error { 91 return b.VerifyWithContext(ctx, nil) 92 } 93 94 func (b *block) Accept(context.Context) error { 95 if err := b.state.Commit(); err != nil { 96 return err 97 } 98 99 // Following this block's acceptance, make sure that it's direct children 100 // point to the base state, which now also contains this block's changes. 101 for childID := range b.verifiedChildrenIDs { 102 child, exists := b.chain.verifiedBlocks[childID] 103 if !exists { 104 return errMissingChild 105 } 106 if err := child.state.SetDatabase(b.chain.acceptedState); err != nil { 107 return err 108 } 109 } 110 111 b.status = choices.Accepted 112 b.chain.lastAccepted = b.id 113 delete(b.chain.verifiedBlocks, b.ParentID) 114 return nil 115 } 116 117 func (b *block) Reject(context.Context) error { 118 b.status = choices.Rejected 119 delete(b.chain.verifiedBlocks, b.id) 120 121 // TODO: push transactions back into the mempool 122 return nil 123 } 124 125 func (b *block) ShouldVerifyWithContext(context.Context) (bool, error) { 126 return execute.ExpectsContext(b.Stateless) 127 } 128 129 func (b *block) VerifyWithContext(ctx context.Context, blockContext *smblock.Context) error { 130 timestamp := b.Time() 131 if time.Until(timestamp) > maxClockSkew { 132 return errFutureTimestamp 133 } 134 135 // parent block must be verified or accepted 136 parent, exists := b.chain.verifiedBlocks[b.ParentID] 137 if !exists { 138 return errMissingParent 139 } 140 141 if b.Stateless.Height != parent.Stateless.Height+1 { 142 return errWrongHeight 143 } 144 145 parentTimestamp := parent.Time() 146 if timestamp.Before(parentTimestamp) { 147 return errTimestampBeforeParent 148 } 149 150 parentState, err := parent.State() 151 if err != nil { 152 return err 153 } 154 155 // This block's state is a versionDB built on top of it's parent state. This 156 // block's changes are pushed atomically to the parent state when accepted. 157 blkState := versiondb.New(parentState) 158 err = execute.Block( 159 ctx, 160 b.chain.chainContext, 161 blkState, 162 b.chain.chainState == snow.Bootstrapping, 163 blockContext, 164 b.Stateless, 165 ) 166 if err != nil { 167 return err 168 } 169 170 // Make sure to only state the state the first time we verify this block. 171 if b.state == nil { 172 b.state = blkState 173 parent.verifiedChildrenIDs.Add(b.id) 174 b.chain.verifiedBlocks[b.id] = b 175 } 176 177 return nil 178 } 179 180 func (b *block) State() (database.Database, error) { 181 if b.id == b.chain.lastAccepted { 182 return b.chain.acceptedState, nil 183 } 184 185 // States of accepted blocks other than the lastAccepted are undefined. 186 if b.Status() == choices.Accepted { 187 return nil, errMissingState 188 } 189 190 // We should not be calling State on an unverified block. 191 if b.state == nil { 192 return nil, errParentNotVerified 193 } 194 195 return b.state, nil 196 } 197 198 func (b *block) calculateStatus() choices.Status { 199 if b.chain.lastAccepted == b.id { 200 return choices.Accepted 201 } 202 if _, ok := b.chain.verifiedBlocks[b.id]; ok { 203 return choices.Processing 204 } 205 206 _, err := state.GetBlock(b.chain.acceptedState, b.id) 207 switch { 208 case err == nil: 209 return choices.Accepted 210 211 case errors.Is(err, database.ErrNotFound): 212 // This block hasn't been verified yet. 213 return choices.Processing 214 215 default: 216 // TODO: correctly report this error to the consensus engine. 217 return choices.Processing 218 } 219 }