return bitcoin transaction from sequence computation.
This commit is contained in:
24
ots.go
24
ots.go
@@ -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 {
|
||||||
|
|||||||
2
stamp.go
2
stamp.go
@@ -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)
|
||||||
|
|||||||
23
verifier.go
23
verifier.go
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user