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") }