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
|
package client
import (
"context"
"fmt"
"io"
"log"
"net/http"
"time"
"git.sigsum.org/sigsum-lib-go/pkg/requests"
"git.sigsum.org/sigsum-lib-go/pkg/types"
"git.sigsum.org/sigsum-tools-go/pkg/client/api"
"git.sigsum.org/sigsum-tools-go/pkg/policy"
)
type Submitter interface {
AddLeaves(context.Context, []requests.Leaf) ([]Bundle, error)
}
type SubmitClient struct {
api api.API
policy policy.Policy
}
// TODO: should signature+key_hash (not) be in bundle?
type Bundle struct {
ShardHint uint64
Signature types.Signature
KeyHash types.Hash
InclusionProof types.InclusionProof
CosignedTreeHead types.CosignedTreeHead
}
func NewSubmitClient(policy policy.Policy) *SubmitClient {
return &SubmitClient{
policy: policy,
}
}
// TODO: feedback on the below sketch; improve it; implement properly.
// 0. Select one log in policy, setup API network client.
// 1. Loop over all leaves that have yet to received 200 OK. Move on to
// the next leaf on a non-200 status code, trying again next itteration.
// 3. Try to fetch an inclusion proof for the latest HTTP 200 OK
// response every time a new cosigned tree head becomes available.
// Output warning if more than two cosigned tree heads pass.
// 4. Loop over all leaves and fetch inclusion proofs for X.
// [Exit with error if any inclusion proof is not available / invalid]
// 5. Return bundles in the same order as leaves were passed.
func (sc *SubmitClient) AddLeaves(_ context.Context, leaves []requests.Leaf) ([]Bundle, error) {
if err := sc.newAPI(); err != nil {
return nil, fmt.Errorf("client: %v", err)
}
for _, leaf := range leaves {
err := sc.api.AddLeaf(&leaf)
if err != nil {
return nil, fmt.Errorf("client: %v", err)
}
}
start := time.Now().Unix()
var bundles []Bundle
for {
cth, err := sc.api.GetCosignedTreeHead()
if err != nil {
return nil, fmt.Errorf("client: %v", err)
}
// TODO: verify that cth is valid for policy
ok := true
bundles = nil
for _, leaf := range leaves {
l := types.Leaf{
Statement: types.Statement{
ShardHint: leaf.ShardHint,
Checksum: *types.HashFn(leaf.Preimage[:]),
},
Signature: leaf.Signature,
KeyHash: *types.HashFn(leaf.VerificationKey[:]),
}
lh := types.HashFn(append([]byte{0x00}, l.ToBinary()...))
log.Printf("leaf hash is: %x", lh[:])
proof, err := sc.api.GetInclusionProof(cth.TreeSize, lh)
if err != nil {
log.Printf("no inclusion proof for tree size %d yet, please wait.\n", cth.TreeSize)
ok = false
break
}
// TODO: verify that inclusion proof is valid
bundles = append(bundles, Bundle{
ShardHint: l.ShardHint,
Signature: l.Signature,
KeyHash: l.KeyHash,
InclusionProof: *proof,
CosignedTreeHead: *cth,
})
}
if ok {
break
}
time.Sleep(15 * time.Second)
log.Printf("waited %d seconds...\n", time.Now().Unix()-start)
}
return bundles, nil
}
func (sc *SubmitClient) newAPI() error {
// TODO: select a log properly.
sc.api = api.NewNetworkClient(http.Client{}, sc.policy.Logs()[0].LogURL)
return nil
}
func (b *Bundle) ToASCII(w io.Writer) error {
return fmt.Errorf("TODO")
}
func (b *Bundle) FromASCII(r io.Reader) error {
return fmt.Errorf("TODO")
}
func (b *Bundle) Verify(k *types.PublicKey, p *policy.Policy, r io.Reader) error {
return fmt.Errorf("TODO")
}
|