github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/types/coin.go (about)

     1  package types
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"regexp"
     7  )
     8  
     9  //-----------------------------------------------------------------------------
    10  // Coin
    11  
    12  // Coin hold some amount of one currency.
    13  //
    14  // CONTRACT: A coin will never hold a negative amount of any denomination.
    15  //
    16  // TODO: Make field members private for further safety.
    17  //type Coin struct {
    18  //	Denom string `json:"denom"`
    19  //
    20  //	// To allow the use of unsigned integers (see: #1273) a larger refactor will
    21  //	// need to be made. So we use signed integers for now with safety measures in
    22  //	// place preventing negative values being used.
    23  //	Amount Int `json:"amount"`
    24  //}
    25  //
    26  //// NewCoin returns a new coin with a denomination and amount. It will panic if
    27  //// the amount is negative.
    28  //func NewCoin(denom string, amount Int) Coin {
    29  //	if err := validate(denom, amount); err != nil {
    30  //		panic(err)
    31  //	}
    32  //
    33  //	return Coin{
    34  //		Denom:  denom,
    35  //		Amount: amount,
    36  //	}
    37  //}
    38  
    39  // NewInt64Coin returns a new coin with a denomination and amount. It will panic
    40  // if the amount is negative.
    41  func NewInt64Coin(denom string, amount int64) Coin {
    42  	return NewCoin(denom, NewInt(amount))
    43  }
    44  
    45  // String provides a human-readable representation of a coin
    46  //func (coin Coin) String() string {
    47  //	return fmt.Sprintf("%v%v", coin.Amount, coin.Denom)
    48  //}
    49  
    50  // validate returns an error if the Coin has a negative amount or if
    51  // the denom is invalid.
    52  func validate(denom string, amount Int) error {
    53  	if err := ValidateDenom(denom); err != nil {
    54  		return err
    55  	}
    56  
    57  	if amount.IsNegative() {
    58  		return fmt.Errorf("negative coin amount: %v", amount)
    59  	}
    60  
    61  	return nil
    62  }
    63  
    64  // IsValid returns true if the Coin has a non-negative amount and the denom is vaild.
    65  //func (coin Coin) IsValid() bool {
    66  //	if err := validate(coin.Denom, coin.Amount); err != nil {
    67  //		return false
    68  //	}
    69  //	return true
    70  //}
    71  //
    72  //// IsZero returns if this represents no money
    73  //func (coin Coin) IsZero() bool {
    74  //	return coin.Amount.IsZero()
    75  //}
    76  //
    77  //// IsGTE returns true if they are the same type and the receiver is
    78  //// an equal or greater value
    79  //func (coin Coin) IsGTE(other Coin) bool {
    80  //	if coin.Denom != other.Denom {
    81  //		panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, other.Denom))
    82  //	}
    83  //
    84  //	return !coin.Amount.LT(other.Amount)
    85  //}
    86  //
    87  //// IsLT returns true if they are the same type and the receiver is
    88  //// a smaller value
    89  //func (coin Coin) IsLT(other Coin) bool {
    90  //	if coin.Denom != other.Denom {
    91  //		panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, other.Denom))
    92  //	}
    93  //
    94  //	return coin.Amount.LT(other.Amount)
    95  //}
    96  //
    97  //// IsEqual returns true if the two sets of Coins have the same value
    98  //func (coin Coin) IsEqual(other Coin) bool {
    99  //	if coin.Denom != other.Denom {
   100  //		panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, other.Denom))
   101  //	}
   102  //
   103  //	return coin.Amount.Equal(other.Amount)
   104  //}
   105  //
   106  //// Adds amounts of two coins with same denom. If the coins differ in denom then
   107  //// it panics.
   108  //func (coin Coin) Add(coinB Coin) Coin {
   109  //	if coin.Denom != coinB.Denom {
   110  //		panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, coinB.Denom))
   111  //	}
   112  //
   113  //	return Coin{coin.Denom, coin.Amount.Add(coinB.Amount)}
   114  //}
   115  //
   116  //// Subtracts amounts of two coins with same denom. If the coins differ in denom
   117  //// then it panics.
   118  //func (coin Coin) Sub(coinB Coin) Coin {
   119  //	if coin.Denom != coinB.Denom {
   120  //		panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, coinB.Denom))
   121  //	}
   122  //
   123  //	res := Coin{coin.Denom, coin.Amount.Sub(coinB.Amount)}
   124  //	if res.IsNegative() {
   125  //		panic("negative coin amount")
   126  //	}
   127  //
   128  //	return res
   129  //}
   130  //
   131  //// IsPositive returns true if coin amount is positive.
   132  ////
   133  //// TODO: Remove once unsigned integers are used.
   134  //func (coin Coin) IsPositive() bool {
   135  //	return coin.Amount.Sign() == 1
   136  //}
   137  //
   138  //// IsNegative returns true if the coin amount is negative and false otherwise.
   139  ////
   140  //// TODO: Remove once unsigned integers are used.
   141  //func (coin Coin) IsNegative() bool {
   142  //	return coin.Amount.Sign() == -1
   143  //}
   144  //
   145  ////-----------------------------------------------------------------------------
   146  //// Coins
   147  //
   148  //// Coins is a set of Coin, one per currency
   149  //type Coins []Coin
   150  
   151  // NewCoins constructs a new coin set.
   152  func NewCoins(coins ...Coin) Coins {
   153  	// remove zeroes
   154  	newCoins := removeZeroCoins(Coins(coins))
   155  	if len(newCoins) == 0 {
   156  		return Coins{}
   157  	}
   158  
   159  	if len(newCoins) > 1 {
   160  		newCoins.Sort()
   161  	}
   162  
   163  	// detect duplicate Denoms
   164  	if dupIndex := findDup(newCoins); dupIndex != -1 {
   165  		panic(fmt.Errorf("find duplicate denom: %s", newCoins[dupIndex]))
   166  	}
   167  
   168  	if !newCoins.IsValid() {
   169  		panic(fmt.Errorf("invalid coin set: %s", newCoins))
   170  	}
   171  
   172  	return newCoins
   173  }
   174  
   175  type coinsJSON Coins
   176  
   177  // MarshalJSON implements a custom JSON marshaller for the Coins type to allow
   178  // nil Coins to be encoded as an empty array.
   179  func (coins Coins) MarshalJSON() ([]byte, error) {
   180  	if coins == nil {
   181  		return json.Marshal(coinsJSON(Coins{}))
   182  	}
   183  
   184  	return json.Marshal(coinsJSON(coins))
   185  }
   186  
   187  //func (coins Coins) String() string {
   188  //	if len(coins) == 0 {
   189  //		return ""
   190  //	}
   191  //
   192  //	out := ""
   193  //	for _, coin := range coins {
   194  //		out += fmt.Sprintf("%v,", coin.String())
   195  //	}
   196  //	return out[:len(out)-1]
   197  //}
   198  //
   199  //// IsValid asserts the Coins are sorted, have positive amount,
   200  //// and Denom does not contain upper case characters.
   201  //func (coins Coins) IsValid() bool {
   202  //	switch len(coins) {
   203  //	case 0:
   204  //		return true
   205  //	case 1:
   206  //		if err := ValidateDenom(coins[0].Denom); err != nil {
   207  //			return false
   208  //		}
   209  //		return coins[0].IsPositive()
   210  //	default:
   211  //		// check single coin case
   212  //		if !(Coins{coins[0]}).IsValid() {
   213  //			return false
   214  //		}
   215  //
   216  //		lowDenom := coins[0].Denom
   217  //		for _, coin := range coins[1:] {
   218  //			if strings.ToLower(coin.Denom) != coin.Denom {
   219  //				return false
   220  //			}
   221  //			if coin.Denom <= lowDenom {
   222  //				return false
   223  //			}
   224  //			if !coin.IsPositive() {
   225  //				return false
   226  //			}
   227  //
   228  //			// we compare each coin against the last denom
   229  //			lowDenom = coin.Denom
   230  //		}
   231  //
   232  //		return true
   233  //	}
   234  //}
   235  //
   236  //// Add adds two sets of coins.
   237  ////
   238  //// e.g.
   239  //// {2A} + {A, 2B} = {3A, 2B}
   240  //// {2A} + {0B} = {2A}
   241  ////
   242  //// NOTE: Add operates under the invariant that coins are sorted by
   243  //// denominations.
   244  ////
   245  //// CONTRACT: Add will never return Coins where one Coin has a non-positive
   246  //// amount. In otherwords, IsValid will always return true.
   247  //func (coins Coins) Add(coinsB ...Coin) Coins {
   248  //	return coins.safeAdd(coinsB)
   249  //}
   250  //
   251  //// safeAdd will perform addition of two coins sets. If both coin sets are
   252  //// empty, then an empty set is returned. If only a single set is empty, the
   253  //// other set is returned. Otherwise, the coins are compared in order of their
   254  //// denomination and addition only occurs when the denominations match, otherwise
   255  //// the coin is simply added to the sum assuming it's not zero.
   256  //func (coins Coins) safeAdd(coinsB Coins) Coins {
   257  //	sum := ([]Coin)(nil)
   258  //	indexA, indexB := 0, 0
   259  //	lenA, lenB := len(coins), len(coinsB)
   260  //
   261  //	for {
   262  //		if indexA == lenA {
   263  //			if indexB == lenB {
   264  //				// return nil coins if both sets are empty
   265  //				return sum
   266  //			}
   267  //
   268  //			// return set B (excluding zero coins) if set A is empty
   269  //			return append(sum, removeZeroCoins(coinsB[indexB:])...)
   270  //		} else if indexB == lenB {
   271  //			// return set A (excluding zero coins) if set B is empty
   272  //			return append(sum, removeZeroCoins(coins[indexA:])...)
   273  //		}
   274  //
   275  //		coinA, coinB := coins[indexA], coinsB[indexB]
   276  //
   277  //		switch strings.Compare(coinA.Denom, coinB.Denom) {
   278  //		case -1: // coin A denom < coin B denom
   279  //			if !coinA.IsZero() {
   280  //				sum = append(sum, coinA)
   281  //			}
   282  //
   283  //			indexA++
   284  //
   285  //		case 0: // coin A denom == coin B denom
   286  //			res := coinA.Add(coinB)
   287  //			if !res.IsZero() {
   288  //				sum = append(sum, res)
   289  //			}
   290  //
   291  //			indexA++
   292  //			indexB++
   293  //
   294  //		case 1: // coin A denom > coin B denom
   295  //			if !coinB.IsZero() {
   296  //				sum = append(sum, coinB)
   297  //			}
   298  //
   299  //			indexB++
   300  //		}
   301  //	}
   302  //}
   303  
   304  // DenomsSubsetOf returns true if receiver's denom set
   305  // is subset of coinsB's denoms.
   306  func (coins Coins) DenomsSubsetOf(coinsB Coins) bool {
   307  	// more denoms in B than in receiver
   308  	if len(coins) > len(coinsB) {
   309  		return false
   310  	}
   311  
   312  	for _, coin := range coins {
   313  		if coinsB.AmountOf(coin.Denom).IsZero() {
   314  			return false
   315  		}
   316  	}
   317  
   318  	return true
   319  }
   320  
   321  // Sub subtracts a set of coins from another.
   322  //
   323  // e.g.
   324  // {2A, 3B} - {A} = {A, 3B}
   325  // {2A} - {0B} = {2A}
   326  // {A, B} - {A} = {B}
   327  //
   328  // CONTRACT: Sub will never return Coins where one Coin has a non-positive
   329  // amount. In otherwords, IsValid will always return true.
   330  //func (coins Coins) Sub(coinsB Coins) Coins {
   331  //	diff, hasNeg := coins.SafeSub(coinsB)
   332  //	if hasNeg {
   333  //		panic("negative coin amount")
   334  //	}
   335  //
   336  //	return diff
   337  //}
   338  //
   339  //// SafeSub performs the same arithmetic as Sub but returns a boolean if any
   340  //// negative coin amount was returned.
   341  //func (coins Coins) SafeSub(coinsB Coins) (Coins, bool) {
   342  //	diff := coins.safeAdd(coinsB.negative())
   343  //	return diff, diff.IsAnyNegative()
   344  //}
   345  
   346  // IsAllGT returns true if for every denom in coinsB,
   347  // the denom is present at a greater amount in coins.
   348  func (coins Coins) IsAllGT(coinsB Coins) bool {
   349  	if len(coins) == 0 {
   350  		return false
   351  	}
   352  
   353  	if len(coinsB) == 0 {
   354  		return true
   355  	}
   356  
   357  	if !coinsB.DenomsSubsetOf(coins) {
   358  		return false
   359  	}
   360  
   361  	for _, coinB := range coinsB {
   362  		amountA, amountB := coins.AmountOf(coinB.Denom), coinB.Amount
   363  		if !amountA.GT(amountB) {
   364  			return false
   365  		}
   366  	}
   367  
   368  	return true
   369  }
   370  
   371  // IsAllGTE returns false if for any denom in coinsB,
   372  // the denom is present at a smaller amount in coins;
   373  // else returns true.
   374  func (coins Coins) IsAllGTE(coinsB Coins) bool {
   375  	if len(coinsB) == 0 {
   376  		return true
   377  	}
   378  
   379  	if len(coins) == 0 {
   380  		return false
   381  	}
   382  
   383  	for _, coinB := range coinsB {
   384  		if coinB.Amount.GT(coins.AmountOf(coinB.Denom)) {
   385  			return false
   386  		}
   387  	}
   388  
   389  	return true
   390  }
   391  
   392  // IsAllLT returns True iff for every denom in coins, the denom is present at
   393  // a smaller amount in coinsB.
   394  func (coins Coins) IsAllLT(coinsB Coins) bool {
   395  	return coinsB.IsAllGT(coins)
   396  }
   397  
   398  // IsAllLTE returns true iff for every denom in coins, the denom is present at
   399  // a smaller or equal amount in coinsB.
   400  func (coins Coins) IsAllLTE(coinsB Coins) bool {
   401  	return coinsB.IsAllGTE(coins)
   402  }
   403  
   404  // IsAnyGT returns true iff for any denom in coins, the denom is present at a
   405  // greater amount in coinsB.
   406  //
   407  // e.g.
   408  // {2A, 3B}.IsAnyGT{A} = true
   409  // {2A, 3B}.IsAnyGT{5C} = false
   410  // {}.IsAnyGT{5C} = false
   411  // {2A, 3B}.IsAnyGT{} = false
   412  func (coins Coins) IsAnyGT(coinsB Coins) bool {
   413  	if len(coinsB) == 0 {
   414  		return false
   415  	}
   416  
   417  	for _, coin := range coins {
   418  		amt := coinsB.AmountOf(coin.Denom)
   419  		if coin.Amount.GT(amt) && !amt.IsZero() {
   420  			return true
   421  		}
   422  	}
   423  
   424  	return false
   425  }
   426  
   427  // IsAnyGTE returns true iff coins contains at least one denom that is present
   428  // at a greater or equal amount in coinsB; it returns false otherwise.
   429  //
   430  // NOTE: IsAnyGTE operates under the invariant that both coin sets are sorted
   431  // by denominations and there exists no zero coins.
   432  func (coins Coins) IsAnyGTE(coinsB Coins) bool {
   433  	if len(coinsB) == 0 {
   434  		return false
   435  	}
   436  
   437  	for _, coin := range coins {
   438  		amt := coinsB.AmountOf(coin.Denom)
   439  		if coin.Amount.GTE(amt) && !amt.IsZero() {
   440  			return true
   441  		}
   442  	}
   443  
   444  	return false
   445  }
   446  
   447  // IsZero returns true if there are no coins or all coins are zero.
   448  //func (coins Coins) IsZero() bool {
   449  //	for _, coin := range coins {
   450  //		if !coin.IsZero() {
   451  //			return false
   452  //		}
   453  //	}
   454  //	return true
   455  //}
   456  //
   457  //// IsEqual returns true if the two sets of Coins have the same value
   458  //func (coins Coins) IsEqual(coinsB Coins) bool {
   459  //	if len(coins) != len(coinsB) {
   460  //		return false
   461  //	}
   462  //
   463  //	coins = coins.Sort()
   464  //	coinsB = coinsB.Sort()
   465  //
   466  //	for i := 0; i < len(coins); i++ {
   467  //		if !coins[i].IsEqual(coinsB[i]) {
   468  //			return false
   469  //		}
   470  //	}
   471  //
   472  //	return true
   473  //}
   474  //
   475  //// Empty returns true if there are no coins and false otherwise.
   476  //func (coins Coins) Empty() bool {
   477  //	return len(coins) == 0
   478  //}
   479  //
   480  //// Returns the amount of a denom from coins
   481  //func (coins Coins) AmountOf(denom string) Int {
   482  //	mustValidateDenom(denom)
   483  //
   484  //	switch len(coins) {
   485  //	case 0:
   486  //		return ZeroInt()
   487  //
   488  //	case 1:
   489  //		coin := coins[0]
   490  //		if coin.Denom == denom {
   491  //			return coin.Amount
   492  //		}
   493  //		return ZeroInt()
   494  //
   495  //	default:
   496  //		midIdx := len(coins) / 2 // 2:1, 3:1, 4:2
   497  //		coin := coins[midIdx]
   498  //		switch {
   499  //		case denom < coin.Denom:
   500  //			return coins[:midIdx].AmountOf(denom)
   501  //		case denom == coin.Denom:
   502  //			return coin.Amount
   503  //		default:
   504  //			return coins[midIdx+1:].AmountOf(denom)
   505  //		}
   506  //	}
   507  //}
   508  //
   509  //// GetDenomByIndex returns the Denom of the certain coin to make the findDup generic
   510  //func (coins Coins) GetDenomByIndex(i int) string {
   511  //	return coins[i].Denom
   512  //}
   513  //
   514  //// IsAllPositive returns true if there is at least one coin and all currencies
   515  //// have a positive value.
   516  //func (coins Coins) IsAllPositive() bool {
   517  //	if len(coins) == 0 {
   518  //		return false
   519  //	}
   520  //
   521  //	for _, coin := range coins {
   522  //		if !coin.IsPositive() {
   523  //			return false
   524  //		}
   525  //	}
   526  //
   527  //	return true
   528  //}
   529  //
   530  //// IsAnyNegative returns true if there is at least one coin whose amount
   531  //// is negative; returns false otherwise. It returns false if the coin set
   532  //// is empty too.
   533  ////
   534  //// TODO: Remove once unsigned integers are used.
   535  //func (coins Coins) IsAnyNegative() bool {
   536  //	for _, coin := range coins {
   537  //		if coin.IsNegative() {
   538  //			return true
   539  //		}
   540  //	}
   541  //
   542  //	return false
   543  //}
   544  //
   545  //// negative returns a set of coins with all amount negative.
   546  ////
   547  //// TODO: Remove once unsigned integers are used.
   548  //func (coins Coins) negative() Coins {
   549  //	res := make([]Coin, 0, len(coins))
   550  //
   551  //	for _, coin := range coins {
   552  //		res = append(res, Coin{
   553  //			Denom:  coin.Denom,
   554  //			Amount: coin.Amount.Neg(),
   555  //		})
   556  //	}
   557  //
   558  //	return res
   559  //}
   560  
   561  // removeZeroCoins removes all zero coins from the given coin set in-place.
   562  func removeZeroCoins(coins Coins) Coins {
   563  	for i := 0; i < len(coins); i++ {
   564  		if coins[i].IsZero() {
   565  			break
   566  		} else if i == len(coins)-1 {
   567  			return coins
   568  		}
   569  	}
   570  	var result []Coin
   571  	if len(coins) > 0 {
   572  		result = make([]Coin, 0, len(coins)-1)
   573  	}
   574  
   575  	for _, coin := range coins {
   576  		if !coin.IsZero() {
   577  			result = append(result, coin)
   578  		}
   579  	}
   580  	return result
   581  }
   582  
   583  //-----------------------------------------------------------------------------
   584  // Sort interface
   585  
   586  ////nolint
   587  //func (coins Coins) Len() int           { return len(coins) }
   588  //func (coins Coins) Less(i, j int) bool { return coins[i].Denom < coins[j].Denom }
   589  //func (coins Coins) Swap(i, j int)      { coins[i], coins[j] = coins[j], coins[i] }
   590  //
   591  //var _ sort.Interface = Coins{}
   592  //
   593  //// Sort is a helper function to sort the set of coins inplace
   594  //func (coins Coins) Sort() Coins {
   595  //	sort.Sort(coins)
   596  //	return coins
   597  //}
   598  
   599  //-----------------------------------------------------------------------------
   600  // Parsing
   601  
   602  var (
   603  	// Denominations can be 3 ~ 16 characters long.
   604  	//reDnmString = `[a-z][a-z0-9]{2,15}`
   605  	reAmt = `[[:digit:]]+`
   606  	//reDecAmt    = `[[:digit:]]*\.[[:digit:]]+`
   607  	reSpc  = `[[:space:]]*`
   608  	reDnm  *regexp.Regexp
   609  	reCoin *regexp.Regexp
   610  
   611  	//ibcReDnm       *regexp.Regexp
   612  	//reDecCoin   *regexp.Regexp
   613  )
   614  
   615  func init() {
   616  	SetCoinDenomRegex(DefaultCoinDenomRegex)
   617  }
   618  
   619  // DefaultCoinDenomRegex returns the default regex string
   620  func DefaultCoinDenomRegex() string {
   621  	return reDnmString
   622  }
   623  
   624  // coinDenomRegex returns the current regex string and can be overwritten through the SetCoinDenomRegex accessor.
   625  var coinDenomRegex = DefaultCoinDenomRegex
   626  
   627  // SetCoinDenomRegex allows for coin's custom validation by overriding the regular
   628  // expression string used for denom validation.
   629  func SetCoinDenomRegex(reFn func() string) {
   630  	//coinDenomRegex = reFn
   631  
   632  	reDnm = regexp.MustCompile(fmt.Sprintf(`^%s$`, coinDenomRegex()))
   633  	reCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reAmt, reSpc, coinDenomRegex()))
   634  }
   635  
   636  // ValidateDenom validates a denomination string returning an error if it is
   637  // invalid.
   638  //func ValidateDenom(denom string) error {
   639  //	if !reDnm.MatchString(denom) {
   640  //		return fmt.Errorf("invalid denom: %s", denom)
   641  //	}
   642  //	return nil
   643  //}
   644  
   645  func mustValidateDenom(denom string) {
   646  	if err := ValidateDenom(denom); err != nil {
   647  		panic(err)
   648  	}
   649  }
   650  
   651  // ParseCoin parses a cli input for one coin type, returning errors if invalid.
   652  // This returns an error on an empty string as well.
   653  //func ParseCoin(coinStr string) (coin Coin, err error) {
   654  //	coinStr = strings.TrimSpace(coinStr)
   655  //
   656  //	matches := reCoin.FindStringSubmatch(coinStr)
   657  //	if matches == nil {
   658  //		return Coin{}, fmt.Errorf("invalid coin expression: %s", coinStr)
   659  //	}
   660  //
   661  //	denomStr, amountStr := matches[2], matches[1]
   662  //
   663  //	amount, ok := NewIntFromString(amountStr)
   664  //	if !ok {
   665  //		return Coin{}, fmt.Errorf("failed to parse coin amount: %s", amountStr)
   666  //	}
   667  //
   668  //	if err := ValidateDenom(denomStr); err != nil {
   669  //		return Coin{}, fmt.Errorf("invalid denom cannot contain upper case characters or spaces: %s", err)
   670  //	}
   671  //
   672  //	return NewCoin(denomStr, amount), nil
   673  //}
   674  
   675  // ParseCoins will parse out a list of coins separated by commas.
   676  // If nothing is provided, it returns nil Coins.
   677  // Returned coins are sorted.
   678  //func ParseCoins(coinsStr string) (Coins, error) {
   679  //	coinsStr = strings.TrimSpace(coinsStr)
   680  //	if len(coinsStr) == 0 {
   681  //		return nil, nil
   682  //	}
   683  //
   684  //	coinStrs := strings.Split(coinsStr, ",")
   685  //	coins := make(Coins, len(coinStrs))
   686  //	for i, coinStr := range coinStrs {
   687  //		coin, err := ParseCoin(coinStr)
   688  //		if err != nil {
   689  //			return nil, err
   690  //		}
   691  //
   692  //		coins[i] = coin
   693  //	}
   694  //
   695  //	// sort coins for determinism
   696  //	coins.Sort()
   697  //
   698  //	// validate coins before returning
   699  //	if !coins.IsValid() {
   700  //		return nil, fmt.Errorf("parseCoins invalid: %#v", coins)
   701  //	}
   702  //
   703  //	return coins, nil
   704  //}
   705  
   706  type findDupDescriptor interface {
   707  	GetDenomByIndex(int) string
   708  	Len() int
   709  }
   710  
   711  // findDup works on the assumption that coins is sorted
   712  func findDup(coins findDupDescriptor) int {
   713  	if coins.Len() <= 1 {
   714  		return -1
   715  	}
   716  
   717  	prevDenom := coins.GetDenomByIndex(0)
   718  	for i := 1; i < coins.Len(); i++ {
   719  		if coins.GetDenomByIndex(i) == prevDenom {
   720  			return i
   721  		}
   722  		prevDenom = coins.GetDenomByIndex(i)
   723  	}
   724  
   725  	return -1
   726  }