OSDN Git Service

Merge pull request #41 from Bytom/dev
[bytom/vapor.git] / claim / bytom / mainchain / builder.go
1 package mainchain
2
3 import (
4         "math"
5         "time"
6
7         "github.com/vapor/blockchain/txbuilder"
8         bytomtypes "github.com/vapor/claim/bytom/protocolbc/types"
9         "github.com/vapor/consensus"
10         "github.com/vapor/errors"
11 )
12
13 // NewBuilder return new TemplateBuilder instance
14 func NewBuilder(maxTime time.Time) *TemplateBuilder {
15         return &TemplateBuilder{maxTime: maxTime}
16 }
17
18 // TemplateBuilder is struct of building transactions
19 type TemplateBuilder struct {
20         base                *bytomtypes.TxData
21         inputs              []*bytomtypes.TxInput
22         outputs             []*bytomtypes.TxOutput
23         signingInstructions []*SigningInstruction
24         minTime             time.Time
25         maxTime             time.Time
26         timeRange           uint64
27         referenceData       []byte
28         rollbacks           []func()
29         callbacks           []func() error
30 }
31
32 // AddInput add inputs of transactions
33 func (b *TemplateBuilder) AddInput(in *bytomtypes.TxInput, sigInstruction *SigningInstruction) error {
34         if in.InputType() != bytomtypes.CoinbaseInputType && in.Amount() > math.MaxInt64 {
35                 return errors.WithDetailf(txbuilder.ErrBadAmount, "amount %d exceeds maximum value 2^63", in.Amount())
36         }
37         b.inputs = append(b.inputs, in)
38         b.signingInstructions = append(b.signingInstructions, sigInstruction)
39         return nil
40 }
41
42 // AddOutput add outputs of transactions
43 func (b *TemplateBuilder) AddOutput(o *bytomtypes.TxOutput) error {
44         if o.Amount > math.MaxInt64 {
45                 return errors.WithDetailf(txbuilder.ErrBadAmount, "amount %d exceeds maximum value 2^63", o.Amount)
46         }
47         b.outputs = append(b.outputs, o)
48         return nil
49 }
50
51 // RestrictMinTime set minTime
52 func (b *TemplateBuilder) RestrictMinTime(t time.Time) {
53         if t.After(b.minTime) {
54                 b.minTime = t
55         }
56 }
57
58 // RestrictMaxTime set maxTime
59 func (b *TemplateBuilder) RestrictMaxTime(t time.Time) {
60         if t.Before(b.maxTime) {
61                 b.maxTime = t
62         }
63 }
64
65 // MaxTime return maxTime
66 func (b *TemplateBuilder) MaxTime() time.Time {
67         return b.maxTime
68 }
69
70 // OnRollback registers a function that can be
71 // used to attempt to undo any side effects of building
72 // actions. For example, it might cancel any reservations
73 // reservations that were made on UTXOs in a spend action.
74 // Rollback is a "best-effort" operation and not guaranteed
75 // to succeed. Each action's side effects, if any, must be
76 // designed with this in mind.
77 func (b *TemplateBuilder) OnRollback(rollbackFn func()) {
78         b.rollbacks = append(b.rollbacks, rollbackFn)
79 }
80
81 // OnBuild registers a function that will be run after all
82 // actions have been successfully built.
83 func (b *TemplateBuilder) OnBuild(buildFn func() error) {
84         b.callbacks = append(b.callbacks, buildFn)
85 }
86
87 func (b *TemplateBuilder) rollback() {
88         for _, f := range b.rollbacks {
89                 f()
90         }
91 }
92
93 // Build build transactions with template
94 func (b *TemplateBuilder) Build() (*Template, *bytomtypes.TxData, error) {
95         // Run any building callbacks.
96         for _, cb := range b.callbacks {
97                 err := cb()
98                 if err != nil {
99                         return nil, nil, err
100                 }
101         }
102
103         tpl := &Template{}
104         tx := b.base
105         if tx == nil {
106                 tx = &bytomtypes.TxData{
107                         Version: 1,
108                 }
109         }
110
111         if b.timeRange != 0 {
112                 tx.TimeRange = b.timeRange
113         }
114
115         // Add all the built outputs.
116         tx.Outputs = append(tx.Outputs, b.outputs...)
117
118         // Add all the built inputs and their corresponding signing instructions.
119         for i, in := range b.inputs {
120                 instruction := b.signingInstructions[i]
121                 instruction.Position = uint32(len(tx.Inputs))
122
123                 // Empty signature arrays should be serialized as empty arrays, not null.
124                 if instruction.WitnessComponents == nil {
125                         instruction.WitnessComponents = []witnessComponent{}
126                 }
127                 tpl.SigningInstructions = append(tpl.SigningInstructions, instruction)
128                 tx.Inputs = append(tx.Inputs, in)
129         }
130
131         tpl.Transaction = bytomtypes.NewTx(*tx)
132         tpl.Fee = CalculateTxFee(tpl.Transaction)
133         return tpl, tx, nil
134 }
135
136 // CalculateTxFee calculate transaction fee
137 func CalculateTxFee(tx *bytomtypes.Tx) (fee uint64) {
138         totalInputBTM := uint64(0)
139         totalOutputBTM := uint64(0)
140
141         for _, input := range tx.Inputs {
142                 if input.InputType() != bytomtypes.CoinbaseInputType && input.AssetID() == *consensus.BTMAssetID {
143                         totalInputBTM += input.Amount()
144                 }
145         }
146
147         for _, output := range tx.Outputs {
148                 if *output.AssetId == *consensus.BTMAssetID {
149                         totalOutputBTM += output.Amount
150                 }
151         }
152
153         fee = totalInputBTM - totalOutputBTM
154         return
155 }