10 log "github.com/sirupsen/logrus"
12 "github.com/bytom/blockchain/txbuilder"
13 "github.com/bytom/consensus"
14 "github.com/bytom/errors"
15 "github.com/bytom/math/checked"
16 "github.com/bytom/net/http/reqid"
17 "github.com/bytom/protocol/bc"
18 "github.com/bytom/protocol/bc/types"
21 var defaultTxTTL = 5 * time.Minute
23 func (a *API) actionDecoder(action string) (func([]byte) (txbuilder.Action, error), bool) {
24 var decoder func([]byte) (txbuilder.Action, error)
26 case "control_address":
27 decoder = txbuilder.DecodeControlAddressAction
28 case "control_program":
29 decoder = txbuilder.DecodeControlProgramAction
30 case "control_receiver":
31 decoder = txbuilder.DecodeControlReceiverAction
33 decoder = a.wallet.AssetReg.DecodeIssueAction
35 decoder = txbuilder.DecodeRetireAction
37 decoder = a.wallet.AccountMgr.DecodeSpendAction
38 case "spend_account_unspent_output":
39 decoder = a.wallet.AccountMgr.DecodeSpendUTXOAction
46 func mergeActions(req *BuildRequest) []map[string]interface{} {
47 var actions []map[string]interface{}
48 actionMap := make(map[string]map[string]interface{})
50 for _, m := range req.Actions {
51 if actionType := m["type"].(string); actionType != "spend_account" {
52 actions = append(actions, m)
56 actionKey := m["asset_id"].(string) + m["account_id"].(string)
57 amountNumber := m["amount"].(json.Number)
58 amount, _ := amountNumber.Int64()
60 if tmpM, ok := actionMap[actionKey]; ok {
61 tmpNumber, _ := tmpM["amount"].(json.Number)
62 tmpAmount, _ := tmpNumber.Int64()
63 tmpM["amount"] = json.Number(fmt.Sprintf("%v", tmpAmount+amount))
65 actionMap[actionKey] = m
66 actions = append(actions, m)
73 func onlyHaveSpendActions(req *BuildRequest) bool {
75 for _, m := range req.Actions {
76 if actionType := m["type"].(string); strings.HasPrefix(actionType, "spend") {
81 return count == len(req.Actions)
84 func (a *API) buildSingle(ctx context.Context, req *BuildRequest) (*txbuilder.Template, error) {
85 err := a.filterAliases(ctx, req)
90 if onlyHaveSpendActions(req) {
91 return nil, errors.New("transaction only contain spend actions, didn't have output actions")
94 reqActions := mergeActions(req)
95 actions := make([]txbuilder.Action, 0, len(reqActions))
96 for i, act := range reqActions {
97 typ, ok := act["type"].(string)
99 return nil, errors.WithDetailf(errBadActionType, "no action type provided on action %d", i)
101 decoder, ok := a.actionDecoder(typ)
103 return nil, errors.WithDetailf(errBadActionType, "unknown action type %q on action %d", typ, i)
106 // Remarshal to JSON, the action may have been modified when we
108 b, err := json.Marshal(act)
112 action, err := decoder(b)
114 return nil, errors.WithDetailf(errBadAction, "%s on action %d", err.Error(), i)
116 actions = append(actions, action)
119 ttl := req.TTL.Duration
123 maxTime := time.Now().Add(ttl)
125 tpl, err := txbuilder.Build(ctx, req.Tx, actions, maxTime, req.TimeRange)
126 if errors.Root(err) == txbuilder.ErrAction {
127 // append each of the inner errors contained in the data.
129 for _, innerErr := range errors.Data(err)["actions"].([]error) {
130 Errs = Errs + "<" + innerErr.Error() + ">"
132 err = errors.New(err.Error() + "-" + Errs)
138 // ensure null is never returned for signing instructions
139 if tpl.SigningInstructions == nil {
140 tpl.SigningInstructions = []*txbuilder.SigningInstruction{}
145 // POST /build-transaction
146 func (a *API) build(ctx context.Context, buildReqs *BuildRequest) Response {
147 subctx := reqid.NewSubContext(ctx, reqid.New())
149 tmpl, err := a.buildSingle(subctx, buildReqs)
151 return NewErrorResponse(err)
154 return NewSuccessResponse(tmpl)
157 type submitTxResp struct {
158 TxID *bc.Hash `json:"tx_id"`
161 // POST /submit-transaction
162 func (a *API) submit(ctx context.Context, ins struct {
163 Tx types.Tx `json:"raw_transaction"`
165 if err := txbuilder.FinalizeTx(ctx, a.chain, &ins.Tx); err != nil {
166 return NewErrorResponse(err)
169 log.WithField("tx_id", ins.Tx.ID).Info("submit single tx")
170 return NewSuccessResponse(&submitTxResp{TxID: &ins.Tx.ID})
173 type EstimateTxGasResp struct {
174 LeftBTM int64 `json:"left_btm"`
175 ConsumedBTM int64 `json:"consumed_btm"`
176 LeftGas int64 `json:"left_gas"`
177 ConsumedGas int64 `json:"consumed_gas"`
178 StorageGas int64 `json:"storage_gas"`
179 VMGas int64 `json:"vm_gas"`
182 // POST /estimate-transaction-gas
183 func (a *API) estimateTxGas(ctx context.Context, ins struct {
184 Tx types.Tx `json:"raw_transaction"`
186 gasState, err := txbuilder.EstimateTxGas(a.chain, &ins.Tx)
188 return NewErrorResponse(err)
191 btmLeft, ok := checked.MulInt64(gasState.GasLeft, consensus.VMGasRate)
193 return NewErrorResponse(errors.New("calculate btmleft got a math error"))
196 txGasResp := &EstimateTxGasResp{
198 ConsumedBTM: int64(gasState.BTMValue) - btmLeft,
199 LeftGas: gasState.GasLeft,
200 ConsumedGas: gasState.GasUsed,
201 StorageGas: gasState.StorageGas,
202 VMGas: gasState.GasUsed - gasState.StorageGas,
205 return NewSuccessResponse(txGasResp)