-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
policy_quote.go
256 lines (221 loc) · 9.03 KB
/
policy_quote.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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
package minercraft
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/libsv/go-bt/v2"
"github.com/tonicpow/go-minercraft/v2/apis/arc"
"github.com/tonicpow/go-minercraft/v2/apis/mapi"
)
// PolicyQuoteModelAdapter is the interface for the adapter to get the policy quote response
type PolicyQuoteModelAdapter interface {
GetPolicyData() *PolicyPayload
}
// PolicyQuoteMapiAdapter is the adapter for the mAPI response
type PolicyQuoteMapiAdapter struct {
*mapi.PolicyQuoteModel
}
// PolicyQuoteArcAdapter is the adapter for the Arc response
type PolicyQuoteArcAdapter struct {
*arc.PolicyQuoteModel
}
// UnifiedPolicy is the unmarshalled version of the policy
type UnifiedPolicy struct {
AcceptNonStdOutputs bool `json:"acceptnonstdoutputs"`
DataCarrier bool `json:"datacarrier"`
DataCarrierSize uint32 `json:"datacarriersize"`
LimitAncestorCount uint32 `json:"limitancestorcount"`
LimitCpfpGroupMembersCount uint32 `json:"limitcpfpgroupmemberscount"`
MaxNonStdTxValidationDuration uint32 `json:"maxnonstdtxvalidationduration"`
MaxScriptNumLengthPolicy uint32 `json:"maxscriptnumlengthpolicy"`
MaxScriptSizePolicy uint32 `json:"maxscriptsizepolicy"`
MaxStackMemoryUsagePolicy uint64 `json:"maxstackmemoryusagepolicy"`
MaxStdTxValidationDuration uint32 `json:"maxstdtxvalidationduration"`
MaxTxSizePolicy uint32 `json:"maxtxsizepolicy"`
SkipScriptFlags []mapi.ScriptFlag `json:"skipscriptflags"`
MaxConsolidationFactor uint32 `json:"minconsolidationfactor"`
MaxConsolidationInputScriptSize uint32 `json:"maxconsolidationinputscriptsize"`
MinConfConsolidationInput uint32 `json:"minconfconsolidationinput"`
AcceptNonStdConsolidationInput bool `json:"acceptnonstdconsolidationinput"`
// Additional fields for Policy in API2
MaxTxSigOpsCount uint32 `json:"maxtxsigopscount"`
}
// UnifiedFeePayload is the unmarshalled version of the payload envelope
type UnifiedFeePayload struct {
mapi.FeePayloadFields
Fees []*bt.Fee `json:"fees"`
}
// PolicyPayload is the unmarshalled version of the payload envelope
type PolicyPayload struct {
UnifiedFeePayload // Inherit the same structure as the fee payload
Callbacks []*mapi.PolicyCallback `json:"callbacks"` // IP addresses of double-spend notification servers such as mAPI reference implementation
Policies *UnifiedPolicy `json:"policies"` // values of miner policies as configured by the mAPI reference implementation administrator
}
// PolicyQuoteResponse is the raw response from the API request
//
// Specs: https://github.com/bitcoin-sv-specs/brfc-merchantapi#1-get-policy-quote
type PolicyQuoteResponse struct {
JSONEnvelope
Quote *PolicyPayload `json:"quote"` // Custom field for unmarshalled payload data
}
// PolicyQuote will fire a Merchant&Arc API request to retrieve the policy from a given miner
//
// This endpoint is used to get the different policies quoted by a miner.
// It returns a JSONEnvelope with a payload that contains the policies used by a specific BSV miner.
// The purpose of the envelope is to ensure strict consistency in the message content for
// the purpose of signing responses. This is a superset of the fee quote service, as it also
// includes information on DSNT IP addresses and miner policies.
//
// Specs: https://github.com/bitcoin-sv-specs/brfc-merchantapi#1-get-policy-quote
// Specs: https://docs.gorillapool.io/arc/api.html#get-the-policy-settings
func (c *Client) PolicyQuote(ctx context.Context, miner *Miner) (*PolicyQuoteResponse, error) {
// Make sure we have a valid miner
if miner == nil {
return nil, errors.New("miner was nil")
}
route, err := ActionRouteByAPIType(PolicyQuote, c.apiType)
if err != nil {
return nil, err
}
// Make the HTTP request
result := getQuote(ctx, c, miner, route)
if result.Response.Error != nil {
return nil, result.Response.Error
}
quoteResponse := &PolicyQuoteResponse{
JSONEnvelope: JSONEnvelope{
APIType: c.apiType,
Miner: result.Miner,
},
}
var modelAdapter PolicyQuoteModelAdapter
switch c.apiType {
case MAPI:
model := &mapi.PolicyQuoteModel{}
err = quoteResponse.process(result.Miner, result.Response.BodyContents)
if err != nil || len(quoteResponse.Payload) <= 0 {
return nil, err
}
err = json.Unmarshal([]byte(quoteResponse.Payload), model)
if err != nil {
return nil, err
}
if model.Fees == nil || len(model.Fees) <= 0 {
return nil, errors.New("empty fees")
}
modelAdapter = &PolicyQuoteMapiAdapter{PolicyQuoteModel: model}
case Arc:
model := &arc.PolicyQuoteModel{}
err = json.Unmarshal(result.Response.BodyContents, model)
if err != nil {
return nil, err
}
modelAdapter = &PolicyQuoteArcAdapter{PolicyQuoteModel: model}
default:
return nil, fmt.Errorf("unknown API type: %s", c.apiType)
}
quoteResponse.Quote = modelAdapter.GetPolicyData()
// Valid?
if quoteResponse.Quote == nil {
return nil, errors.New("failed getting quote response from: " + miner.Name)
}
isValid, err := quoteResponse.IsValid()
if err != nil {
return nil, err
}
quoteResponse.Validated = isValid
// Return the fully parsed response
return quoteResponse, nil
}
// GetPolicyData will return the policy data from the mapi adapter
func (a *PolicyQuoteMapiAdapter) GetPolicyData() *PolicyPayload {
// Creates instance of UnifiedFeePayload
feePayload := UnifiedFeePayload{
FeePayloadFields: mapi.FeePayloadFields{
APIVersion: a.APIVersion,
Timestamp: a.Timestamp,
ExpirationTime: a.ExpiryTime,
MinerID: a.MinerID,
CurrentHighestBlockHash: a.CurrentHighestBlockHash,
CurrentHighestBlockHeight: a.CurrentHighestBlockHeight,
MinerReputation: nil,
},
Fees: make([]*bt.Fee, len(a.Fees)),
}
for i, mapiFee := range a.Fees {
feePayload.Fees[i] = &bt.Fee{
FeeType: bt.FeeType(mapiFee.FeeType),
MiningFee: bt.FeeUnit(mapiFee.MiningFee),
RelayFee: bt.FeeUnit(mapiFee.RelayFee),
}
}
callbacks := make([]*mapi.PolicyCallback, len(a.Callbacks))
for i, cb := range a.Callbacks {
//nolint:gosec,exportloopref // ignore those linter errors
callbacks[i] = &cb
}
policyPayload := &PolicyPayload{
UnifiedFeePayload: feePayload,
Callbacks: callbacks,
Policies: &UnifiedPolicy{
AcceptNonStdOutputs: a.Policies.AcceptNonStdOutputs,
DataCarrier: a.Policies.DataCarrier,
DataCarrierSize: a.Policies.DataCarrierSize,
LimitAncestorCount: a.Policies.LimitAncestorCount,
LimitCpfpGroupMembersCount: a.Policies.LimitCpfpGroupMembersCount,
MaxNonStdTxValidationDuration: a.Policies.MaxNonStdTxValidationDuration,
MaxScriptNumLengthPolicy: a.Policies.MaxScriptNumLengthPolicy,
MaxScriptSizePolicy: a.Policies.MaxScriptSizePolicy,
MaxStackMemoryUsagePolicy: a.Policies.MaxStackMemoryUsagePolicy,
MaxStdTxValidationDuration: a.Policies.MaxStdTxValidationDuration,
MaxTxSizePolicy: a.Policies.MaxTxSizePolicy,
SkipScriptFlags: a.Policies.SkipScriptFlags,
MaxConsolidationFactor: a.Policies.MaxConsolidationFactor,
MaxConsolidationInputScriptSize: a.Policies.MaxConsolidationInputScriptSize,
MinConfConsolidationInput: a.Policies.MinConfConsolidationInput,
AcceptNonStdConsolidationInput: a.Policies.AcceptNonStdConsolidationInput,
MaxTxSigOpsCount: 0,
},
}
return policyPayload
}
// GetPolicyData will return the policy data from the arc adapter
func (a *PolicyQuoteArcAdapter) GetPolicyData() *PolicyPayload {
feePayload := UnifiedFeePayload{
FeePayloadFields: mapi.FeePayloadFields{
APIVersion: "",
Timestamp: a.Timestamp,
ExpirationTime: "",
MinerID: "",
CurrentHighestBlockHash: "",
CurrentHighestBlockHeight: 0,
MinerReputation: nil,
},
Fees: []*bt.Fee{a.Policy.MiningFee},
}
policyPayload := &PolicyPayload{
UnifiedFeePayload: feePayload,
Callbacks: nil,
Policies: &UnifiedPolicy{
AcceptNonStdOutputs: false,
DataCarrier: false,
DataCarrierSize: 0,
LimitAncestorCount: 0,
LimitCpfpGroupMembersCount: 0,
MaxNonStdTxValidationDuration: 0,
MaxScriptNumLengthPolicy: 0,
MaxScriptSizePolicy: a.Policy.MaxScriptSizePolicy,
MaxStackMemoryUsagePolicy: 0,
MaxStdTxValidationDuration: 0,
MaxTxSizePolicy: a.Policy.MaxTxSizePolicy,
SkipScriptFlags: nil,
MaxConsolidationFactor: 0,
MaxConsolidationInputScriptSize: 0,
MinConfConsolidationInput: 0,
AcceptNonStdConsolidationInput: false,
MaxTxSigOpsCount: a.Policy.MaxTxSigOpsCount,
},
}
return policyPayload
}