diff --git a/prover/guardian_prover_sender/guardian_prover.go b/prover/guardian_prover_sender/guardian_prover.go index db3c25f95..bff8b34db 100644 --- a/prover/guardian_prover_sender/guardian_prover.go +++ b/prover/guardian_prover_sender/guardian_prover.go @@ -34,6 +34,14 @@ type signedBlockReq struct { Prover common.Address `json:"proverAddress"` } +// startupReq is the request body send to the health check server when the guardian prover starts up. +type startupReq struct { + ProverAddress string `json:"prover"` + Version string `json:"version"` + Revision string `json:"revision"` + Signature []byte `json:"signature"` +} + // GuardianProverBlockSender is responsible for signing and sending known blocks to the health check server. type GuardianProverBlockSender struct { privateKey *ecdsa.PrivateKey @@ -88,7 +96,7 @@ func (s *GuardianProverBlockSender) post(ctx context.Context, route string, req // SignAndSendBlock signs the given block and sends it to the health check server. func (s *GuardianProverBlockSender) SignAndSendBlock(ctx context.Context, blockID *big.Int) error { - signed, header, err := s.sign(ctx, blockID) + signed, header, err := s.signBlock(ctx, blockID) if err != nil { return nil } @@ -110,6 +118,41 @@ func (s *GuardianProverBlockSender) SignAndSendBlock(ctx context.Context, blockI ) } +func (s *GuardianProverBlockSender) SendStartup(ctx context.Context, revision string, version string) error { + if s.healthCheckServerEndpoint == nil { + log.Info("No health check server endpoint set, returning early") + return nil + } + + sig, err := crypto.Sign( + crypto.Keccak256Hash( + s.proverAddress.Bytes(), + []byte(revision), + []byte(version)).Bytes(), + s.privateKey) + if err != nil { + return err + } + + req := &startupReq{ + Revision: revision, + Version: version, + ProverAddress: s.proverAddress.Hex(), + Signature: sig, + } + + if err := s.post(ctx, "startup", req); err != nil { + return err + } + + log.Info("Guardian prover successfully sent startup", + "revision", revision, + "version", version, + ) + + return nil +} + // sendSignedBlockReq is the actual method that sends the signed block to the health check server. func (s *GuardianProverBlockSender) sendSignedBlockReq( ctx context.Context, @@ -139,7 +182,7 @@ func (s *GuardianProverBlockSender) sendSignedBlockReq( } // sign signs the given block and returns the signature and header. -func (s *GuardianProverBlockSender) sign(ctx context.Context, blockID *big.Int) ([]byte, *types.Header, error) { +func (s *GuardianProverBlockSender) signBlock(ctx context.Context, blockID *big.Int) ([]byte, *types.Header, error) { log.Info("Guardian prover signing block", "blockID", blockID.Uint64()) head, err := s.rpc.L2.BlockNumber(ctx) diff --git a/prover/guardian_prover_sender/interface.go b/prover/guardian_prover_sender/interface.go index 19abf2220..aa4d4503d 100644 --- a/prover/guardian_prover_sender/interface.go +++ b/prover/guardian_prover_sender/interface.go @@ -7,6 +7,7 @@ import ( type BlockSigner interface { SignAndSendBlock(ctx context.Context, blockID *big.Int) error + SendStartup(ctx context.Context, revision string, version string) error } type Heartbeater interface { diff --git a/prover/prover.go b/prover/prover.go index 65157574e..7a98890bf 100644 --- a/prover/prover.go +++ b/prover/prover.go @@ -7,6 +7,7 @@ import ( "fmt" "math/big" "net/http" + "runtime/debug" "sync" "time" @@ -400,6 +401,15 @@ func (p *Prover) Start() error { }() if p.IsGuardianProver() { + revision, version, err := getRevision() + if err != nil { + log.Crit("Unable to get revision", "error", err) + } + + if err := p.guardianProverSender.SendStartup(p.ctx, revision, version); err != nil { + log.Crit("Failed to send guardian prover startup", "error", err) + } + p.wg.Add(1) go p.heartbeatInterval(p.ctx) } @@ -1320,3 +1330,25 @@ func (p *Prover) heartbeatInterval(ctx context.Context) { } } } + +func getRevision() (string, string, error) { + var revision string + + var version string + + if info, ok := debug.ReadBuildInfo(); ok { + version = info.Main.Version + + for _, setting := range info.Settings { + if setting.Key == "vcs.revision" { + revision = setting.Value + } + } + } + + if revision == "" || version == "" { + return "", "", fmt.Errorf("unable to get revision or version. revision: %v, version: %v", revision, version) + } + + return revision, version, nil +}