You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

account_info.go 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. package atto
  2. import (
  3. "fmt"
  4. "math/big"
  5. "regexp"
  6. "strings"
  7. )
  8. // AccountInfo holds the basic data needed for Block creation.
  9. type AccountInfo struct {
  10. // Ignore this field. It only exists because of
  11. // https://github.com/nanocurrency/nano-node/issues/1782.
  12. Error string `json:"error"`
  13. Frontier string `json:"frontier"`
  14. Representative string `json:"representative"`
  15. Balance string `json:"balance"`
  16. PublicKey *big.Int `json:"-"`
  17. Address string `json:"-"`
  18. }
  19. // Send creates a send block, which will still be missing its signature
  20. // and work. The Frontier and Balance of the AccountInfo will be
  21. // updated. The amount is interpreted as Nano, not raw!
  22. func (i *AccountInfo) Send(amount, toAddr string) (Block, error) {
  23. balance, err := getBalanceAfterSend(i.Balance, amount)
  24. if err != nil {
  25. return Block{}, err
  26. }
  27. recipientNumber, err := getPublicKeyFromAddress(toAddr)
  28. if err != nil {
  29. return Block{}, err
  30. }
  31. recipientBytes := bigIntToBytes(recipientNumber, 32)
  32. block := Block{
  33. Type: "state",
  34. SubType: SubTypeSend,
  35. Account: i.Address,
  36. Previous: i.Frontier,
  37. Representative: i.Representative,
  38. Balance: balance.String(),
  39. Link: fmt.Sprintf("%064X", recipientBytes),
  40. }
  41. hash, err := block.Hash()
  42. if err != nil {
  43. return Block{}, err
  44. }
  45. i.Frontier = hash
  46. i.Balance = block.Balance
  47. return block, err
  48. }
  49. func getBalanceAfterSend(oldBalance string, amount string) (*big.Int, error) {
  50. balance, ok := big.NewInt(0).SetString(oldBalance, 10)
  51. if !ok {
  52. err := fmt.Errorf("cannot parse '%s' as an integer", oldBalance)
  53. return nil, err
  54. }
  55. amountRaw, err := nanoStringToRaw(amount)
  56. if err != nil {
  57. return nil, err
  58. }
  59. return balance.Sub(balance, amountRaw), nil
  60. }
  61. func nanoStringToRaw(amountString string) (*big.Int, error) {
  62. pattern := `^([0-9]+|[0-9]*\.[0-9]{1,30})$`
  63. amountOk, err := regexp.MatchString(pattern, amountString)
  64. if !amountOk {
  65. return nil, fmt.Errorf("'%s' is no legal amountString", amountString)
  66. } else if err != nil {
  67. return nil, err
  68. }
  69. missingZerosUntilRaw := 30
  70. if i := strings.Index(amountString, "."); i > -1 {
  71. missingZerosUntilRaw -= len(amountString) - i - 1
  72. amountString = strings.Replace(amountString, ".", "", 1)
  73. }
  74. amountString += strings.Repeat("0", missingZerosUntilRaw)
  75. amount, ok := big.NewInt(0).SetString(amountString, 10)
  76. if !ok {
  77. err := fmt.Errorf("cannot parse '%s' as an interger", amountString)
  78. return nil, err
  79. }
  80. return amount, nil
  81. }
  82. // Change creates a change block, which will still be missing its
  83. // signature and work. The Frontier and Representative of the
  84. // AccountInfo will be updated.
  85. func (i *AccountInfo) Change(representative string) (Block, error) {
  86. block := Block{
  87. Type: "state",
  88. SubType: SubTypeChange,
  89. Account: i.Address,
  90. Previous: i.Frontier,
  91. Representative: representative,
  92. Balance: i.Balance,
  93. Link: "0000000000000000000000000000000000000000000000000000000000000000",
  94. }
  95. hash, err := block.Hash()
  96. if err != nil {
  97. return Block{}, err
  98. }
  99. i.Frontier = hash
  100. return block, err
  101. }
  102. // Receive creates a receive block, which will still be missing its
  103. // signature and work. The Frontier and Balance of the AccountInfo will
  104. // be updated.
  105. func (i *AccountInfo) Receive(pending Pending) (Block, error) {
  106. updatedBalance, ok := big.NewInt(0).SetString(i.Balance, 10)
  107. if !ok {
  108. err := fmt.Errorf("cannot parse '%s' as an integer", i.Balance)
  109. return Block{}, err
  110. }
  111. amount, ok := big.NewInt(0).SetString(pending.Amount, 10)
  112. if !ok {
  113. err := fmt.Errorf("cannot parse '%s' as an integer", pending.Amount)
  114. return Block{}, err
  115. }
  116. if amount.Sign() < 1 {
  117. err := fmt.Errorf("amount '%s' is not positive", pending.Amount)
  118. return Block{}, err
  119. }
  120. updatedBalance.Add(updatedBalance, amount)
  121. block := Block{
  122. Type: "state",
  123. SubType: SubTypeReceive,
  124. Account: i.Address,
  125. Previous: i.Frontier,
  126. Representative: i.Representative,
  127. Balance: updatedBalance.String(),
  128. Link: pending.Hash,
  129. }
  130. hash, err := block.Hash()
  131. if err != nil {
  132. return Block{}, err
  133. }
  134. i.Frontier = hash
  135. i.Balance = block.Balance
  136. return block, err
  137. }