OSDN Git Service

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