OSDN Git Service

Add confirm tx
[bytom/vapor.git] / account / dpos_builder.go
1 package account
2
3 import (
4         "context"
5         "encoding/json"
6
7         "github.com/vapor/config"
8
9         "github.com/vapor/blockchain/txbuilder"
10         "github.com/vapor/common"
11         "github.com/vapor/consensus"
12         "github.com/vapor/crypto/ed25519/chainkd"
13         "github.com/vapor/errors"
14         "github.com/vapor/protocol/bc"
15         "github.com/vapor/protocol/bc/types"
16         "github.com/vapor/protocol/vm/vmutil"
17 )
18
19 func (m *Manager) DecodeDposAction(data []byte) (txbuilder.Action, error) {
20         a := &DopsAction{Accounts: m}
21         err := json.Unmarshal(data, a)
22         return a, err
23 }
24
25 type DopsAction struct {
26         Accounts *Manager
27         bc.AssetAmount
28         From           string `json:"from"`
29         To             string `json:"to"`
30         Fee            uint64 `json:"fee"`
31         UseUnconfirmed bool   `json:"use_unconfirmed"`
32         TxType         uint32 `json:"tx_type"`
33         Height         uint64 `json:"height"`
34 }
35
36 func (a *DopsAction) Build(ctx context.Context, b *txbuilder.TemplateBuilder) error {
37         var missing []string
38
39         if a.AssetId.IsZero() {
40                 missing = append(missing, "asset_id")
41         }
42         if a.From == "" {
43                 missing = append(missing, "from")
44         }
45         if a.To == "" {
46                 missing = append(missing, "to")
47         }
48
49         if types.TxType(a.TxType) < types.Binary || types.TxType(a.TxType) > types.ConfirmTx {
50                 return errors.New("tx type  of dpos is error")
51         }
52
53         txType := types.TxType(a.TxType)
54
55         if len(missing) > 0 {
56                 return txbuilder.MissingFieldsError(missing...)
57         }
58         res, err := a.Accounts.utxoKeeper.ReserveByAddress(a.From, a.AssetId, a.Amount, a.UseUnconfirmed, false)
59         if err != nil {
60                 return errors.Wrap(err, "reserving utxos")
61         }
62
63         // Cancel the reservation if the build gets rolled back.
64         b.OnRollback(func() { a.Accounts.utxoKeeper.Cancel(res.id) })
65         for _, r := range res.utxos {
66                 txInput, sigInst, err := DposTx(a.From, a.To, a.Amount, r, txType, a.Height)
67                 if err != nil {
68                         return errors.Wrap(err, "creating inputs")
69                 }
70                 if err = b.AddInput(txInput, sigInst); err != nil {
71                         return errors.Wrap(err, "adding inputs")
72                 }
73         }
74
75         res, err = a.Accounts.utxoKeeper.ReserveByAddress(a.From, a.AssetId, a.Fee, a.UseUnconfirmed, true)
76         if err != nil {
77                 return errors.Wrap(err, "reserving utxos")
78         }
79
80         // Cancel the reservation if the build gets rolled back.
81         b.OnRollback(func() { a.Accounts.utxoKeeper.Cancel(res.id) })
82         for _, r := range res.utxos {
83                 txSpendInput, sigInst, err := spendInput(r)
84                 if err != nil {
85                         return errors.Wrap(err, "creating inputs")
86                 }
87
88                 if err = b.AddInput(txSpendInput, sigInst); err != nil {
89                         return errors.Wrap(err, "adding inputs")
90                 }
91         }
92         if res.change >= 0 {
93                 address, err := common.DecodeAddress(a.From, &consensus.ActiveNetParams)
94                 if err != nil {
95                         return err
96                 }
97                 redeemContract := address.ScriptAddress()
98                 program, err := vmutil.P2WPKHProgram(redeemContract)
99                 if err != nil {
100                         return err
101                 }
102                 if err = b.AddOutput(types.NewTxOutput(*consensus.BTMAssetID, res.change, program)); err != nil {
103                         return errors.Wrap(err, "adding change output")
104                 }
105         }
106
107         return nil
108 }
109
110 func (a *DopsAction) ActionType() string {
111         return "dpos"
112 }
113
114 // DposInputs convert an utxo to the txinput
115 func DposTx(from, to string, stake uint64, u *UTXO, txType types.TxType, h uint64) (*types.TxInput, *txbuilder.SigningInstruction, error) {
116         txInput := types.NewDpos(nil, from, to, u.SourceID, u.AssetID, stake, u.Amount, u.SourcePos, u.ControlProgram, txType, h)
117         sigInst := &txbuilder.SigningInstruction{}
118         var xpubs []chainkd.XPub
119         var xprv chainkd.XPrv
120         xprv.UnmarshalText([]byte(config.CommonConfig.Consensus.Dpos.XPrv))
121         xpubs = append(xpubs, xprv.XPub())
122         quorum := len(xpubs)
123         if u.Address == "" {
124                 sigInst.AddWitnessKeysWithOutPath(xpubs, quorum)
125                 return txInput, sigInst, nil
126         }
127
128         address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)
129         if err != nil {
130                 return nil, nil, err
131         }
132         sigInst.AddRawWitnessKeysWithoutPath(xpubs, quorum)
133         switch address.(type) {
134         case *common.AddressWitnessPubKeyHash:
135                 derivedPK := xpubs[0].PublicKey()
136                 sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))
137
138         case *common.AddressWitnessScriptHash:
139                 derivedXPubs := xpubs
140                 derivedPKs := chainkd.XPubKeys(derivedXPubs)
141                 script, err := vmutil.P2SPMultiSigProgram(derivedPKs, quorum)
142                 if err != nil {
143                         return nil, nil, err
144                 }
145                 sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))
146
147         default:
148                 return nil, nil, errors.New("unsupport address type")
149         }
150
151         return txInput, sigInst, nil
152 }
153
154 // spendInput convert an utxo to the txinput
155 func spendInput(u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {
156         txSpendInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
157         sigInst := &txbuilder.SigningInstruction{}
158         var xpubs []chainkd.XPub
159         var xprv chainkd.XPrv
160         xprv.UnmarshalText([]byte(config.CommonConfig.Consensus.Dpos.XPrv))
161         xpubs = append(xpubs, xprv.XPub())
162         quorum := len(xpubs)
163         if u.Address == "" {
164                 sigInst.AddWitnessKeysWithOutPath(xpubs, quorum)
165                 return txSpendInput, sigInst, nil
166         }
167
168         address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)
169         if err != nil {
170                 return nil, nil, err
171         }
172         sigInst.AddRawWitnessKeysWithoutPath(xpubs, quorum)
173         switch address.(type) {
174         case *common.AddressWitnessPubKeyHash:
175                 derivedPK := xpubs[0].PublicKey()
176                 sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))
177
178         case *common.AddressWitnessScriptHash:
179                 derivedXPubs := xpubs
180                 derivedPKs := chainkd.XPubKeys(derivedXPubs)
181                 script, err := vmutil.P2SPMultiSigProgram(derivedPKs, quorum)
182                 if err != nil {
183                         return nil, nil, err
184                 }
185                 sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))
186
187         default:
188                 return nil, nil, errors.New("unsupport address type")
189         }
190
191         return txSpendInput, sigInst, nil
192 }