-
Notifications
You must be signed in to change notification settings - Fork 5
/
stamp.go
132 lines (117 loc) · 3.62 KB
/
stamp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright 2020 The Penguin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package postage
import (
"bytes"
"errors"
"fmt"
"github.com/penguintop/penguin/pkg/crypto"
"github.com/penguintop/penguin/pkg/storage"
"github.com/penguintop/penguin/pkg/penguin"
)
// StampSize is the number of bytes in the serialisation of a stamp
const StampSize = 97
var (
// ErrOwnerMismatch is the error given for invalid signatures.
ErrOwnerMismatch = errors.New("owner mismatch")
// ErrStampInvalid is the error given if stamp cannot deserialise.
ErrStampInvalid = errors.New("invalid stamp")
)
// Valid checks the validity of the postage stamp; in particular:
// - authenticity - check batch is valid on the blockchain
// - authorisation - the batch owner is the stamp signer
// the validity check is only meaningful in its association of a chunk
// this chunk address needs to be given as argument
func (s *Stamp) Valid(chunkAddr penguin.Address, ownerAddr []byte) error {
toSign, err := toSignDigest(chunkAddr, s.batchID)
if err != nil {
return err
}
signerPubkey, err := crypto.Recover(s.sig, toSign)
if err != nil {
return err
}
//signerAddr, err := crypto.NewEthereumAddress(*signerPubkey)
//if err != nil {
// return err
//}
signerAddr, err := crypto.NewXwcAddress(*signerPubkey)
if err != nil {
return err
}
if !bytes.Equal(signerAddr, ownerAddr) {
return ErrOwnerMismatch
}
return nil
}
var _ penguin.Stamp = (*Stamp)(nil)
// Stamp represents a postage stamp as attached to a chunk.
type Stamp struct {
batchID []byte // postage batch ID
sig []byte // common r[32]s[32]v[1]-style 65 byte ECDSA signature
}
// NewStamp constructs a new stamp from a given batch ID and signature.
func NewStamp(batchID, sig []byte) *Stamp {
return &Stamp{batchID, sig}
}
// BatchID returns the batch ID of the stamp.
func (s *Stamp) BatchID() []byte {
return s.batchID
}
// Sig returns the signature of the stamp.
func (s *Stamp) Sig() []byte {
return s.sig
}
// MarshalBinary gives the byte slice serialisation of a stamp:
// batchID[32]|Signature[65].
func (s *Stamp) MarshalBinary() ([]byte, error) {
buf := make([]byte, StampSize)
copy(buf, s.batchID)
copy(buf[32:], s.sig)
return buf, nil
}
// UnmarshalBinary parses a serialised stamp into id and signature.
func (s *Stamp) UnmarshalBinary(buf []byte) error {
if len(buf) != StampSize {
return ErrStampInvalid
}
s.batchID = buf[:32]
s.sig = buf[32:]
return nil
}
// toSignDigest creates a digest to represent the stamp which is to be signed by
// the owner.
func toSignDigest(addr penguin.Address, id []byte) ([]byte, error) {
h := penguin.NewHasher()
_, err := h.Write(addr.Bytes())
if err != nil {
return nil, err
}
_, err = h.Write(id)
if err != nil {
return nil, err
}
return h.Sum(nil), nil
}
// ValidStamp returns a stampvalidator function passed to protocols with chunk entrypoints.
func ValidStamp(batchStore Storer) func(chunk penguin.Chunk, stampBytes []byte) (penguin.Chunk, error) {
return func(chunk penguin.Chunk, stampBytes []byte) (penguin.Chunk, error) {
stamp := new(Stamp)
err := stamp.UnmarshalBinary(stampBytes)
if err != nil {
return nil, err
}
b, err := batchStore.Get(stamp.BatchID())
if err != nil {
if errors.Is(err, storage.ErrNotFound) {
return nil, fmt.Errorf("batchstore get: %v, %w", err, ErrNotFound)
}
return nil, err
}
if err = stamp.Valid(chunk.Address(), b.Owner); err != nil {
return nil, fmt.Errorf("chunk %s stamp invalid: %w", chunk.Address().String(), err)
}
return chunk.WithStamp(stamp).WithBatch(b.Radius, b.Depth), nil
}
}