aboutsummaryrefslogtreecommitdiff
path: root/pkg/client/submitter.go
blob: fe40b6358e8e9786d9b9a82199cee43280077c9f (plain)
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-go/pkg/requests"
	"git.sigsum.org/sigsum-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")
}