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.

block.go 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. package atto
  2. import (
  3. "encoding/hex"
  4. "encoding/json"
  5. "fmt"
  6. "math/big"
  7. "golang.org/x/crypto/blake2b"
  8. )
  9. var errInvalidSignature = fmt.Errorf("invalid block signature")
  10. // ErrSignatureMissing is used when the Signature of a Block is missing
  11. // but required for the attempted operation.
  12. var ErrSignatureMissing = fmt.Errorf("signature is missing")
  13. // ErrWorkMissing is used when the Work of a Block is missing but
  14. // required for the attempted operation.
  15. var ErrWorkMissing = fmt.Errorf("work is missing")
  16. // BlockSubType represents the sub-type of a block.
  17. type BlockSubType int64
  18. const (
  19. // SubTypeReceive denotes blocks which raise the balance.
  20. SubTypeReceive BlockSubType = iota
  21. // SubTypeChange denotes blocks which change the representative.
  22. SubTypeChange
  23. // SubTypeSend denotes blocks which lower the balance.
  24. SubTypeSend
  25. )
  26. // Block represents a block in the block chain of an account.
  27. type Block struct {
  28. Type string `json:"type"`
  29. Account string `json:"account"`
  30. Previous string `json:"previous"`
  31. Representative string `json:"representative"`
  32. Balance string `json:"balance"`
  33. Link string `json:"link"`
  34. Signature string `json:"signature"`
  35. Work string `json:"work"`
  36. // This field is not part of the JSON but needed to improve the
  37. // performance of FetchWork and the security of Submit.
  38. SubType BlockSubType `json:"-"`
  39. }
  40. type workGenerateResponse struct {
  41. Error string `json:"error"`
  42. Work string `json:"work"`
  43. }
  44. // Sign computes and sets the Signature of b.
  45. func (b *Block) Sign(privateKey *big.Int) error {
  46. publicKey, err := getPublicKeyFromAddress(b.Account)
  47. if err != nil {
  48. return err
  49. }
  50. hash, err := b.hashBytes()
  51. if err != nil {
  52. return err
  53. }
  54. signature, err := sign(publicKey, privateKey, hash)
  55. if err != nil {
  56. return err
  57. }
  58. b.Signature = fmt.Sprintf("%0128X", signature)
  59. return nil
  60. }
  61. func (b *Block) verifySignature(a Account) (err error) {
  62. sig, ok := big.NewInt(0).SetString(b.Signature, 16)
  63. if !ok {
  64. return fmt.Errorf("cannot parse '%s' as an integer", b.Signature)
  65. }
  66. hash, err := b.hashBytes()
  67. if err != nil {
  68. return err
  69. }
  70. if !isValidSignature(a.PublicKey, hash, bigIntToBytes(sig, 64)) {
  71. err = errInvalidSignature
  72. }
  73. return
  74. }
  75. // FetchWork uses the generate_work RPC on node to fetch and then set
  76. // the Work of b.
  77. func (b *Block) FetchWork(node string) error {
  78. var hash string
  79. if b.Previous == "0000000000000000000000000000000000000000000000000000000000000000" {
  80. publicKey, err := getPublicKeyFromAddress(b.Account)
  81. if err != nil {
  82. return err
  83. }
  84. hash = fmt.Sprintf("%064X", bigIntToBytes(publicKey, 32))
  85. } else {
  86. hash = b.Previous
  87. }
  88. requestBody := fmt.Sprintf(`{"action":"work_generate", "hash":"%s"`, string(hash))
  89. if b.SubType == SubTypeReceive {
  90. // Receive blocks need less work, so lower the difficulty.
  91. var receiveWorkThreshold uint64 = 0xfffffe0000000000
  92. requestBody += fmt.Sprintf(`, "difficulty":"%016x"`, receiveWorkThreshold)
  93. }
  94. requestBody += `}`
  95. responseBytes, err := doRPC(requestBody, node)
  96. if err != nil {
  97. return err
  98. }
  99. var response workGenerateResponse
  100. if err = json.Unmarshal(responseBytes, &response); err != nil {
  101. return err
  102. }
  103. // Need to check response.Error because of
  104. // https://github.com/nanocurrency/nano-node/issues/1782.
  105. if response.Error != "" {
  106. return fmt.Errorf("could not get work for block: %s", response.Error)
  107. }
  108. b.Work = response.Work
  109. return nil
  110. }
  111. // Hash calculates the block's hash and returns it's string
  112. // representation.
  113. func (b Block) Hash() (string, error) {
  114. hashBytes, err := b.hashBytes()
  115. if err != nil {
  116. return "", err
  117. }
  118. return fmt.Sprintf("%064X", hashBytes), nil
  119. }
  120. func (b Block) hashBytes() ([]byte, error) {
  121. // See https://nanoo.tools/block for a reference.
  122. msg := make([]byte, 176, 176)
  123. msg[31] = 0x6 // block preamble
  124. publicKey, err := getPublicKeyFromAddress(b.Account)
  125. if err != nil {
  126. return nil, err
  127. }
  128. copy(msg[32:64], bigIntToBytes(publicKey, 32))
  129. previous, err := hex.DecodeString(b.Previous)
  130. if err != nil {
  131. return nil, err
  132. }
  133. copy(msg[64:96], previous)
  134. representative, err := getPublicKeyFromAddress(b.Representative)
  135. if err != nil {
  136. return nil, err
  137. }
  138. copy(msg[96:128], bigIntToBytes(representative, 32))
  139. balance, ok := big.NewInt(0).SetString(b.Balance, 10)
  140. if !ok {
  141. return nil, fmt.Errorf("cannot parse '%s' as an integer", b.Balance)
  142. }
  143. copy(msg[128:144], bigIntToBytes(balance, 16))
  144. link, err := hex.DecodeString(b.Link)
  145. if err != nil {
  146. return nil, err
  147. }
  148. copy(msg[144:176], link)
  149. hash := blake2b.Sum256(msg)
  150. return hash[:], nil
  151. }
  152. // Submit submits the Block to the given node. Work and Signature of b
  153. // must be populated beforehand.
  154. //
  155. // May return ErrWorkMissing or ErrSignatureMissing.
  156. func (b Block) Submit(node string) error {
  157. if b.Work == "" {
  158. return ErrWorkMissing
  159. }
  160. if b.Signature == "" {
  161. return ErrSignatureMissing
  162. }
  163. var subType string
  164. switch b.SubType {
  165. case SubTypeReceive:
  166. subType = "receive"
  167. case SubTypeChange:
  168. subType = "change"
  169. case SubTypeSend:
  170. subType = "send"
  171. }
  172. process := process{
  173. Action: "process",
  174. JsonBlock: "true",
  175. SubType: subType,
  176. Block: b,
  177. }
  178. return doProcessRPC(process, node)
  179. }