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 = &timestamp
    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  }