|
|
@@ -14,7 +14,10 @@
|
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
-package les
|
|
|
+// Package checkpointoracle is a wrapper of checkpoint oracle contract with
|
|
|
+// additional rules defined. This package can be used both in LES client or
|
|
|
+// server side for offering oracle related APIs.
|
|
|
+package checkpointoracle
|
|
|
|
|
|
import (
|
|
|
"encoding/binary"
|
|
|
@@ -28,10 +31,10 @@ import (
|
|
|
"github.com/ethereum/go-ethereum/params"
|
|
|
)
|
|
|
|
|
|
-// checkpointOracle is responsible for offering the latest stable checkpoint
|
|
|
-// generated and announced by the contract admins on-chain. The checkpoint is
|
|
|
-// verified by clients locally during the checkpoint syncing.
|
|
|
-type checkpointOracle struct {
|
|
|
+// CheckpointOracle is responsible for offering the latest stable checkpoint
|
|
|
+// generated and announced by the contract admins on-chain. The checkpoint can
|
|
|
+// be verified by clients locally during the checkpoint syncing.
|
|
|
+type CheckpointOracle struct {
|
|
|
config *params.CheckpointOracleConfig
|
|
|
contract *checkpointoracle.CheckpointOracle
|
|
|
|
|
|
@@ -39,8 +42,8 @@ type checkpointOracle struct {
|
|
|
getLocal func(uint64) params.TrustedCheckpoint // Function used to retrieve local checkpoint
|
|
|
}
|
|
|
|
|
|
-// newCheckpointOracle returns a checkpoint registrar handler.
|
|
|
-func newCheckpointOracle(config *params.CheckpointOracleConfig, getLocal func(uint64) params.TrustedCheckpoint) *checkpointOracle {
|
|
|
+// New creates a checkpoint oracle handler with given configs and callback.
|
|
|
+func New(config *params.CheckpointOracleConfig, getLocal func(uint64) params.TrustedCheckpoint) *CheckpointOracle {
|
|
|
if config == nil {
|
|
|
log.Info("Checkpoint registrar is not enabled")
|
|
|
return nil
|
|
|
@@ -51,41 +54,46 @@ func newCheckpointOracle(config *params.CheckpointOracleConfig, getLocal func(ui
|
|
|
}
|
|
|
log.Info("Configured checkpoint registrar", "address", config.Address, "signers", len(config.Signers), "threshold", config.Threshold)
|
|
|
|
|
|
- return &checkpointOracle{
|
|
|
+ return &CheckpointOracle{
|
|
|
config: config,
|
|
|
getLocal: getLocal,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// start binds the registrar contract and start listening to the
|
|
|
-// newCheckpointEvent for the server side.
|
|
|
-func (reg *checkpointOracle) start(backend bind.ContractBackend) {
|
|
|
- contract, err := checkpointoracle.NewCheckpointOracle(reg.config.Address, backend)
|
|
|
+// Start binds the contract backend, initializes the oracle instance
|
|
|
+// and marks the status as available.
|
|
|
+func (oracle *CheckpointOracle) Start(backend bind.ContractBackend) {
|
|
|
+ contract, err := checkpointoracle.NewCheckpointOracle(oracle.config.Address, backend)
|
|
|
if err != nil {
|
|
|
log.Error("Oracle contract binding failed", "err", err)
|
|
|
return
|
|
|
}
|
|
|
- if !atomic.CompareAndSwapInt32(®.running, 0, 1) {
|
|
|
+ if !atomic.CompareAndSwapInt32(&oracle.running, 0, 1) {
|
|
|
log.Error("Already bound and listening to registrar")
|
|
|
return
|
|
|
}
|
|
|
- reg.contract = contract
|
|
|
+ oracle.contract = contract
|
|
|
}
|
|
|
|
|
|
-// isRunning returns an indicator whether the registrar is running.
|
|
|
-func (reg *checkpointOracle) isRunning() bool {
|
|
|
- return atomic.LoadInt32(®.running) == 1
|
|
|
+// IsRunning returns an indicator whether the oracle is running.
|
|
|
+func (oracle *CheckpointOracle) IsRunning() bool {
|
|
|
+ return atomic.LoadInt32(&oracle.running) == 1
|
|
|
}
|
|
|
|
|
|
-// stableCheckpoint returns the stable checkpoint which was generated by local
|
|
|
+// Contract returns the underlying raw checkpoint oracle contract.
|
|
|
+func (oracle *CheckpointOracle) Contract() *checkpointoracle.CheckpointOracle {
|
|
|
+ return oracle.contract
|
|
|
+}
|
|
|
+
|
|
|
+// StableCheckpoint returns the stable checkpoint which was generated by local
|
|
|
// indexers and announced by trusted signers.
|
|
|
-func (reg *checkpointOracle) stableCheckpoint() (*params.TrustedCheckpoint, uint64) {
|
|
|
+func (oracle *CheckpointOracle) StableCheckpoint() (*params.TrustedCheckpoint, uint64) {
|
|
|
// Retrieve the latest checkpoint from the contract, abort if empty
|
|
|
- latest, hash, height, err := reg.contract.Contract().GetLatestCheckpoint(nil)
|
|
|
+ latest, hash, height, err := oracle.contract.Contract().GetLatestCheckpoint(nil)
|
|
|
if err != nil || (latest == 0 && hash == [32]byte{}) {
|
|
|
return nil, 0
|
|
|
}
|
|
|
- local := reg.getLocal(latest)
|
|
|
+ local := oracle.getLocal(latest)
|
|
|
|
|
|
// The following scenarios may occur:
|
|
|
//
|
|
|
@@ -93,19 +101,18 @@ func (reg *checkpointOracle) stableCheckpoint() (*params.TrustedCheckpoint, uint
|
|
|
// checkpoint which registered in the contract.
|
|
|
// * local checkpoint doesn't match with the registered one.
|
|
|
//
|
|
|
- // In both cases, server won't send the **stable** checkpoint
|
|
|
- // to the client(no worry, client can use hardcoded one instead).
|
|
|
- if local.HashEqual(common.Hash(hash)) {
|
|
|
+ // In both cases, no stable checkpoint will be returned.
|
|
|
+ if local.HashEqual(hash) {
|
|
|
return &local, height.Uint64()
|
|
|
}
|
|
|
return nil, 0
|
|
|
}
|
|
|
|
|
|
-// verifySigners recovers the signer addresses according to the signature and
|
|
|
+// VerifySigners recovers the signer addresses according to the signature and
|
|
|
// checks whether there are enough approvals to finalize the checkpoint.
|
|
|
-func (reg *checkpointOracle) verifySigners(index uint64, hash [32]byte, signatures [][]byte) (bool, []common.Address) {
|
|
|
+func (oracle *CheckpointOracle) VerifySigners(index uint64, hash [32]byte, signatures [][]byte) (bool, []common.Address) {
|
|
|
// Short circuit if the given signatures doesn't reach the threshold.
|
|
|
- if len(signatures) < int(reg.config.Threshold) {
|
|
|
+ if len(signatures) < int(oracle.config.Threshold) {
|
|
|
return false, nil
|
|
|
}
|
|
|
var (
|
|
|
@@ -128,7 +135,7 @@ func (reg *checkpointOracle) verifySigners(index uint64, hash [32]byte, signatur
|
|
|
// hash = keccak256(checkpoint_index, section_head, cht_root, bloom_root)
|
|
|
buf := make([]byte, 8)
|
|
|
binary.BigEndian.PutUint64(buf, index)
|
|
|
- data := append([]byte{0x19, 0x00}, append(reg.config.Address.Bytes(), append(buf, hash[:]...)...)...)
|
|
|
+ data := append([]byte{0x19, 0x00}, append(oracle.config.Address.Bytes(), append(buf, hash[:]...)...)...)
|
|
|
signatures[i][64] -= 27 // Transform V from 27/28 to 0/1 according to the yellow paper for verification.
|
|
|
pubkey, err := crypto.Ecrecover(crypto.Keccak256(data), signatures[i])
|
|
|
if err != nil {
|
|
|
@@ -139,14 +146,14 @@ func (reg *checkpointOracle) verifySigners(index uint64, hash [32]byte, signatur
|
|
|
if _, exist := checked[signer]; exist {
|
|
|
continue
|
|
|
}
|
|
|
- for _, s := range reg.config.Signers {
|
|
|
+ for _, s := range oracle.config.Signers {
|
|
|
if s == signer {
|
|
|
signers = append(signers, signer)
|
|
|
checked[signer] = struct{}{}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- threshold := reg.config.Threshold
|
|
|
+ threshold := oracle.config.Threshold
|
|
|
if uint64(len(signers)) < threshold {
|
|
|
log.Warn("Not enough signers to approve checkpoint", "signers", len(signers), "threshold", threshold)
|
|
|
return false, nil
|