return bitcoin transaction from sequence computation.

This commit is contained in:
fiatjaf
2024-10-27 09:37:17 -03:00
parent 4cb1ec89c0
commit a0aba28a2a
3 changed files with 33 additions and 16 deletions

24
ots.go
View File

@@ -1,12 +1,14 @@
package opentimestamps package opentimestamps
import ( import (
"bytes"
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"slices"
"strings" "strings"
"slices" "github.com/btcsuite/btcd/wire"
) )
/* /*
@@ -82,15 +84,29 @@ func (seq Sequence) GetAttestation() Attestation {
return *att.Attestation return *att.Attestation
} }
func (seq Sequence) Compute(initial []byte) []byte { // Compute runs a sequence of operations on top of an initial digest and returns the result, which is often a
// Bitcoin block merkle root. It also tries to identify the point in the sequence in which an actual Bitcoin
// transaction is formed and parse that.
func (seq Sequence) Compute(initial []byte) (merkleRoot []byte, bitcoinTx *wire.MsgTx) {
current := initial current := initial
for _, inst := range seq { for i, inst := range seq {
if inst.Operation == nil { if inst.Operation == nil {
break break
} }
// the first time we do a double-sha256 that is likely a bitcoin transaction
if bitcoinTx == nil &&
inst.Operation.Name == "sha256" &&
len(seq) > i+1 && seq[i+1].Operation != nil &&
seq[i+1].Operation.Name == "sha256" {
tx := &wire.MsgTx{}
tx.Deserialize(bytes.NewReader(current))
bitcoinTx = tx
}
current = inst.Operation.Apply(current, inst.Argument) current = inst.Operation.Apply(current, inst.Argument)
} }
return current return current, bitcoinTx
} }
func (ts File) GetPendingSequences() []Sequence { func (ts File) GetPendingSequences() []Sequence {

View File

@@ -42,7 +42,7 @@ func ReadFromFile(data []byte) (*File, error) {
} }
func UpgradeSequence(ctx context.Context, seq Sequence, initial []byte) (Sequence, error) { func UpgradeSequence(ctx context.Context, seq Sequence, initial []byte) (Sequence, error) {
result := seq.Compute(initial) result, _ := seq.Compute(initial)
attestation := seq.GetAttestation() attestation := seq.GetAttestation()
url := fmt.Sprintf("%s/timestamp/%x", normalizeUrl(attestation.CalendarServerURL), result) url := fmt.Sprintf("%s/timestamp/%x", normalizeUrl(attestation.CalendarServerURL), result)

View File

@@ -1,10 +1,9 @@
package opentimestamps package opentimestamps
import ( import (
"bytes"
"fmt" "fmt"
"slices"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
) )
@@ -14,33 +13,35 @@ type Bitcoin interface {
GetBlockHeader(hash *chainhash.Hash) (*wire.BlockHeader, error) GetBlockHeader(hash *chainhash.Hash) (*wire.BlockHeader, error)
} }
func (seq Sequence) Verify(bitcoin Bitcoin, initial []byte) error { // Verify validates sequence of operations that starts with digest and ends on a Bitcoin attestation against
// an actual Bitcoin block, as given by the provided Bitcoin interface.
func (seq Sequence) Verify(bitcoin Bitcoin, digest []byte) (*wire.MsgTx, error) {
if len(seq) == 0 { if len(seq) == 0 {
return fmt.Errorf("empty sequence") return nil, fmt.Errorf("empty sequence")
} }
att := seq[len(seq)-1] att := seq[len(seq)-1]
if att.Attestation == nil || att.BitcoinBlockHeight == 0 { if att.Attestation == nil || att.BitcoinBlockHeight == 0 {
return fmt.Errorf("sequence doesn't include a bitcoin attestation") return nil, fmt.Errorf("sequence doesn't include a bitcoin attestation")
} }
blockHash, err := bitcoin.GetBlockHash(int64(att.BitcoinBlockHeight)) blockHash, err := bitcoin.GetBlockHash(int64(att.BitcoinBlockHeight))
if err != nil { if err != nil {
return fmt.Errorf("failed to get block %d hash: %w", att.BitcoinBlockHeight, err) return nil, fmt.Errorf("failed to get block %d hash: %w", att.BitcoinBlockHeight, err)
} }
blockHeader, err := bitcoin.GetBlockHeader(blockHash) blockHeader, err := bitcoin.GetBlockHeader(blockHash)
if err != nil { if err != nil {
return fmt.Errorf("failed to get block %s header: %w", blockHash, err) return nil, fmt.Errorf("failed to get block %s header: %w", blockHash, err)
} }
merkleRoot := blockHeader.MerkleRoot[:] merkleRoot := blockHeader.MerkleRoot[:]
result, tx := seq.Compute(digest)
result := seq.Compute(initial) if !bytes.Equal(result, merkleRoot) {
if !slices.Equal(result, merkleRoot) { return nil, fmt.Errorf("sequence result '%x' doesn't match the bitcoin merkle root for block %d: %x",
return fmt.Errorf("sequence result '%x' doesn't match the bitcoin merkle root for block %d: %x",
result, att.BitcoinBlockHeight, merkleRoot) result, att.BitcoinBlockHeight, merkleRoot)
} }
return nil return tx, nil
} }