@ -14,7 +14,10 @@
// You should have received a copy of the GNU Lesser General Public License
// 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/>.
// 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 (
import (
"encoding/binary"
"encoding/binary"
@ -28,10 +31,10 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/params"
)
)
// c heckpointOracle is responsible for offering the latest stable checkpoint
// C heckpointOracle is responsible for offering the latest stable checkpoint
// generated and announced by the contract admins on-chain. The checkpoint is
// generated and announced by the contract admins on-chain. The checkpoint can
// verified by clients locally during the checkpoint syncing.
// be verified by clients locally during the checkpoint syncing.
type c heckpointOracle struct {
type C heckpointOracle struct {
config * params . CheckpointOracleConfig
config * params . CheckpointOracleConfig
contract * checkpointoracle . CheckpointOracle
contract * checkpointoracle . CheckpointOracle
@ -39,8 +42,8 @@ type checkpointOracle struct {
getLocal func ( uint64 ) params . TrustedCheckpoint // Function used to retrieve local checkpoint
getLocal func ( uint64 ) params . TrustedCheckpoint // Function used to retrieve local checkpoint
}
}
// newCheckpointOracle returns a checkpoint registrar handler .
// New creates a checkpoint oracle handler with given configs and callback .
func newCheckpointOracle ( config * params . CheckpointOracleConfig , getLocal func ( uint64 ) params . TrustedCheckpoint ) * c heckpointOracle {
func New ( config * params . CheckpointOracleConfig , getLocal func ( uint64 ) params . TrustedCheckpoint ) * C heckpointOracle {
if config == nil {
if config == nil {
log . Info ( "Checkpoint registrar is not enabled" )
log . Info ( "Checkpoint registrar is not enabled" )
return nil
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 )
log . Info ( "Configured checkpoint registrar" , "address" , config . Address , "signers" , len ( config . Signers ) , "threshold" , config . Threshold )
return & c heckpointOracle{
return & C heckpointOracle{
config : config ,
config : config ,
getLocal : getLocal ,
getLocal : getLocal ,
}
}
}
}
// start binds the registrar contract and start listening to th e
// Start binds the contract backend, initializes the oracle instanc e
// newCheckpointEvent for the server sid e.
// and marks the status as availabl e.
func ( reg * checkpointOracle ) s tart( backend bind . ContractBackend ) {
func ( oracle * CheckpointOracle ) S tart( backend bind . ContractBackend ) {
contract , err := checkpointoracle . NewCheckpointOracle ( reg . config . Address , backend )
contract , err := checkpointoracle . NewCheckpointOracle ( o racl e. config . Address , backend )
if err != nil {
if err != nil {
log . Error ( "Oracle contract binding failed" , "err" , err )
log . Error ( "Oracle contract binding failed" , "err" , err )
return
return
}
}
if ! atomic . CompareAndSwapInt32 ( & reg . running , 0 , 1 ) {
if ! atomic . CompareAndSwapInt32 ( & o racl e. running , 0 , 1 ) {
log . Error ( "Already bound and listening to registrar" )
log . Error ( "Already bound and listening to registrar" )
return
return
}
}
reg . contract = contract
o racl e. contract = contract
}
}
// isRunning returns an indicator whether the registrar is running.
// IsRunning returns an indicator whether the oracle is running.
func ( reg * checkpointOracle ) i sRunning( ) bool {
func ( oracle * CheckpointOracle ) I sRunning( ) bool {
return atomic . LoadInt32 ( & reg . running ) == 1
return atomic . LoadInt32 ( & o racl e. 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.
// indexers and announced by trusted signers.
func ( reg * checkpointOracle ) stableCheckpoint ( ) ( * params . TrustedCheckpoint , uint64 ) {
func ( oracle * CheckpointOracle ) S tableCheckpoint( ) ( * params . TrustedCheckpoint , uint64 ) {
// Retrieve the latest checkpoint from the contract, abort if empty
// Retrieve the latest checkpoint from the contract, abort if empty
latest , hash , height , err := reg . contract . Contract ( ) . GetLatestCheckpoint ( nil )
latest , hash , height , err := o racl e. contract . Contract ( ) . GetLatestCheckpoint ( nil )
if err != nil || ( latest == 0 && hash == [ 32 ] byte { } ) {
if err != nil || ( latest == 0 && hash == [ 32 ] byte { } ) {
return nil , 0
return nil , 0
}
}
local := reg . getLocal ( latest )
local := o racl e. getLocal ( latest )
// The following scenarios may occur:
// The following scenarios may occur:
//
//
@ -93,19 +101,18 @@ func (reg *checkpointOracle) stableCheckpoint() (*params.TrustedCheckpoint, uint
// checkpoint which registered in the contract.
// checkpoint which registered in the contract.
// * local checkpoint doesn't match with the registered one.
// * local checkpoint doesn't match with the registered one.
//
//
// In both cases, server won't send the **stable** checkpoint
// In both cases, no stable checkpoint will be returned.
// to the client(no worry, client can use hardcoded one instead).
if local . HashEqual ( hash ) {
if local . HashEqual ( common . Hash ( hash ) ) {
return & local , height . Uint64 ( )
return & local , height . Uint64 ( )
}
}
return nil , 0
return nil , 0
}
}
// v erifySigners recovers the signer addresses according to the signature and
// V erifySigners recovers the signer addresses according to the signature and
// checks whether there are enough approvals to finalize the checkpoint.
// checks whether there are enough approvals to finalize the checkpoint.
func ( reg * checkpointOracle ) v erifySigners( index uint64 , hash [ 32 ] byte , signatures [ ] [ ] byte ) ( bool , [ ] common . Address ) {
func ( oracle * CheckpointOracle ) V erifySigners( index uint64 , hash [ 32 ] byte , signatures [ ] [ ] byte ) ( bool , [ ] common . Address ) {
// Short circuit if the given signatures doesn't reach the threshold.
// Short circuit if the given signatures doesn't reach the threshold.
if len ( signatures ) < int ( reg . config . Threshold ) {
if len ( signatures ) < int ( o racl e. config . Threshold ) {
return false , nil
return false , nil
}
}
var (
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)
// hash = keccak256(checkpoint_index, section_head, cht_root, bloom_root)
buf := make ( [ ] byte , 8 )
buf := make ( [ ] byte , 8 )
binary . BigEndian . PutUint64 ( buf , index )
binary . BigEndian . PutUint64 ( buf , index )
data := append ( [ ] byte { 0x19 , 0x00 } , append ( reg . config . Address . Bytes ( ) , append ( buf , hash [ : ] ... ) ... ) ... )
data := append ( [ ] byte { 0x19 , 0x00 } , append ( o racl e. 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.
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 ] )
pubkey , err := crypto . Ecrecover ( crypto . Keccak256 ( data ) , signatures [ i ] )
if err != nil {
if err != nil {
@ -139,14 +146,14 @@ func (reg *checkpointOracle) verifySigners(index uint64, hash [32]byte, signatur
if _ , exist := checked [ signer ] ; exist {
if _ , exist := checked [ signer ] ; exist {
continue
continue
}
}
for _ , s := range reg . config . Signers {
for _ , s := range o racl e. config . Signers {
if s == signer {
if s == signer {
signers = append ( signers , signer )
signers = append ( signers , signer )
checked [ signer ] = struct { } { }
checked [ signer ] = struct { } { }
}
}
}
}
}
}
threshold := reg . config . Threshold
threshold := o racl e. config . Threshold
if uint64 ( len ( signers ) ) < threshold {
if uint64 ( len ( signers ) ) < threshold {
log . Warn ( "Not enough signers to approve checkpoint" , "signers" , len ( signers ) , "threshold" , threshold )
log . Warn ( "Not enough signers to approve checkpoint" , "signers" , len ( signers ) , "threshold" , threshold )
return false , nil
return false , nil