8 "github.com/vapor/config"
10 "github.com/vapor/blockchain/txbuilder"
11 "github.com/vapor/common"
12 "github.com/vapor/consensus"
13 dpos "github.com/vapor/consensus/consensus/dpos"
14 "github.com/vapor/crypto/ed25519/chainkd"
15 "github.com/vapor/errors"
16 "github.com/vapor/protocol/bc"
17 "github.com/vapor/protocol/bc/types"
18 "github.com/vapor/protocol/vm"
19 "github.com/vapor/protocol/vm/vmutil"
22 func (m *Manager) DecodeDposAction(data []byte) (txbuilder.Action, error) {
23 a := &DopsAction{Accounts: m}
24 err := json.Unmarshal(data, a)
28 type DopsAction struct {
31 DposType uint32 `json:"dpos_type"`
32 Address string `json:"address"`
33 Name string `json:"name"`
34 Forgers []string `json:"forgers"`
35 UseUnconfirmed bool `json:"use_unconfirmed"`
38 func (a *DopsAction) Build(ctx context.Context, b *txbuilder.TemplateBuilder) error {
41 if a.AssetId.IsZero() {
42 missing = append(missing, "asset_id")
45 missing = append(missing, "address")
48 return txbuilder.MissingFieldsError(missing...)
50 if types.TxType(a.DposType) < types.Binary || types.TxType(a.DposType) > types.CancelVote {
51 return errors.New("tx type of dpos is error")
60 switch types.TxType(a.DposType) {
64 return errors.New("name is null for dpos Registe")
66 if a.Amount < consensus.RegisrerForgerFee {
67 return errors.New("The transaction fee is 100000000 for dpos Registe")
70 if dpos.GDpos.HaveDelegate(a.Name, a.Address) {
71 return errors.New("Forger name has registe")
74 data, err = json.Marshal(&dpos.RegisterForgerData{Name: a.Name})
80 if len(a.Forgers) == 0 {
81 return errors.New("Forgers is null for dpos Vote")
84 if a.Amount < consensus.VoteForgerFee {
85 return errors.New("The transaction fee is 10000000 for dpos Registe")
88 for _, forger := range a.Forgers {
89 if dpos.GDpos.HaveVote(a.Address, forger) {
90 return fmt.Errorf("delegate name: %s is voted", forger)
94 data, err = json.Marshal(&dpos.VoteForgerData{Forgers: a.Forgers})
99 case types.CancelVote:
100 if len(a.Forgers) == 0 {
101 return errors.New("Forgers is null for dpos CancelVote")
103 if a.Amount < consensus.CancelVoteForgerFee {
104 return errors.New("The transaction fee is 10000000 for dpos Registe")
107 for _, forger := range a.Forgers {
108 if !dpos.GDpos.HaveVote(a.Address, forger) {
109 return fmt.Errorf("delegate name: %s is not voted", forger)
113 data, err = json.Marshal(&dpos.CancelVoteForgerData{Forgers: a.Forgers})
125 referenceData, err = json.Marshal(&msg)
129 b.SetReferenceData(referenceData)
131 res, err := a.Accounts.utxoKeeper.ReserveByAddress(a.Address, a.AssetId, a.Amount, a.UseUnconfirmed, false)
133 return errors.Wrap(err, "reserving utxos")
136 // Cancel the reservation if the build gets rolled back.
137 b.OnRollback(func() { a.Accounts.utxoKeeper.Cancel(res.id) })
138 for _, r := range res.utxos {
139 txSpendInput, sigInst, err := spendInput(r)
141 return errors.Wrap(err, "creating inputs")
144 if err = b.AddInput(txSpendInput, sigInst); err != nil {
145 return errors.Wrap(err, "adding inputs")
149 address, err := common.DecodeAddress(a.Address, &consensus.ActiveNetParams)
153 redeemContract := address.ScriptAddress()
154 program, err := vmutil.P2WPKHProgram(redeemContract)
158 if err = b.AddOutput(types.NewTxOutput(*consensus.BTMAssetID, res.change, program)); err != nil {
159 return errors.Wrap(err, "adding change output")
166 func (a *DopsAction) ActionType() string {
170 // DposInputs convert an utxo to the txinput
171 func DposTx(from, to string, stake uint64, u *UTXO, txType types.TxType, h uint64) (*types.TxInput, *txbuilder.SigningInstruction, error) {
172 txInput := types.NewDpos(nil, from, to, u.SourceID, u.AssetID, stake, u.Amount, u.SourcePos, u.ControlProgram, txType, h)
173 sigInst := &txbuilder.SigningInstruction{}
174 var xpubs []chainkd.XPub
175 var xprv chainkd.XPrv
176 xprv.UnmarshalText([]byte(config.CommonConfig.Consensus.XPrv))
177 xpubs = append(xpubs, xprv.XPub())
180 sigInst.AddWitnessKeysWithOutPath(xpubs, quorum)
181 return txInput, sigInst, nil
184 address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)
188 sigInst.AddRawWitnessKeysWithoutPath(xpubs, quorum)
189 switch address.(type) {
190 case *common.AddressWitnessPubKeyHash:
191 derivedPK := xpubs[0].PublicKey()
192 sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))
194 case *common.AddressWitnessScriptHash:
195 derivedXPubs := xpubs
196 derivedPKs := chainkd.XPubKeys(derivedXPubs)
197 script, err := vmutil.P2SPMultiSigProgram(derivedPKs, quorum)
201 sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))
204 return nil, nil, errors.New("unsupport address type")
207 return txInput, sigInst, nil
210 // spendInput convert an utxo to the txinput
211 func spendInput(u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {
212 txSpendInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
213 sigInst := &txbuilder.SigningInstruction{}
214 var xpubs []chainkd.XPub
215 var xprv chainkd.XPrv
216 xprv.UnmarshalText([]byte(config.CommonConfig.Consensus.XPrv))
217 xpubs = append(xpubs, xprv.XPub())
220 sigInst.AddWitnessKeysWithOutPath(xpubs, quorum)
221 return txSpendInput, sigInst, nil
224 address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)
228 sigInst.AddRawWitnessKeysWithoutPath(xpubs, quorum)
229 switch address.(type) {
230 case *common.AddressWitnessPubKeyHash:
231 derivedPK := xpubs[0].PublicKey()
232 sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))
234 case *common.AddressWitnessScriptHash:
235 derivedXPubs := xpubs
236 derivedPKs := chainkd.XPubKeys(derivedXPubs)
237 script, err := vmutil.P2SPMultiSigProgram(derivedPKs, quorum)
241 sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))
244 return nil, nil, errors.New("unsupport address type")
247 return txSpendInput, sigInst, nil