implement verifier.
This commit is contained in:
20
esplora.go
20
esplora.go
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func NewEsploraClient(url string) Bitcoin {
|
||||
@@ -32,16 +33,23 @@ func (e esplora) GetBlockHash(height int64) (*chainhash.Hash, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := hex.Decode(hexb, hexb); err != nil || len(hexb) != chainhash.HashSize {
|
||||
|
||||
hash, err := hex.DecodeString(string(hexb))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(hash) != chainhash.HashSize {
|
||||
return nil, fmt.Errorf("got block hash (%x) of invalid size (expected %d)", hash, chainhash.HashSize)
|
||||
}
|
||||
|
||||
slices.Reverse(hash)
|
||||
var chash chainhash.Hash
|
||||
copy(chash[:], hexb)
|
||||
copy(chash[:], hash)
|
||||
return &chash, nil
|
||||
}
|
||||
|
||||
func (e esplora) GetBlockHeader(hash *chainhash.Hash) (*wire.BlockHeader, error) {
|
||||
resp, err := http.Get(fmt.Sprintf("%s/block/%x/header", e.baseurl, hash))
|
||||
resp, err := http.Get(fmt.Sprintf("%s/block/%s/header", e.baseurl, hash.String()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -50,12 +58,14 @@ func (e esplora) GetBlockHeader(hash *chainhash.Hash) (*wire.BlockHeader, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := hex.Decode(hexb, hexb); err != nil {
|
||||
|
||||
headerHash, err := hex.DecodeString(string(hexb))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
header := &wire.BlockHeader{}
|
||||
if err := header.BtcDecode(bytes.NewBuffer(hexb), 0, 0); err != nil {
|
||||
if err := header.BtcDecode(bytes.NewBuffer(headerHash), 0, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
22
ots.go
22
ots.go
@@ -100,6 +100,17 @@ func (a Instruction) Equal(b Instruction) bool {
|
||||
|
||||
type Sequence []Instruction
|
||||
|
||||
func (seq Sequence) Compute(initial []byte) []byte {
|
||||
current := initial
|
||||
for _, inst := range seq {
|
||||
if inst.Operation == nil {
|
||||
break
|
||||
}
|
||||
current = inst.Operation.Apply(current, inst.Argument)
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
func (ts Timestamp) GetPendingSequences() []Sequence {
|
||||
bitcoin := ts.GetBitcoinAttestedSequences()
|
||||
|
||||
@@ -238,14 +249,3 @@ func (att Attestation) Human() string {
|
||||
return "unknown/broken"
|
||||
}
|
||||
}
|
||||
|
||||
func ComputeSequence(initial []byte, seq []Instruction) []byte {
|
||||
current := initial
|
||||
for _, inst := range seq {
|
||||
if inst.Operation == nil {
|
||||
break
|
||||
}
|
||||
current = inst.Operation.Apply(current, inst.Argument)
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
2
stamp.go
2
stamp.go
@@ -45,7 +45,7 @@ func ReadFromFile(data []byte) (*Timestamp, error) {
|
||||
}
|
||||
|
||||
func (seq Sequence) Upgrade(ctx context.Context, initial []byte) (Sequence, error) {
|
||||
result := ComputeSequence(initial, seq)
|
||||
result := seq.Compute(initial)
|
||||
attestation := seq[len(seq)-1]
|
||||
|
||||
url := fmt.Sprintf("%s/timestamp/%x", normalizeUrl(attestation.CalendarServerURL), result)
|
||||
|
||||
34
verifier.go
34
verifier.go
@@ -1,11 +1,45 @@
|
||||
package opentimestamps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type Bitcoin interface {
|
||||
GetBlockHash(height int64) (*chainhash.Hash, error)
|
||||
GetBlockHeader(hash *chainhash.Hash) (*wire.BlockHeader, error)
|
||||
}
|
||||
|
||||
func (seq Sequence) Verify(bitcoin Bitcoin, initial []byte) error {
|
||||
if len(seq) == 0 {
|
||||
return fmt.Errorf("empty sequence")
|
||||
}
|
||||
|
||||
att := seq[len(seq)-1]
|
||||
if att.Attestation == nil || att.BitcoinBlockHeight == 0 {
|
||||
return fmt.Errorf("sequence doesn't include a bitcoin attestation")
|
||||
}
|
||||
|
||||
blockHash, err := bitcoin.GetBlockHash(int64(att.BitcoinBlockHeight))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get block %d hash: %w", att.BitcoinBlockHeight, err)
|
||||
}
|
||||
|
||||
blockHeader, err := bitcoin.GetBlockHeader(blockHash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get block %s header: %w", blockHash, err)
|
||||
}
|
||||
|
||||
merkleRoot := blockHeader.MerkleRoot[:]
|
||||
|
||||
result := seq.Compute(initial)
|
||||
if slices.Equal(result, merkleRoot) {
|
||||
return fmt.Errorf("sequence result '%x' doesn't match the bitcoin merkle root for block %d: %x",
|
||||
result, att.BitcoinBlockHeight, merkleRoot)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user