Files
opentimestamps/verifier.go
2023-09-19 21:18:40 -03:00

87 lines
2.2 KiB
Go

package opentimestamps
import (
"fmt"
"math"
"time"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
)
type Bitcoin interface {
GetBlockHash(height int64) (*chainhash.Hash, error)
GetBlockHeader(hash *chainhash.Hash) (*wire.BlockHeader, error)
}
// VerifyAttestation checks a BitcoinAttestation using a given hash digest. It
// returns the time of the block if the verification succeeds, an error
// otherwise.
func VerifyAttestation(bitcoinInterface Bitcoin, digest []byte, a *BitcoinAttestation) (*time.Time, error) {
if a.Height > math.MaxInt64 {
return nil, fmt.Errorf("illegal block height")
}
blockHash, err := bitcoinInterface.GetBlockHash(int64(a.Height))
if err != nil {
return nil, err
}
h, err := bitcoinInterface.GetBlockHeader(blockHash)
if err != nil {
return nil, err
}
merkleRootBytes := h.MerkleRoot[:]
err = a.VerifyAgainstBlockHash(digest, merkleRootBytes)
if err != nil {
return nil, err
}
utc := h.Timestamp.UTC()
return &utc, nil
}
// A BitcoinVerification is the result of verifying a BitcoinAttestation
type BitcoinVerification struct {
Timestamp *Timestamp
Attestation *BitcoinAttestation
AttestationTime *time.Time
Error error
}
// BitcoinVerifications returns the all bitcoin attestation results for the
// timestamp.
func BitcoinVerifications(bitcoinInterface Bitcoin, t *Timestamp) (res []BitcoinVerification) {
t.Walk(func(ts *Timestamp) {
for _, att := range ts.Attestations {
btcAtt, ok := att.(*BitcoinAttestation)
if !ok {
continue
}
attTime, err := VerifyAttestation(bitcoinInterface, ts.Message, btcAtt)
res = append(res, BitcoinVerification{
Timestamp: ts,
Attestation: btcAtt,
AttestationTime: attTime,
Error: err,
})
}
})
return res
}
// Verify returns the earliest bitcoin-attested time, or nil if none can be
// found or verified successfully.
func Verify(bitcoinInterface Bitcoin, t *Timestamp) (ret *time.Time, err error) {
res := BitcoinVerifications(bitcoinInterface, t)
for _, r := range res {
if r.Error != nil {
err = r.Error
continue
}
if ret == nil || r.AttestationTime.Before(*ret) {
ret = r.AttestationTime
}
}
return
}