-
Notifications
You must be signed in to change notification settings - Fork 8
/
v1history.go
158 lines (147 loc) · 4.62 KB
/
v1history.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
151
152
153
154
155
156
157
158
package fio
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/fioprotocol/fio-go/eos"
"io/ioutil"
"sort"
"strings"
)
// BlockTxidsResp contains a list of transactions in a block.
type BlockTxidsResp struct {
Ids []eos.Checksum256 `json:"ids"`
LastIrreversibleBlock uint32 `json:"last_irreversible_block"`
}
// HistGetBlockTxids retrieves the txid for all transactions that occurred in a block from the v1 history plugin.
func (api *API) HistGetBlockTxids(blockNum uint32) (*BlockTxidsResp, error) {
resp, err := api.HttpClient.Post(
api.BaseURL+"/v1/history/get_block_txids",
"application/json",
bytes.NewReader([]byte(fmt.Sprintf(`{"block_num": %d}`, blockNum))),
)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
blocks := &BlockTxidsResp{}
err = json.Unmarshal(body, blocks)
if err != nil {
return nil, err
}
return blocks, nil
}
// GetTransaction duplicates eos-go's GetTransaction. TODO: is this redundant? Can it be removed?
func (api *API) GetTransaction(id eos.Checksum256) (*eos.TransactionResp, error) {
resp, err := api.HttpClient.Post(
api.BaseURL+"/v1/history/get_transaction",
"application/json",
bytes.NewReader([]byte(fmt.Sprintf(`{"id": "%s"}`, id.String()))),
)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
at := &eos.TransactionResp{}
err = json.Unmarshal(body, at)
if err != nil {
return nil, err
}
return at, nil
}
// accountActionSequence is a truncated action trace used only for finding the highest sequence number
type accountActionSequence struct {
AccountActionSequence uint32 `json:"account_action_seq"`
}
type accountActions struct {
Actions []accountActionSequence `json:"actions"`
}
// GetMaxActions returns the highest account_action_sequence from the get_actions endpoint.
// This is needed because paging only works with positive offsets.
func (api *API) GetMaxActions(account eos.AccountName) (highest uint32, err error) {
resp, err := api.HttpClient.Post(
api.BaseURL+"/v1/history/get_actions",
"application/json",
bytes.NewReader([]byte(fmt.Sprintf(`{"account_name":"%s","pos":-1}`, account))),
)
if err != nil {
return 0, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return 0, err
}
if len(body) == 0 {
return 0, errors.New("received empty response")
}
aa := &accountActions{}
err = json.Unmarshal(body, &aa)
if err != nil {
return 0, err
}
if aa.Actions == nil || len(aa.Actions) == 0 {
return 0, nil
}
return aa.Actions[len(aa.Actions)-1].AccountActionSequence, nil
}
// HasHistory looks at available APIs and returns true if /v1/history/* exists.
func (api *API) HasHistory() bool {
_, apis, err := api.GetSupportedApis()
if err != nil {
return false
}
for _, a := range apis {
if strings.HasPrefix(a, `/v1/history/`) {
return true
}
}
return false
}
// GetActionsUniq strips the results of GetActions of duplicate traces, this can occur with certain transactions
// that may have multiple actors involved and the same trace is presented more than once but associated with a
// different actor. This will give preference to the trace referencing the actor queried if possible.
//
// Deprecated: a new endpoint that handles de-duplication will make this function irrelevant.
func (api *API) GetActionsUniq(actor eos.AccountName, offset int64, pos int64) ([]*eos.ActionTrace, error) {
traceUniq := make(map[string]*eos.ActionTrace)
resp, err := api.GetActions(eos.GetActionsRequest{AccountName: actor, Offset: offset, Pos: pos})
if err != nil {
return nil, err
}
if resp == nil || len(resp.Actions) == 0 {
return nil, errors.New("empty result")
}
for i := range resp.Actions {
// use a closure to dereference
func(act *eos.ActionResp) {
// have we already seen this act_digest?
switch traceUniq[act.Trace.Receipt.ActionDigest] {
case nil:
traceUniq[act.Trace.Receipt.ActionDigest] = &act.Trace
default:
// if there is a dup, prefer the correct actor, otherwise just ignore and keep the existing:
if act.Trace.Receipt.AuthSequence != nil && len(act.Trace.Receipt.AuthSequence) > 0 && act.Trace.Receipt.AuthSequence[0].Account == actor {
traceUniq[act.Trace.Receipt.ActionDigest] = &act.Trace
}
}
}(&resp.Actions[i])
}
traces := make([]*eos.ActionTrace, 0)
for _, v := range traceUniq {
traces = append(traces, v)
}
sort.Slice(traces, func(i, j int) bool {
return traces[i].Receipt.GlobalSequence < traces[j].Receipt.GlobalSequence
})
return traces, nil
}