copy the other repo and make packages install.
This commit is contained in:
183
attestations.go
Normal file
183
attestations.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package opentimestamps
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
attestationTagSize = 8
|
||||
attestationMaxPayloadSize = 8192
|
||||
pendingAttestationMaxUriLength = 1000
|
||||
)
|
||||
|
||||
var (
|
||||
bitcoinAttestationTag = mustDecodeHex("0588960d73d71901")
|
||||
pendingAttestationTag = mustDecodeHex("83dfe30d2ef90c8e")
|
||||
)
|
||||
|
||||
type Attestation interface {
|
||||
tag() []byte
|
||||
decode(*deserializationContext) (Attestation, error)
|
||||
encode(*serializationContext) error
|
||||
}
|
||||
|
||||
type baseAttestation struct {
|
||||
fixedTag []byte
|
||||
}
|
||||
|
||||
func (b *baseAttestation) tag() []byte {
|
||||
return b.fixedTag
|
||||
}
|
||||
|
||||
type pendingAttestation struct {
|
||||
baseAttestation
|
||||
uri string
|
||||
}
|
||||
|
||||
func newPendingAttestation() *pendingAttestation {
|
||||
return &pendingAttestation{
|
||||
baseAttestation: baseAttestation{
|
||||
fixedTag: pendingAttestationTag,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pendingAttestation) decode(
|
||||
ctx *deserializationContext,
|
||||
) (Attestation, error) {
|
||||
uri, err := ctx.readVarBytes(0, pendingAttestationMaxUriLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO utf8 checks
|
||||
ret := *p
|
||||
ret.uri = string(uri)
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
func (p *pendingAttestation) encode(ctx *serializationContext) error {
|
||||
return ctx.writeVarBytes([]byte(p.uri))
|
||||
}
|
||||
|
||||
func (p *pendingAttestation) String() string {
|
||||
return fmt.Sprintf("VERIFY PendingAttestation(url=%s)", p.uri)
|
||||
}
|
||||
|
||||
type BitcoinAttestation struct {
|
||||
baseAttestation
|
||||
Height uint64
|
||||
}
|
||||
|
||||
func newBitcoinAttestation() *BitcoinAttestation {
|
||||
return &BitcoinAttestation{
|
||||
baseAttestation: baseAttestation{bitcoinAttestationTag},
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BitcoinAttestation) String() string {
|
||||
return fmt.Sprintf("VERIFY BitcoinAttestation(height=%d)", b.Height)
|
||||
}
|
||||
|
||||
func (b *BitcoinAttestation) decode(
|
||||
ctx *deserializationContext,
|
||||
) (Attestation, error) {
|
||||
height, err := ctx.readVarUint()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := *b
|
||||
ret.Height = height
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
func (b *BitcoinAttestation) encode(ctx *serializationContext) error {
|
||||
return ctx.writeVarUint(uint64(b.Height))
|
||||
}
|
||||
|
||||
const hashMerkleRootSize = 32
|
||||
|
||||
//
|
||||
func (b *BitcoinAttestation) VerifyAgainstBlockHash(
|
||||
digest, blockHash []byte,
|
||||
) error {
|
||||
if len(digest) != hashMerkleRootSize {
|
||||
return fmt.Errorf("invalid digest size %d", len(digest))
|
||||
}
|
||||
if !bytes.Equal(digest, blockHash) {
|
||||
return fmt.Errorf(
|
||||
"hash mismatch digest=%x blockHash=%x",
|
||||
digest, blockHash,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// This is a catch-all for when we don't know how to parse it
|
||||
type unknownAttestation struct {
|
||||
tagBytes []byte
|
||||
bytes []byte
|
||||
}
|
||||
|
||||
func (u unknownAttestation) tag() []byte {
|
||||
return u.tagBytes
|
||||
}
|
||||
|
||||
func (unknownAttestation) decode(*deserializationContext) (Attestation, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (unknownAttestation) encode(*serializationContext) error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (u unknownAttestation) String() string {
|
||||
return fmt.Sprintf("UnknownAttestation(bytes=%q)", u.bytes)
|
||||
}
|
||||
|
||||
var attestations []Attestation = []Attestation{
|
||||
newPendingAttestation(),
|
||||
newBitcoinAttestation(),
|
||||
}
|
||||
|
||||
func encodeAttestation(ctx *serializationContext, att Attestation) error {
|
||||
if err := ctx.writeBytes(att.tag()); err != nil {
|
||||
return err
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
if err := att.encode(&serializationContext{buf}); err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.writeVarBytes(buf.Bytes())
|
||||
}
|
||||
|
||||
func ParseAttestation(ctx *deserializationContext) (Attestation, error) {
|
||||
tag, err := ctx.readBytes(attestationTagSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attBytes, err := ctx.readVarBytes(
|
||||
0, attestationMaxPayloadSize,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attCtx := newDeserializationContext(
|
||||
bytes.NewBuffer(attBytes),
|
||||
)
|
||||
|
||||
for _, a := range attestations {
|
||||
if bytes.Equal(tag, a.tag()) {
|
||||
att, err := a.decode(attCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !attCtx.assertEOF() {
|
||||
return nil, fmt.Errorf("expected EOF in attCtx")
|
||||
}
|
||||
return att, nil
|
||||
}
|
||||
}
|
||||
return unknownAttestation{tag, attBytes}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user