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  }