OSDN Git Service

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