/
apiwatch.go
150 lines (128 loc) · 2.65 KB
/
apiwatch.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright 2015 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.
package main
import (
"encoding/hex"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"sync"
"time"
"github.com/FactomProject/factom"
ed "github.com/agl/ed25519"
)
const usage = "Usage: apiwatch conf.json"
type APICall struct {
APIMethod string
ChainID string
SecKey string
ECAddr string
}
// Create a factom.Entry and commit/reveal
func (a *APICall) Factomize() error {
type entryBody struct {
APIMethod string
ReturnData string
Timestamp int64
}
b := new(entryBody)
b.APIMethod = a.APIMethod
// get the ReturnData from the api call
resp, err := http.Get(a.APIMethod)
if err != nil {
return err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode != 200 {
return fmt.Errorf(string(data))
}
b.ReturnData = string(data)
// get the current time
b.Timestamp = time.Now().Unix()
// create the factom entry
e := factom.NewEntry()
e.ChainID = a.ChainID
if e.Content, err = json.Marshal(b); err != nil {
return err
}
// Write the signature of the Entry Content to the first ExtID
e.ExtIDs = append(e.ExtIDs, func() []byte {
sec := new([64]byte)
if s, err := hex.DecodeString(a.SecKey); err != nil {
log.Fatal(err)
} else {
copy(sec[:], s)
}
return ed.Sign(sec, e.Content)[:]
}())
// Commit+Reveal the Entry to the Factom Network
if err := factom.CommitEntry(e, a.ECAddr); err != nil {
return err
}
time.Sleep(10 * time.Second)
if err := factom.RevealEntry(e); err != nil {
return err
}
return nil
}
// NewConfigReader creates a function that returns the next api call from the
// config file.
func NewConfigReader(r io.Reader) func() (*APICall, error) {
dec := json.NewDecoder(r)
return func() (*APICall, error) {
a := new(APICall)
if err := dec.Decode(a); err != nil {
return a, err
}
return a, nil
}
}
func main() {
if len(os.Args) < 2 {
log.Fatal(usage)
}
conf, err := os.Open(os.Args[1])
if err != nil {
log.Fatal(err)
}
defer conf.Close()
// error channel
ec := make(chan error)
// cr is type func() (*APICall, error)
cr := NewConfigReader(conf)
go func() {
wg := new(sync.WaitGroup)
for {
a, err := cr()
if err != nil {
if err != io.EOF {
ec <- err
}
break
}
// write 'a' into factom
wg.Add(1)
go func(a *APICall) {
defer wg.Done()
if err := a.Factomize(); err != nil {
ec <- err
}
}(a)
}
wg.Wait()
close(ec)
}()
// read any errors from the error chan
for err := range ec {
log.Println(err)
}
}