github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/toolbar/apinode/transaction.go (about) 1 package apinode 2 3 import ( 4 "encoding/hex" 5 "encoding/json" 6 7 "github.com/bytom/bytom/blockchain/txbuilder" 8 "github.com/bytom/bytom/consensus" 9 "github.com/bytom/bytom/errors" 10 "github.com/bytom/bytom/protocol/bc" 11 "github.com/bytom/bytom/protocol/bc/types" 12 ) 13 14 type SpendAccountAction struct { 15 AccountID string `json:"account_id"` 16 *bc.AssetAmount 17 } 18 19 func (s *SpendAccountAction) MarshalJSON() ([]byte, error) { 20 return json.Marshal(&struct { 21 Type string `json:"type"` 22 AccountID string `json:"account_id"` 23 *bc.AssetAmount 24 }{ 25 Type: "spend_account", 26 AccountID: s.AccountID, 27 AssetAmount: s.AssetAmount, 28 }) 29 } 30 31 type ControlAddressAction struct { 32 Address string `json:"address"` 33 *bc.AssetAmount 34 } 35 36 func (c *ControlAddressAction) MarshalJSON() ([]byte, error) { 37 return json.Marshal(&struct { 38 Type string `json:"type"` 39 Address string `json:"address"` 40 *bc.AssetAmount 41 }{ 42 Type: "control_address", 43 Address: c.Address, 44 AssetAmount: c.AssetAmount, 45 }) 46 } 47 48 type RetireAction struct { 49 *bc.AssetAmount 50 Arbitrary []byte 51 } 52 53 func (r *RetireAction) MarshalJSON() ([]byte, error) { 54 return json.Marshal(&struct { 55 Type string `json:"type"` 56 Arbitrary string `json:"arbitrary"` 57 *bc.AssetAmount 58 }{ 59 Type: "retire", 60 Arbitrary: hex.EncodeToString(r.Arbitrary), 61 AssetAmount: r.AssetAmount, 62 }) 63 } 64 65 func (n *Node) BatchSendBTM(accountID, password string, outputs map[string]uint64, memo []byte) error { 66 totalBTM := uint64(10000000) 67 actions := []interface{}{} 68 if len(memo) > 0 { 69 actions = append(actions, &RetireAction{ 70 Arbitrary: memo, 71 AssetAmount: &bc.AssetAmount{AssetId: consensus.BTMAssetID, Amount: 1}, 72 }) 73 } 74 75 for address, amount := range outputs { 76 actions = append(actions, &ControlAddressAction{ 77 Address: address, 78 AssetAmount: &bc.AssetAmount{AssetId: consensus.BTMAssetID, Amount: amount}, 79 }) 80 totalBTM += amount 81 } 82 83 actions = append(actions, &SpendAccountAction{ 84 AccountID: accountID, 85 AssetAmount: &bc.AssetAmount{AssetId: consensus.BTMAssetID, Amount: totalBTM}, 86 }) 87 88 tpls, err := n.buildTx(actions) 89 if err != nil { 90 return err 91 } 92 93 tpls, err = n.signTx(tpls, password) 94 if err != nil { 95 return err 96 } 97 98 for _, tpl := range tpls { 99 if _, err := n.SubmitTx(tpl.Transaction); err != nil { 100 return err 101 } 102 } 103 104 return nil 105 } 106 107 type buildTxReq struct { 108 Actions []interface{} `json:"actions"` 109 } 110 111 func (n *Node) buildTx(actions []interface{}) ([]*txbuilder.Template, error) { 112 url := "/build-chain-transactions" 113 payload, err := json.Marshal(&buildTxReq{Actions: actions}) 114 if err != nil { 115 return nil, errors.Wrap(err, "Marshal spend request") 116 } 117 118 result := []*txbuilder.Template{} 119 return result, n.request(url, payload, &result) 120 } 121 122 type signTxReq struct { 123 Txs []*txbuilder.Template `json:"transactions"` 124 Password string `json:"password"` 125 } 126 127 type signTxResp struct { 128 Txs []*txbuilder.Template `json:"transaction"` 129 SignComplete bool `json:"sign_complete"` 130 } 131 132 func (n *Node) signTx(tpls []*txbuilder.Template, password string) ([]*txbuilder.Template, error) { 133 url := "/sign-transactions" 134 payload, err := json.Marshal(&signTxReq{Txs: tpls, Password: password}) 135 if err != nil { 136 return nil, errors.Wrap(err, "json marshal") 137 } 138 139 resp := &signTxResp{} 140 if err := n.request(url, payload, resp); err != nil { 141 return nil, err 142 } 143 144 if !resp.SignComplete { 145 return nil, errors.New("sign fail") 146 } 147 148 return resp.Txs, nil 149 } 150 151 type submitTxReq struct { 152 Tx *types.Tx `json:"raw_transaction"` 153 } 154 155 type submitTxResp struct { 156 TxID string `json:"tx_id"` 157 } 158 159 func (n *Node) SubmitTx(tx *types.Tx) (string, error) { 160 url := "/submit-transaction" 161 payload, err := json.Marshal(submitTxReq{Tx: tx}) 162 if err != nil { 163 return "", errors.Wrap(err, "json marshal") 164 } 165 166 res := &submitTxResp{} 167 return res.TxID, n.request(url, payload, res) 168 }