OSDN Git Service

add dpos consensus
[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 }
33
34 func (a *DopsAction) Build(ctx context.Context, b *txbuilder.TemplateBuilder) error {
35         var missing []string
36
37         if a.AssetId.IsZero() {
38                 missing = append(missing, "asset_id")
39         }
40         if a.From == "" {
41                 missing = append(missing, "from")
42         }
43         if a.To == "" {
44                 missing = append(missing, "to")
45         }
46
47         if len(missing) > 0 {
48                 return txbuilder.MissingFieldsError(missing...)
49         }
50         res, err := a.Accounts.utxoKeeper.ReserveByAddress(a.From, a.AssetId, a.Amount, a.UseUnconfirmed, false)
51         if err != nil {
52                 return errors.Wrap(err, "reserving utxos")
53         }
54
55         // Cancel the reservation if the build gets rolled back.
56         b.OnRollback(func() { a.Accounts.utxoKeeper.Cancel(res.id) })
57         for _, r := range res.utxos {
58                 txInput, sigInst, err := DposTx(a.From, a.To, a.Amount, r)
59                 if err != nil {
60                         return errors.Wrap(err, "creating inputs")
61                 }
62                 if err = b.AddInput(txInput, sigInst); err != nil {
63                         return errors.Wrap(err, "adding inputs")
64                 }
65         }
66
67         res, err = a.Accounts.utxoKeeper.ReserveByAddress(a.From, a.AssetId, a.Fee, a.UseUnconfirmed, true)
68         if err != nil {
69                 return errors.Wrap(err, "reserving utxos")
70         }
71
72         // Cancel the reservation if the build gets rolled back.
73         b.OnRollback(func() { a.Accounts.utxoKeeper.Cancel(res.id) })
74         for _, r := range res.utxos {
75                 txSpendInput, sigInst, err := spendInput(r)
76                 if err != nil {
77                         return errors.Wrap(err, "creating inputs")
78                 }
79
80                 if err = b.AddInput(txSpendInput, sigInst); err != nil {
81                         return errors.Wrap(err, "adding inputs")
82                 }
83         }
84         if res.change >= 0 {
85                 address, err := common.DecodeAddress(a.From, &consensus.ActiveNetParams)
86                 if err != nil {
87                         return err
88                 }
89                 redeemContract := address.ScriptAddress()
90                 program, err := vmutil.P2WPKHProgram(redeemContract)
91                 if err != nil {
92                         return err
93                 }
94                 if err = b.AddOutput(types.NewTxOutput(*consensus.BTMAssetID, res.change, program)); err != nil {
95                         return errors.Wrap(err, "adding change output")
96                 }
97         }
98
99         return nil
100 }
101
102 func (a *DopsAction) ActionType() string {
103         return "dpos"
104 }
105
106 // DposInputs convert an utxo to the txinput
107 func DposTx(from, to string, stake uint64, u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {
108         txInput := types.NewDpos(nil, from, to, u.SourceID, u.AssetID, stake, u.Amount, u.SourcePos, u.ControlProgram, types.Delegate)
109         sigInst := &txbuilder.SigningInstruction{}
110         var xpubs []chainkd.XPub
111         var xprv chainkd.XPrv
112         xprv.UnmarshalText([]byte(config.CommonConfig.Consensus.Dpos.XPrv))
113         xpubs = append(xpubs, xprv.XPub())
114         quorum := len(xpubs)
115         if u.Address == "" {
116                 sigInst.AddWitnessKeysWithOutPath(xpubs, quorum)
117                 return txInput, sigInst, nil
118         }
119
120         address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)
121         if err != nil {
122                 return nil, nil, err
123         }
124         sigInst.AddRawWitnessKeysWithoutPath(xpubs, quorum)
125         switch address.(type) {
126         case *common.AddressWitnessPubKeyHash:
127                 derivedPK := xpubs[0].PublicKey()
128                 sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))
129
130         case *common.AddressWitnessScriptHash:
131                 derivedXPubs := xpubs
132                 derivedPKs := chainkd.XPubKeys(derivedXPubs)
133                 script, err := vmutil.P2SPMultiSigProgram(derivedPKs, quorum)
134                 if err != nil {
135                         return nil, nil, err
136                 }
137                 sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))
138
139         default:
140                 return nil, nil, errors.New("unsupport address type")
141         }
142
143         return txInput, sigInst, nil
144 }
145
146 // spendInput convert an utxo to the txinput
147 func spendInput(u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {
148         txSpendInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
149         sigInst := &txbuilder.SigningInstruction{}
150         var xpubs []chainkd.XPub
151         var xprv chainkd.XPrv
152         xprv.UnmarshalText([]byte(config.CommonConfig.Consensus.Dpos.XPrv))
153         xpubs = append(xpubs, xprv.XPub())
154         quorum := len(xpubs)
155         if u.Address == "" {
156                 sigInst.AddWitnessKeysWithOutPath(xpubs, quorum)
157                 return txSpendInput, sigInst, nil
158         }
159
160         address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)
161         if err != nil {
162                 return nil, nil, err
163         }
164         sigInst.AddRawWitnessKeysWithoutPath(xpubs, quorum)
165         switch address.(type) {
166         case *common.AddressWitnessPubKeyHash:
167                 derivedPK := xpubs[0].PublicKey()
168                 sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))
169
170         case *common.AddressWitnessScriptHash:
171                 derivedXPubs := xpubs
172                 derivedPKs := chainkd.XPubKeys(derivedXPubs)
173                 script, err := vmutil.P2SPMultiSigProgram(derivedPKs, quorum)
174                 if err != nil {
175                         return nil, nil, err
176                 }
177                 sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))
178
179         default:
180                 return nil, nil, errors.New("unsupport address type")
181         }
182
183         return txSpendInput, sigInst, nil
184 }