github.com/condensat/bank-core@v0.1.0/database/query/cryptoaddress.go (about) 1 // Copyright 2020 Condensat Tech. All rights reserved. 2 // Use of this source code is governed by a MIT 3 // license that can be found in the LICENSE file. 4 5 package query 6 7 import ( 8 "errors" 9 "time" 10 11 "github.com/condensat/bank-core/database" 12 "github.com/condensat/bank-core/database/model" 13 14 "github.com/jinzhu/gorm" 15 ) 16 17 const ( 18 AllChains = "*" 19 ) 20 21 var ( 22 ErrInvalidChain = errors.New("Invalid Chain") 23 ErrInvalidPublicAddress = errors.New("Invalid Public Address") 24 ErrInvalidCryptoAddressID = errors.New("Invalid CryptoAddress ID") 25 ) 26 27 // AddOrUpdateCryptoAddress 28 func AddOrUpdateCryptoAddress(db database.Context, address model.CryptoAddress) (model.CryptoAddress, error) { 29 var result model.CryptoAddress 30 gdb := db.DB().(*gorm.DB) 31 if db == nil { 32 return result, database.ErrInvalidDatabase 33 } 34 35 if address.AccountID == 0 { 36 return result, ErrInvalidAccountID 37 } 38 if len(address.PublicAddress) == 0 { 39 return result, ErrInvalidPublicAddress 40 } 41 if len(address.Chain) == 0 || address.Chain == AllChains { 42 return result, ErrInvalidChain 43 } 44 45 if address.ID == 0 { 46 // set CreationDate for new entry 47 timestamp := time.Now().UTC().Truncate(time.Second) 48 address.CreationDate = ×tamp 49 } else { 50 // do not update non mutable fields 51 address.CreationDate = nil 52 address.PublicAddress = "" 53 address.Unconfidential = "" 54 address.Chain = "" 55 } 56 57 // search by id 58 search := model.CryptoAddress{ 59 ID: address.ID, 60 } 61 // create entry 62 if address.ID == 0 { 63 search = model.CryptoAddress{ 64 AccountID: address.AccountID, 65 PublicAddress: address.PublicAddress, 66 Unconfidential: address.Unconfidential, 67 } 68 } 69 70 err := gdb. 71 Where(search). 72 Assign(address). 73 FirstOrCreate(&result).Error 74 75 return result, err 76 } 77 78 func GetCryptoAddress(db database.Context, ID model.CryptoAddressID) (model.CryptoAddress, error) { 79 var result model.CryptoAddress 80 gdb := db.DB().(*gorm.DB) 81 if gdb == nil { 82 return result, database.ErrInvalidDatabase 83 } 84 85 if ID == 0 { 86 return result, ErrInvalidCryptoAddressID 87 } 88 89 err := gdb. 90 Where(model.CryptoAddress{ 91 ID: ID, 92 }). 93 First(&result).Error 94 95 return result, err 96 } 97 98 func GetCryptoAddressWithPublicAddress(db database.Context, publicAddress model.String) (model.CryptoAddress, error) { 99 var result model.CryptoAddress 100 gdb := db.DB().(*gorm.DB) 101 if gdb == nil { 102 return result, database.ErrInvalidDatabase 103 } 104 105 if len(publicAddress) == 0 { 106 return result, ErrInvalidPublicAddress 107 } 108 109 err := gdb. 110 Where(model.CryptoAddress{ 111 PublicAddress: publicAddress, 112 }). 113 First(&result).Error 114 115 return result, err 116 } 117 118 func GetCryptoAddressWithUnconfidential(db database.Context, unconfidential model.String) (model.CryptoAddress, error) { 119 var result model.CryptoAddress 120 gdb := db.DB().(*gorm.DB) 121 if gdb == nil { 122 return result, database.ErrInvalidDatabase 123 } 124 125 if len(unconfidential) == 0 { 126 return result, ErrInvalidPublicAddress 127 } 128 129 err := gdb. 130 Where(model.CryptoAddress{ 131 Unconfidential: unconfidential, 132 }). 133 First(&result).Error 134 135 return result, err 136 } 137 138 func LastAccountCryptoAddress(db database.Context, accountID model.AccountID) (model.CryptoAddress, error) { 139 var result model.CryptoAddress 140 gdb := db.DB().(*gorm.DB) 141 if gdb == nil { 142 return result, database.ErrInvalidDatabase 143 } 144 145 if accountID == 0 { 146 return result, ErrInvalidAccountID 147 } 148 149 err := gdb. 150 Where(model.CryptoAddress{ 151 AccountID: accountID, 152 }). 153 Last(&result).Error 154 155 if err != nil && err != gorm.ErrRecordNotFound { 156 return result, err 157 } 158 159 return result, nil 160 } 161 162 func AllAccountCryptoAddresses(db database.Context, accountID model.AccountID) ([]model.CryptoAddress, error) { 163 gdb := db.DB().(*gorm.DB) 164 if gdb == nil { 165 return nil, database.ErrInvalidDatabase 166 } 167 168 if accountID == 0 { 169 return nil, ErrInvalidAccountID 170 } 171 172 var list []*model.CryptoAddress 173 err := gdb. 174 Where(model.CryptoAddress{ 175 AccountID: accountID, 176 }). 177 Order("id ASC"). 178 Find(&list).Error 179 180 if err != nil && err != gorm.ErrRecordNotFound { 181 return nil, err 182 } 183 184 return converCryptoAddressList(list), nil 185 } 186 187 func AllUnusedAccountCryptoAddresses(db database.Context, accountID model.AccountID) ([]model.CryptoAddress, error) { 188 if accountID == 0 { 189 return nil, ErrInvalidAccountID 190 } 191 return findUnusedCryptoAddresses(db, accountID, 0, AllChains) 192 } 193 194 func AllUnusedCryptoAddresses(db database.Context, chain model.String) ([]model.CryptoAddress, error) { 195 return findUnusedCryptoAddresses(db, 0, 0, chain) 196 } 197 198 func AllMempoolCryptoAddresses(db database.Context, chain model.String) ([]model.CryptoAddress, error) { 199 return findUnusedCryptoAddresses(db, 0, model.MemPoolBlockID, chain) 200 } 201 202 func AllUnconfirmedCryptoAddresses(db database.Context, chain model.String, afterBlock model.BlockID) ([]model.CryptoAddress, error) { 203 return findUnusedCryptoAddresses(db, 0, afterBlock, chain) 204 } 205 206 func findUnusedCryptoAddresses(db database.Context, accountID model.AccountID, blockID model.BlockID, chain model.String) ([]model.CryptoAddress, error) { 207 gdb := db.DB().(*gorm.DB) 208 if gdb == nil { 209 return nil, database.ErrInvalidDatabase 210 } 211 212 if len(chain) == 0 { 213 return nil, ErrInvalidChain 214 } 215 216 // support wildcard for all chains 217 if chain == AllChains { 218 chain = "" 219 } 220 221 var filter func(db *gorm.DB) *gorm.DB 222 // filter for confirmed or unconfirmed 223 if blockID > model.MemPoolBlockID { 224 // filter confirmed 225 filter = ScopeFirstBlockIDAfter(blockID) 226 } else { 227 // filter unconfirmed 228 filter = ScopeFirstBlockIDExact(blockID) 229 } 230 231 var list []*model.CryptoAddress 232 err := gdb. 233 Where(model.CryptoAddress{ 234 AccountID: accountID, 235 Chain: chain, 236 }). 237 Scopes(filter). 238 Order("id ASC"). 239 Find(&list).Error 240 241 if err != nil && err != gorm.ErrRecordNotFound { 242 return nil, err 243 } 244 245 return converCryptoAddressList(list), nil 246 } 247 248 func converCryptoAddressList(list []*model.CryptoAddress) []model.CryptoAddress { 249 var result []model.CryptoAddress 250 for _, curr := range list { 251 if curr != nil { 252 result = append(result, *curr) 253 } 254 } 255 256 return result[:] 257 } 258 259 // ScopeFirstBefore 260 func ScopeFirstBefore(blockID model.BlockID) func(db *gorm.DB) *gorm.DB { 261 return func(db *gorm.DB) *gorm.DB { 262 return db.Where(reqFirstBlockIDBefore(), blockID) 263 } 264 } 265 266 // ScopeFirstBlockIDExact 267 func ScopeFirstBlockIDExact(blockID model.BlockID) func(db *gorm.DB) *gorm.DB { 268 return func(db *gorm.DB) *gorm.DB { 269 return db.Where(reqFirstBlockIDExact(), blockID) 270 } 271 } 272 273 // ScopeFirstBlockIDAfter 274 func ScopeFirstBlockIDAfter(blockID model.BlockID) func(db *gorm.DB) *gorm.DB { 275 return func(db *gorm.DB) *gorm.DB { 276 return db.Where(reqFirstBlockIDAfter(), blockID) 277 } 278 } 279 280 const ( 281 colAccountID = "account_id" 282 colPublicAddress = "public_address" 283 colUnconfidential = "unconfidential" 284 colChain = "chain" 285 colCreationDate = "creation_date" 286 colFirstBlockID = "first_block_id" 287 colIgnoreAccounting = "ignore_accounting" 288 ) 289 290 func cryptoAddressColumnNames() []string { 291 return []string{ 292 colID, 293 colAccountID, 294 colPublicAddress, 295 colUnconfidential, 296 colChain, 297 colCreationDate, 298 colFirstBlockID, 299 colIgnoreAccounting, 300 } 301 } 302 303 const () 304 305 func reqFirstBlockIDBefore() string { 306 var req [len(colFirstBlockID) + len(reqLTE)]byte 307 off := 0 308 off += copy(req[off:], colFirstBlockID) 309 copy(req[off:], reqLTE) 310 311 return string(req[:]) 312 } 313 314 func reqFirstBlockIDExact() string { 315 var req [len(colFirstBlockID) + len(reqEQ)]byte 316 off := 0 317 off += copy(req[off:], colFirstBlockID) 318 copy(req[off:], reqEQ) 319 320 return string(req[:]) 321 } 322 323 func reqFirstBlockIDAfter() string { 324 var req [len(colFirstBlockID) + len(reqGTE)]byte 325 off := 0 326 off += copy(req[off:], colFirstBlockID) 327 copy(req[off:], reqGTE) 328 329 return string(req[:]) 330 }