Files
opentimestamps/operations.go
2023-09-19 21:18:40 -03:00

159 lines
3.4 KiB
Go

package opentimestamps
import (
"crypto/sha256"
"encoding/hex"
"fmt"
)
const maxResultLength = 4096
type (
unaryMsgOp func(message []byte) ([]byte, error)
binaryMsgOp func(message, argument []byte) ([]byte, error)
)
// msgAppend returns the concatenation of msg and arg
func msgAppend(msg, arg []byte) (res []byte, err error) {
res = append(res, msg...)
res = append(res, arg...)
return
}
// msgPrepend returns the concatenation of arg and msg
func msgPrepend(msg, arg []byte) (res []byte, err error) {
res = append(res, arg...)
res = append(res, msg...)
return
}
// msgReverse returns the reversed msg. Deprecated.
func msgReverse(msg []byte) ([]byte, error) {
if len(msg) == 0 {
return nil, fmt.Errorf("empty input invalid for msgReverse")
}
res := make([]byte, len(msg))
for i, b := range msg {
res[len(res)-i-1] = b
}
return res, nil
}
func msgHexlify(msg []byte) ([]byte, error) {
if len(msg) == 0 {
return nil, fmt.Errorf("empty input invalid for msgHexlify")
}
return []byte(hex.EncodeToString(msg)), nil
}
type opCode interface {
match(byte) bool
decode(*deserializationContext) (opCode, error)
encode(*serializationContext) error
apply(message []byte) ([]byte, error)
}
type op struct {
tag byte
name string
}
func (o op) match(tag byte) bool {
return o.tag == tag
}
type unaryOp struct {
op
msgOp unaryMsgOp
}
func newUnaryOp(tag byte, name string, msgOp unaryMsgOp) *unaryOp {
return &unaryOp{op{tag: tag, name: name}, msgOp}
}
func (u *unaryOp) String() string {
return u.name
}
func (u *unaryOp) decode(ctx *deserializationContext) (opCode, error) {
ret := *u
return &ret, nil
}
func (u *unaryOp) encode(ctx *serializationContext) error {
return ctx.writeByte(u.tag)
}
func (u *unaryOp) apply(message []byte) ([]byte, error) {
return u.msgOp(message)
}
// Binary operations
// We decode an extra varbyte argument and use it in apply()
type binaryOp struct {
op
msgOp binaryMsgOp
argument []byte
}
func newBinaryOp(tag byte, name string, msgOp binaryMsgOp) *binaryOp {
return &binaryOp{
op: op{tag: tag, name: name},
msgOp: msgOp,
argument: nil,
}
}
func (b *binaryOp) decode(ctx *deserializationContext) (opCode, error) {
arg, err := ctx.readVarBytes(0, maxResultLength)
if err != nil {
return nil, err
}
if len(arg) == 0 {
return nil, fmt.Errorf("empty argument invalid for binaryOp")
}
ret := *b
ret.argument = arg
return &ret, nil
}
func (b *binaryOp) encode(ctx *serializationContext) error {
if err := ctx.writeByte(b.tag); err != nil {
return err
}
return ctx.writeVarBytes(b.argument)
}
func (b *binaryOp) apply(message []byte) ([]byte, error) {
return b.msgOp(message, b.argument)
}
func (b *binaryOp) String() string {
return fmt.Sprintf("%s %x", b.name, b.argument)
}
func msgSHA256(msg []byte) ([]byte, error) {
res := sha256.Sum256(msg)
return res[:], nil
}
var (
opAppend = newBinaryOp(0xf0, "APPEND", msgAppend)
opPrepend = newBinaryOp(0xf1, "PREPEND", msgPrepend)
opReverse = newUnaryOp(0xf2, "REVERSE", msgReverse)
opHexlify = newUnaryOp(0xf3, "HEXLIFY", msgHexlify)
opSHA256 = newUnaryOp(0x08, "SHA256", msgSHA256)
)
var opCodes []opCode = []opCode{opAppend, opPrepend, opReverse, opHexlify, opSHA256}
func parseOp(ctx *deserializationContext, tag byte) (opCode, error) {
for _, op := range opCodes {
if op.match(tag) {
return op.decode(ctx)
}
}
return nil, fmt.Errorf("could not decode tag %02x", tag)
}