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 }