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/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewEsploraClient(url string) Bitcoin {
|
func NewEsploraClient(url string) Bitcoin {
|
||||||
@@ -32,16 +33,23 @@ func (e esplora) GetBlockHash(height int64) (*chainhash.Hash, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
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
|
var chash chainhash.Hash
|
||||||
copy(chash[:], hexb)
|
copy(chash[:], hash)
|
||||||
return &chash, nil
|
return &chash, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e esplora) GetBlockHeader(hash *chainhash.Hash) (*wire.BlockHeader, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -50,12 +58,14 @@ func (e esplora) GetBlockHeader(hash *chainhash.Hash) (*wire.BlockHeader, error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if _, err := hex.Decode(hexb, hexb); err != nil {
|
|
||||||
|
headerHash, err := hex.DecodeString(string(hexb))
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
header := &wire.BlockHeader{}
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
22
ots.go
22
ots.go
@@ -100,6 +100,17 @@ func (a Instruction) Equal(b Instruction) bool {
|
|||||||
|
|
||||||
type Sequence []Instruction
|
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 {
|
func (ts Timestamp) GetPendingSequences() []Sequence {
|
||||||
bitcoin := ts.GetBitcoinAttestedSequences()
|
bitcoin := ts.GetBitcoinAttestedSequences()
|
||||||
|
|
||||||
@@ -238,14 +249,3 @@ func (att Attestation) Human() string {
|
|||||||
return "unknown/broken"
|
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) {
|
func (seq Sequence) Upgrade(ctx context.Context, initial []byte) (Sequence, error) {
|
||||||
result := ComputeSequence(initial, seq)
|
result := seq.Compute(initial)
|
||||||
attestation := seq[len(seq)-1]
|
attestation := seq[len(seq)-1]
|
||||||
|
|
||||||
url := fmt.Sprintf("%s/timestamp/%x", normalizeUrl(attestation.CalendarServerURL), result)
|
url := fmt.Sprintf("%s/timestamp/%x", normalizeUrl(attestation.CalendarServerURL), result)
|
||||||
|
|||||||
34
verifier.go
34
verifier.go
@@ -1,11 +1,45 @@
|
|||||||
package opentimestamps
|
package opentimestamps
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Bitcoin interface {
|
type Bitcoin interface {
|
||||||
GetBlockHash(height int64) (*chainhash.Hash, error)
|
GetBlockHash(height int64) (*chainhash.Hash, error)
|
||||||
GetBlockHeader(hash *chainhash.Hash) (*wire.BlockHeader, 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