oracle.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. // Copyright 2019 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. // Package checkpointoracle is a wrapper of checkpoint oracle contract with
  17. // additional rules defined. This package can be used both in LES client or
  18. // server side for offering oracle related APIs.
  19. package checkpointoracle
  20. import (
  21. "encoding/binary"
  22. "sync"
  23. "sync/atomic"
  24. "time"
  25. "github.com/ethereum/go-ethereum/accounts/abi/bind"
  26. "github.com/ethereum/go-ethereum/common"
  27. "github.com/ethereum/go-ethereum/contracts/checkpointoracle"
  28. "github.com/ethereum/go-ethereum/crypto"
  29. "github.com/ethereum/go-ethereum/log"
  30. "github.com/ethereum/go-ethereum/params"
  31. )
  32. // CheckpointOracle is responsible for offering the latest stable checkpoint
  33. // generated and announced by the contract admins on-chain. The checkpoint can
  34. // be verified by clients locally during the checkpoint syncing.
  35. type CheckpointOracle struct {
  36. config *params.CheckpointOracleConfig
  37. contract *checkpointoracle.CheckpointOracle
  38. running int32 // Flag whether the contract backend is set or not
  39. getLocal func(uint64) params.TrustedCheckpoint // Function used to retrieve local checkpoint
  40. checkMu sync.Mutex // Mutex to sync access to the fields below
  41. lastCheckTime time.Time // Time we last checked the checkpoint
  42. lastCheckPoint *params.TrustedCheckpoint // The last stable checkpoint
  43. lastCheckPointHeight uint64 // The height of last stable checkpoint
  44. }
  45. // New creates a checkpoint oracle handler with given configs and callback.
  46. func New(config *params.CheckpointOracleConfig, getLocal func(uint64) params.TrustedCheckpoint) *CheckpointOracle {
  47. return &CheckpointOracle{
  48. config: config,
  49. getLocal: getLocal,
  50. }
  51. }
  52. // Start binds the contract backend, initializes the oracle instance
  53. // and marks the status as available.
  54. func (oracle *CheckpointOracle) Start(backend bind.ContractBackend) {
  55. contract, err := checkpointoracle.NewCheckpointOracle(oracle.config.Address, backend)
  56. if err != nil {
  57. log.Error("Oracle contract binding failed", "err", err)
  58. return
  59. }
  60. if !atomic.CompareAndSwapInt32(&oracle.running, 0, 1) {
  61. log.Error("Already bound and listening to registrar")
  62. return
  63. }
  64. oracle.contract = contract
  65. }
  66. // IsRunning returns an indicator whether the oracle is running.
  67. func (oracle *CheckpointOracle) IsRunning() bool {
  68. return atomic.LoadInt32(&oracle.running) == 1
  69. }
  70. // Contract returns the underlying raw checkpoint oracle contract.
  71. func (oracle *CheckpointOracle) Contract() *checkpointoracle.CheckpointOracle {
  72. return oracle.contract
  73. }
  74. // StableCheckpoint returns the stable checkpoint which was generated by local
  75. // indexers and announced by trusted signers.
  76. func (oracle *CheckpointOracle) StableCheckpoint() (*params.TrustedCheckpoint, uint64) {
  77. oracle.checkMu.Lock()
  78. defer oracle.checkMu.Unlock()
  79. if time.Since(oracle.lastCheckTime) < 1*time.Minute {
  80. return oracle.lastCheckPoint, oracle.lastCheckPointHeight
  81. }
  82. // Look it up properly
  83. // Retrieve the latest checkpoint from the contract, abort if empty
  84. latest, hash, height, err := oracle.contract.Contract().GetLatestCheckpoint(nil)
  85. oracle.lastCheckTime = time.Now()
  86. if err != nil || (latest == 0 && hash == [32]byte{}) {
  87. oracle.lastCheckPointHeight = 0
  88. oracle.lastCheckPoint = nil
  89. return oracle.lastCheckPoint, oracle.lastCheckPointHeight
  90. }
  91. local := oracle.getLocal(latest)
  92. // The following scenarios may occur:
  93. //
  94. // * local node is out of sync so that it doesn't have the
  95. // checkpoint which registered in the contract.
  96. // * local checkpoint doesn't match with the registered one.
  97. //
  98. // In both cases, no stable checkpoint will be returned.
  99. if local.HashEqual(hash) {
  100. oracle.lastCheckPointHeight = height.Uint64()
  101. oracle.lastCheckPoint = &local
  102. return oracle.lastCheckPoint, oracle.lastCheckPointHeight
  103. }
  104. return nil, 0
  105. }
  106. // VerifySigners recovers the signer addresses according to the signature and
  107. // checks whether there are enough approvals to finalize the checkpoint.
  108. func (oracle *CheckpointOracle) VerifySigners(index uint64, hash [32]byte, signatures [][]byte) (bool, []common.Address) {
  109. // Short circuit if the given signatures doesn't reach the threshold.
  110. if len(signatures) < int(oracle.config.Threshold) {
  111. return false, nil
  112. }
  113. var (
  114. signers []common.Address
  115. checked = make(map[common.Address]struct{})
  116. )
  117. for i := 0; i < len(signatures); i++ {
  118. if len(signatures[i]) != 65 {
  119. continue
  120. }
  121. // EIP 191 style signatures
  122. //
  123. // Arguments when calculating hash to validate
  124. // 1: byte(0x19) - the initial 0x19 byte
  125. // 2: byte(0) - the version byte (data with intended validator)
  126. // 3: this - the validator address
  127. // -- Application specific data
  128. // 4 : checkpoint section_index (uint64)
  129. // 5 : checkpoint hash (bytes32)
  130. // hash = keccak256(checkpoint_index, section_head, cht_root, bloom_root)
  131. buf := make([]byte, 8)
  132. binary.BigEndian.PutUint64(buf, index)
  133. data := append([]byte{0x19, 0x00}, append(oracle.config.Address.Bytes(), append(buf, hash[:]...)...)...)
  134. signatures[i][64] -= 27 // Transform V from 27/28 to 0/1 according to the yellow paper for verification.
  135. pubkey, err := crypto.Ecrecover(crypto.Keccak256(data), signatures[i])
  136. if err != nil {
  137. return false, nil
  138. }
  139. var signer common.Address
  140. copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
  141. if _, exist := checked[signer]; exist {
  142. continue
  143. }
  144. for _, s := range oracle.config.Signers {
  145. if s == signer {
  146. signers = append(signers, signer)
  147. checked[signer] = struct{}{}
  148. }
  149. }
  150. }
  151. threshold := oracle.config.Threshold
  152. if uint64(len(signers)) < threshold {
  153. log.Warn("Not enough signers to approve checkpoint", "signers", len(signers), "threshold", threshold)
  154. return false, nil
  155. }
  156. return true, signers
  157. }