github.com/ava-labs/avalanchego@v1.11.11/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/ava-labs/avalanchego/database" 12 "github.com/ava-labs/avalanchego/database/versiondb" 13 "github.com/ava-labs/avalanchego/ids" 14 "github.com/ava-labs/avalanchego/snow" 15 "github.com/ava-labs/avalanchego/snow/consensus/snowman" 16 "github.com/ava-labs/avalanchego/utils/set" 17 "github.com/ava-labs/avalanchego/vms/example/xsvm/execute" 18 19 smblock "github.com/ava-labs/avalanchego/snow/engine/snowman/block" 20 xsblock "github.com/ava-labs/avalanchego/vms/example/xsvm/block" 21 ) 22 23 const maxClockSkew = 10 * time.Second 24 25 var ( 26 _ Block = (*block)(nil) 27 28 errMissingParent = errors.New("missing parent block") 29 errMissingChild = errors.New("missing child block") 30 errParentNotVerified = errors.New("parent block has not been verified") 31 errFutureTimestamp = errors.New("future timestamp") 32 errTimestampBeforeParent = errors.New("timestamp before parent") 33 errWrongHeight = errors.New("wrong height") 34 ) 35 36 type Block interface { 37 snowman.Block 38 smblock.WithVerifyContext 39 40 // State intends to return the new chain state following this block's 41 // acceptance. The new chain state is built (but not persisted) following a 42 // block's verification to allow block's descendants verification before 43 // being accepted. 44 State() (database.Database, error) 45 } 46 47 type block struct { 48 *xsblock.Stateless 49 50 chain *chain 51 52 id ids.ID 53 bytes []byte 54 55 state *versiondb.Database 56 verifiedChildrenIDs set.Set[ids.ID] 57 } 58 59 func (b *block) ID() ids.ID { 60 return b.id 61 } 62 63 func (b *block) Parent() ids.ID { 64 return b.ParentID 65 } 66 67 func (b *block) Bytes() []byte { 68 return b.bytes 69 } 70 71 func (b *block) Height() uint64 { 72 return b.Stateless.Height 73 } 74 75 func (b *block) Timestamp() time.Time { 76 return b.Time() 77 } 78 79 func (b *block) Verify(ctx context.Context) error { 80 return b.VerifyWithContext(ctx, nil) 81 } 82 83 func (b *block) Accept(context.Context) error { 84 if err := b.state.Commit(); err != nil { 85 return err 86 } 87 88 // Following this block's acceptance, make sure that it's direct children 89 // point to the base state, which now also contains this block's changes. 90 for childID := range b.verifiedChildrenIDs { 91 child, exists := b.chain.verifiedBlocks[childID] 92 if !exists { 93 return errMissingChild 94 } 95 if err := child.state.SetDatabase(b.chain.acceptedState); err != nil { 96 return err 97 } 98 } 99 100 b.chain.lastAcceptedID = b.id 101 b.chain.lastAcceptedHeight = b.Height() 102 delete(b.chain.verifiedBlocks, b.ParentID) 103 b.state = nil 104 return nil 105 } 106 107 func (b *block) Reject(context.Context) error { 108 delete(b.chain.verifiedBlocks, b.id) 109 b.state = nil 110 111 // TODO: push transactions back into the mempool 112 return nil 113 } 114 115 func (b *block) ShouldVerifyWithContext(context.Context) (bool, error) { 116 return execute.ExpectsContext(b.Stateless) 117 } 118 119 func (b *block) VerifyWithContext(ctx context.Context, blockContext *smblock.Context) error { 120 timestamp := b.Time() 121 if time.Until(timestamp) > maxClockSkew { 122 return errFutureTimestamp 123 } 124 125 // parent block must be verified or accepted 126 parent, exists := b.chain.verifiedBlocks[b.ParentID] 127 if !exists { 128 return errMissingParent 129 } 130 131 if b.Stateless.Height != parent.Stateless.Height+1 { 132 return errWrongHeight 133 } 134 135 parentTimestamp := parent.Time() 136 if timestamp.Before(parentTimestamp) { 137 return errTimestampBeforeParent 138 } 139 140 parentState, err := parent.State() 141 if err != nil { 142 return err 143 } 144 145 // This block's state is a versionDB built on top of it's parent state. This 146 // block's changes are pushed atomically to the parent state when accepted. 147 blkState := versiondb.New(parentState) 148 err = execute.Block( 149 ctx, 150 b.chain.chainContext, 151 blkState, 152 b.chain.chainState == snow.Bootstrapping, 153 blockContext, 154 b.Stateless, 155 ) 156 if err != nil { 157 return err 158 } 159 160 // Make sure to only state the state the first time we verify this block. 161 if b.state == nil { 162 b.state = blkState 163 parent.verifiedChildrenIDs.Add(b.id) 164 b.chain.verifiedBlocks[b.id] = b 165 } 166 167 return nil 168 } 169 170 func (b *block) State() (database.Database, error) { 171 if b.id == b.chain.lastAcceptedID { 172 return b.chain.acceptedState, nil 173 } 174 175 // If this block isn't processing, then the child should never have had 176 // verify called on it. 177 if b.state == nil { 178 return nil, errParentNotVerified 179 } 180 181 return b.state, nil 182 }