github.com/ava-labs/avalanchego@v1.11.11/vms/example/xsvm/state/storage.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 "errors" 8 9 "github.com/ava-labs/avalanchego/database" 10 "github.com/ava-labs/avalanchego/ids" 11 "github.com/ava-labs/avalanchego/utils/math" 12 "github.com/ava-labs/avalanchego/vms/platformvm/warp" 13 ) 14 15 var ( 16 errWrongNonce = errors.New("wrong nonce") 17 errInsufficientBalance = errors.New("insufficient balance") 18 ) 19 20 /* 21 * VMDB 22 * |-- initializedKey -> nil 23 * |-. blocks 24 * | |-- lastAcceptedKey -> blockID 25 * | |-- height -> blockID 26 * | '-- blockID -> block bytes 27 * |-. addresses 28 * | '-- addressID -> nonce 29 * | '-- addressID + chainID -> balance 30 * |-. chains 31 * | |-- chainID -> balance 32 * | '-- chainID + loanID -> nil 33 * '-. message 34 * '-- txID -> message bytes 35 */ 36 37 // Chain state 38 39 func IsInitialized(db database.KeyValueReader) (bool, error) { 40 return db.Has(initializedKey) 41 } 42 43 func SetInitialized(db database.KeyValueWriter) error { 44 return db.Put(initializedKey, nil) 45 } 46 47 // Block state 48 49 func GetLastAccepted(db database.KeyValueReader) (ids.ID, error) { 50 return database.GetID(db, blockPrefix) 51 } 52 53 func SetLastAccepted(db database.KeyValueWriter, blkID ids.ID) error { 54 return database.PutID(db, blockPrefix, blkID) 55 } 56 57 func GetBlockIDByHeight(db database.KeyValueReader, height uint64) (ids.ID, error) { 58 key := Flatten(blockPrefix, database.PackUInt64(height)) 59 return database.GetID(db, key) 60 } 61 62 func GetBlock(db database.KeyValueReader, blkID ids.ID) ([]byte, error) { 63 key := Flatten(blockPrefix, blkID[:]) 64 return db.Get(key) 65 } 66 67 func AddBlock(db database.KeyValueWriter, height uint64, blkID ids.ID, blk []byte) error { 68 heightToIDKey := Flatten(blockPrefix, database.PackUInt64(height)) 69 if err := database.PutID(db, heightToIDKey, blkID); err != nil { 70 return err 71 } 72 idToBlockKey := Flatten(blockPrefix, blkID[:]) 73 return db.Put(idToBlockKey, blk) 74 } 75 76 // Address state 77 78 func GetNonce(db database.KeyValueReader, address ids.ShortID) (uint64, error) { 79 key := Flatten(addressPrefix, address[:]) 80 nonce, err := database.GetUInt64(db, key) 81 if errors.Is(err, database.ErrNotFound) { 82 return 0, nil 83 } 84 return nonce, err 85 } 86 87 func SetNonce(db database.KeyValueWriter, address ids.ShortID, nonce uint64) error { 88 key := Flatten(addressPrefix, address[:]) 89 return database.PutUInt64(db, key, nonce) 90 } 91 92 func IncrementNonce(db database.KeyValueReaderWriter, address ids.ShortID, nonce uint64) error { 93 expectedNonce, err := GetNonce(db, address) 94 if err != nil { 95 return err 96 } 97 if nonce != expectedNonce { 98 return errWrongNonce 99 } 100 return SetNonce(db, address, nonce+1) 101 } 102 103 func GetBalance(db database.KeyValueReader, address ids.ShortID, chainID ids.ID) (uint64, error) { 104 key := Flatten(addressPrefix, address[:], chainID[:]) 105 balance, err := database.GetUInt64(db, key) 106 if errors.Is(err, database.ErrNotFound) { 107 return 0, nil 108 } 109 return balance, err 110 } 111 112 func SetBalance(db database.KeyValueWriterDeleter, address ids.ShortID, chainID ids.ID, balance uint64) error { 113 key := Flatten(addressPrefix, address[:], chainID[:]) 114 if balance == 0 { 115 return db.Delete(key) 116 } 117 return database.PutUInt64(db, key, balance) 118 } 119 120 func DecreaseBalance(db database.KeyValueReaderWriterDeleter, address ids.ShortID, chainID ids.ID, amount uint64) error { 121 balance, err := GetBalance(db, address, chainID) 122 if err != nil { 123 return err 124 } 125 if balance < amount { 126 return errInsufficientBalance 127 } 128 return SetBalance(db, address, chainID, balance-amount) 129 } 130 131 func IncreaseBalance(db database.KeyValueReaderWriterDeleter, address ids.ShortID, chainID ids.ID, amount uint64) error { 132 balance, err := GetBalance(db, address, chainID) 133 if err != nil { 134 return err 135 } 136 balance, err = math.Add(balance, amount) 137 if err != nil { 138 return err 139 } 140 return SetBalance(db, address, chainID, balance) 141 } 142 143 // Chain state 144 145 func HasLoanID(db database.KeyValueReader, chainID ids.ID, loanID ids.ID) (bool, error) { 146 key := Flatten(chainPrefix, chainID[:], loanID[:]) 147 return db.Has(key) 148 } 149 150 func AddLoanID(db database.KeyValueWriter, chainID ids.ID, loanID ids.ID) error { 151 key := Flatten(chainPrefix, chainID[:], loanID[:]) 152 return db.Put(key, nil) 153 } 154 155 func GetLoan(db database.KeyValueReader, chainID ids.ID) (uint64, error) { 156 key := Flatten(chainPrefix, chainID[:]) 157 balance, err := database.GetUInt64(db, key) 158 if errors.Is(err, database.ErrNotFound) { 159 return 0, nil 160 } 161 return balance, err 162 } 163 164 func SetLoan(db database.KeyValueWriterDeleter, chainID ids.ID, balance uint64) error { 165 key := Flatten(chainPrefix, chainID[:]) 166 if balance == 0 { 167 return db.Delete(key) 168 } 169 return database.PutUInt64(db, key, balance) 170 } 171 172 func DecreaseLoan(db database.KeyValueReaderWriterDeleter, chainID ids.ID, amount uint64) error { 173 balance, err := GetLoan(db, chainID) 174 if err != nil { 175 return err 176 } 177 if balance < amount { 178 return errInsufficientBalance 179 } 180 return SetLoan(db, chainID, balance-amount) 181 } 182 183 func IncreaseLoan(db database.KeyValueReaderWriterDeleter, chainID ids.ID, amount uint64) error { 184 balance, err := GetLoan(db, chainID) 185 if err != nil { 186 return err 187 } 188 balance, err = math.Add(balance, amount) 189 if err != nil { 190 return err 191 } 192 return SetLoan(db, chainID, balance) 193 } 194 195 // Message state 196 197 func GetMessage(db database.KeyValueReader, txID ids.ID) (*warp.UnsignedMessage, error) { 198 key := Flatten(messagePrefix, txID[:]) 199 bytes, err := db.Get(key) 200 if err != nil { 201 return nil, err 202 } 203 return warp.ParseUnsignedMessage(bytes) 204 } 205 206 func SetMessage(db database.KeyValueWriter, txID ids.ID, message *warp.UnsignedMessage) error { 207 key := Flatten(messagePrefix, txID[:]) 208 bytes := message.Bytes() 209 return db.Put(key, bytes) 210 }