9 log "github.com/sirupsen/logrus"
11 "github.com/bytom/blockchain/txbuilder"
12 "github.com/bytom/errors"
13 "github.com/bytom/net/http/reqid"
14 "github.com/bytom/protocol/bc"
15 "github.com/bytom/protocol/bc/types"
18 var defaultTxTTL = 5 * time.Minute
20 func (a *API) actionDecoder(action string) (func([]byte) (txbuilder.Action, error), bool) {
21 var decoder func([]byte) (txbuilder.Action, error)
23 case "control_account":
24 decoder = a.wallet.AccountMgr.DecodeControlAction
25 case "control_address":
26 decoder = txbuilder.DecodeControlAddressAction
27 case "control_program":
28 decoder = txbuilder.DecodeControlProgramAction
29 case "control_receiver":
30 decoder = txbuilder.DecodeControlReceiverAction
32 decoder = a.wallet.AssetReg.DecodeIssueAction
34 decoder = txbuilder.DecodeRetireAction
36 decoder = a.wallet.AccountMgr.DecodeSpendAction
37 case "spend_account_unspent_output":
38 decoder = a.wallet.AccountMgr.DecodeSpendUTXOAction
45 func mergeActions(req *BuildRequest) []map[string]interface{} {
46 actions := make([]map[string]interface{}, 0)
47 actionMap := make(map[string]map[string]interface{})
49 for _, m := range req.Actions {
50 if actionType := m["type"].(string); actionType != "spend_account" {
51 actions = append(actions, m)
55 actionKey := m["asset_id"].(string) + m["account_id"].(string)
56 amountNumber := m["amount"].(json.Number)
57 amount, _ := amountNumber.Int64()
59 if tmpM, ok := actionMap[actionKey]; ok {
60 tmpNumber, _ := tmpM["amount"].(json.Number)
61 tmpAmount, _ := tmpNumber.Int64()
62 tmpM["amount"] = json.Number(fmt.Sprintf("%v", tmpAmount+amount))
64 actionMap[actionKey] = m
65 actions = append(actions, m)
72 func (a *API) buildSingle(ctx context.Context, req *BuildRequest) (*txbuilder.Template, error) {
73 err := a.filterAliases(ctx, req)
77 reqActions := mergeActions(req)
78 actions := make([]txbuilder.Action, 0, len(reqActions))
79 for i, act := range reqActions {
80 typ, ok := act["type"].(string)
82 return nil, errors.WithDetailf(errBadActionType, "no action type provided on action %d", i)
84 decoder, ok := a.actionDecoder(typ)
86 return nil, errors.WithDetailf(errBadActionType, "unknown action type %q on action %d", typ, i)
89 // Remarshal to JSON, the action may have been modified when we
91 b, err := json.Marshal(act)
95 action, err := decoder(b)
97 return nil, errors.WithDetailf(errBadAction, "%s on action %d", err.Error(), i)
99 actions = append(actions, action)
102 ttl := req.TTL.Duration
106 maxTime := time.Now().Add(ttl)
108 tpl, err := txbuilder.Build(ctx, req.Tx, actions, maxTime, req.TimeRange)
109 if errors.Root(err) == txbuilder.ErrAction {
110 // append each of the inner errors contained in the data.
112 for _, innerErr := range errors.Data(err)["actions"].([]error) {
113 Errs = Errs + "<" + innerErr.Error() + ">"
115 err = errors.New(err.Error() + "-" + Errs)
121 // ensure null is never returned for signing instructions
122 if tpl.SigningInstructions == nil {
123 tpl.SigningInstructions = []*txbuilder.SigningInstruction{}
128 // POST /build-transaction
129 func (a *API) build(ctx context.Context, buildReqs *BuildRequest) Response {
130 subctx := reqid.NewSubContext(ctx, reqid.New())
132 tmpl, err := a.buildSingle(subctx, buildReqs)
134 return NewErrorResponse(err)
137 return NewSuccessResponse(tmpl)
140 func (a *API) submitSingle(ctx context.Context, tpl *txbuilder.Template) (map[string]string, error) {
141 if tpl.Transaction == nil {
142 return nil, errors.Wrap(txbuilder.ErrMissingRawTx)
145 if err := txbuilder.FinalizeTx(ctx, a.chain, tpl.Transaction); err != nil {
146 return nil, errors.Wrapf(err, "tx %s", tpl.Transaction.ID.String())
149 return map[string]string{"tx_id": tpl.Transaction.ID.String()}, nil
152 type submitTxResp struct {
153 TxID *bc.Hash `json:"tx_id"`
156 // POST /submit-transaction
157 func (a *API) submit(ctx context.Context, ins struct {
158 Tx types.Tx `json:"raw_transaction"`
160 if err := txbuilder.FinalizeTx(ctx, a.chain, &ins.Tx); err != nil {
161 return NewErrorResponse(err)
164 log.WithField("tx_id", ins.Tx.ID).Info("submit single tx")
165 return NewSuccessResponse(&submitTxResp{TxID: &ins.Tx.ID})
168 // POST /sign-submit-transaction
169 func (a *API) signSubmit(ctx context.Context, x struct {
170 Password []string `json:"password"`
171 Txs txbuilder.Template `json:"transaction"`
173 if err := txbuilder.Sign(ctx, &x.Txs, nil, x.Password[0], a.pseudohsmSignTemplate); err != nil {
174 log.WithField("build err", err).Error("fail on sign transaction.")
175 return NewErrorResponse(err)
177 log.Info("Sign Transaction complete.")
179 txID, err := a.submitSingle(nil, &x.Txs)
181 log.WithField("err", err).Error("submit single tx")
182 return NewErrorResponse(err)
185 log.WithField("tx_id", txID["tx_id"]).Info("submit single tx")
186 return NewSuccessResponse(txID)