OSDN Git Service

add docs
[bytom/shuttle.git] / swap / transaction.go
1 package swap
2
3 import (
4         "encoding/hex"
5         "encoding/json"
6         "errors"
7         "fmt"
8
9         "github.com/bytom/crypto/ed25519/chainkd"
10 )
11
12 var (
13         errXPrvLength              = errors.New("XPrv length is invalid.")
14         errFailedGetContractUTXOID = errors.New("Failed to get contract UTXO ID")
15 )
16
17 type TxOutput struct {
18         UTXOID string `json:"utxo_id"`
19         Script string `json:"script"`
20 }
21
22 type getTxReq struct {
23         TxID string `json:"tx_id"`
24 }
25
26 type getTxResp struct {
27         TxOutputs []TxOutput `json:"outputs"`
28 }
29
30 // getUTXOID get UTXO ID by transaction ID.
31 func getUTXOID(s *Server, txID, controlProgram string) (string, error) {
32         payload, err := json.Marshal(getTxReq{TxID: txID})
33         if err != nil {
34                 return "", err
35         }
36
37         res := new(getTxResp)
38         if err := s.request(getTransactionURL, payload, res); err != nil {
39                 return "", err
40         }
41
42         for _, v := range res.TxOutputs {
43                 if v.Script == controlProgram {
44                         return v.UTXOID, nil
45                 }
46         }
47
48         return "", errFailedGetContractUTXOID
49 }
50
51 type SigningInstruction struct {
52         DerivationPath []string `json:"derivation_path"`
53         SignData       []string `json:"sign_data"`
54         DataWitness    []byte   `json:"-"`
55
56         // only shown for a single-signature tx
57         Pubkey string `json:"pubkey,omitempty"`
58 }
59
60 type SpendUTXOInput struct {
61         Type     string `json:"type"`
62         OutputID string `json:"output_id"`
63 }
64
65 type SpendWalletInput struct {
66         Type    string `json:"type"`
67         AssetID string `json:"asset"`
68         Amount  uint64 `json:"amount"`
69 }
70
71 type ControlAddressOutput struct {
72         Type    string `json:"type"`
73         Amount  uint64 `json:"amount"`
74         AssetID string `json:"asset"`
75         Address string `json:"address"`
76 }
77
78 type ControlProgramOutput struct {
79         Type           string `json:"type"`
80         Amount         uint64 `json:"amount"`
81         AssetID        string `json:"asset"`
82         ControlProgram string `json:"control_program"`
83 }
84
85 type buildTxReq struct {
86         GUID          string        `json:"guid"`
87         Fee           uint64        `json:"fee"`
88         Confirmations uint64        `json:"confirmations"`
89         Inputs        []interface{} `json:"inputs"`
90         Outputs       []interface{} `json:"outputs"`
91 }
92
93 type buildTxResp struct {
94         RawTx               string                `json:"raw_transaction"`
95         SigningInstructions []*SigningInstruction `json:"signing_instructions"`
96         Fee                 uint64                `json:"fee"`
97 }
98
99 // BuildTx build tx.
100 func BuildTx(s *Server, guid, lockedAsset, contractProgram string, lockedAmount uint64) (string, error) {
101         // inputs:
102         spendWalletInput := SpendWalletInput{
103                 Type:    "spend_wallet",
104                 AssetID: lockedAsset,
105                 Amount:  lockedAmount,
106         }
107
108         // outputs:
109         controlProgramOutput := ControlProgramOutput{
110                 Type:           "control_program",
111                 Amount:         lockedAmount,
112                 AssetID:        lockedAsset,
113                 ControlProgram: contractProgram,
114         }
115
116         var inputs, outputs []interface{}
117         inputs = append(inputs, spendWalletInput)
118         outputs = append(outputs, controlProgramOutput)
119         payload, err := json.Marshal(buildTxReq{
120                 GUID:          guid,
121                 Fee:           fee,
122                 Confirmations: confirmations,
123                 Inputs:        inputs,
124                 Outputs:       outputs,
125         })
126         if err != nil {
127                 return "", err
128         }
129
130         fmt.Println("buildTx:", string(payload))
131
132         res := new(buildTxResp)
133         if err := s.request(buildTransactionURL, payload, res); err != nil {
134                 return "", err
135         }
136
137         r, err := json.MarshalIndent(res, "", "\t")
138         if err != nil {
139                 return "", err
140         }
141
142         return string(r), nil
143 }
144
145 type submitPaymentReq struct {
146         GUID       string     `json:"guid"`
147         RawTx      string     `json:"raw_transaction"`
148         Signatures [][]string `json:"signatures"`
149         Memo       string     `json:"memo"`
150 }
151
152 type submitPaymentResp struct {
153         TxID string `json:"transaction_hash"`
154 }
155
156 // SubmitPayment submit raw transaction and return transaction ID.
157 func SubmitPayment(s *Server, guid, rawTx, memo string, sigs [][]string) (string, error) {
158         payload, err := json.MarshalIndent(submitPaymentReq{
159                 GUID:       guid,
160                 RawTx:      rawTx,
161                 Signatures: sigs,
162                 Memo:       memo,
163         }, "", "\t")
164         if err != nil {
165                 return "", err
166         }
167
168         fmt.Println("\nsubmitPayment:", string(payload))
169
170         res := new(submitPaymentResp)
171         if err := s.request(submitTransactionURL, payload, res); err != nil {
172                 return "", err
173         }
174
175         return res.TxID, nil
176 }
177
178 // SignMessage sign message, return sig.
179 func SignMessage(signData, xprv string) (string, error) {
180         xprvBytes, err := hex.DecodeString(xprv)
181         if err != nil {
182                 return "", err
183         }
184         if len(xprvBytes) != 64 {
185                 return "", errXPrvLength
186         }
187
188         var newXPrv chainkd.XPrv
189         copy(newXPrv[:], xprvBytes[:])
190
191         msg, err := hex.DecodeString(signData)
192         if err != nil {
193                 return "", err
194         }
195         sig := newXPrv.Sign(msg)
196         return hex.EncodeToString(sig), nil
197 }
198
199 // BuildUnlockedTx build unlocked contract tx.
200 func BuildUnlockedTx(s *Server, guid, contractUTXOID, lockedAsset, receiver string, lockedAmount uint64) (string, error) {
201         // inputs:
202         spendUTXOInput := SpendUTXOInput{
203                 Type:     "spend_utxo",
204                 OutputID: contractUTXOID,
205         }
206
207         // outputs:
208         controlAddressOutput := ControlAddressOutput{
209                 Type:    "control_address",
210                 Amount:  lockedAmount,
211                 AssetID: lockedAsset,
212                 Address: receiver,
213         }
214
215         var inputs, outputs []interface{}
216         // inputs = append(inputs, spendUTXOInput, spendWalletInput)
217         inputs = append(inputs, spendUTXOInput)
218         outputs = append(outputs, controlAddressOutput)
219         payload, err := json.Marshal(buildTxReq{
220                 GUID:          guid,
221                 Fee:           fee,
222                 Confirmations: confirmations,
223                 Inputs:        inputs,
224                 Outputs:       outputs,
225         })
226         if err != nil {
227                 return "", err
228         }
229
230         fmt.Println("build unlocked contract tx:", string(payload))
231
232         res := new(buildTxResp)
233         if err := s.request(buildTransactionURL, payload, res); err != nil {
234                 return "", err
235         }
236
237         r, err := json.MarshalIndent(res, "", "\t")
238         if err != nil {
239                 return "", err
240         }
241
242         return string(r), nil
243 }
244
245 // BuildCallTradeoffTx build unlocked tradeoff contract tx.
246 func BuildCallTradeoffTx(s *Server, guid, contractUTXOID, seller, assetRequested string, amountRequested uint64) (string, error) {
247         // inputs:
248         spendUTXOInput := SpendUTXOInput{
249                 Type:     "spend_utxo",
250                 OutputID: contractUTXOID,
251         }
252
253         spendWalletUnlockTradeoffInput := SpendWalletInput{
254                 Type:    "spend_wallet",
255                 AssetID: assetRequested,
256                 Amount:  amountRequested,
257         }
258
259         // outputs:
260         controlProgramOutput := ControlProgramOutput{
261                 Type:           "control_program",
262                 Amount:         amountRequested,
263                 AssetID:        assetRequested,
264                 ControlProgram: seller,
265         }
266
267         var inputs, outputs []interface{}
268         inputs = append(inputs, spendUTXOInput, spendWalletUnlockTradeoffInput)
269         outputs = append(outputs, controlProgramOutput)
270         payload, err := json.Marshal(buildTxReq{
271                 GUID:          guid,
272                 Fee:           fee,
273                 Confirmations: confirmations,
274                 Inputs:        inputs,
275                 Outputs:       outputs,
276         })
277         if err != nil {
278                 return "", err
279         }
280
281         fmt.Println("build unlocked contract tx:", string(payload))
282
283         res := new(buildTxResp)
284         if err := s.request(buildTransactionURL, payload, res); err != nil {
285                 return "", err
286         }
287
288         r, err := json.MarshalIndent(res, "", "\t")
289         if err != nil {
290                 return "", err
291         }
292
293         return string(r), nil
294 }