OSDN Git Service

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