OSDN Git Service

620abab457cf391b44438e54db2446e68da71588
[bytom/vapor.git] / protocol / bc / types / txoutput.go
1 package types
2
3 import (
4         "fmt"
5         "io"
6
7         "github.com/vapor/encoding/blockchain"
8         "github.com/vapor/errors"
9         "github.com/vapor/protocol/bc"
10 )
11
12 // serflag variables for output types.
13 const (
14         IntraChainOutputType uint8 = iota
15         CrossChainOutputType
16 )
17
18 type (
19         // TxOutput is the top level struct of tx output.
20         TxOutput struct {
21                 AssetVersion uint64
22                 TypedOutput
23                 // Unconsumed suffixes of the commitment and witness extensible strings.
24                 CommitmentSuffix []byte
25         }
26
27         // TypedOutput return the txoutput type.
28         TypedOutput interface {
29                 OutputType() uint8
30         }
31 )
32
33 // OutputCommitment return the OutputCommitment of a txoutput.
34 func (to *TxOutput) OutputCommitment() OutputCommitment {
35         switch outp := to.TypedOutput.(type) {
36         case *IntraChainOutput:
37                 return outp.OutputCommitment
38
39         case *CrossChainOutput:
40                 return outp.OutputCommitment
41
42         default:
43                 return OutputCommitment{}
44         }
45 }
46
47 // AssetAmount return the asset id and amount of a txoutput.
48 func (to *TxOutput) AssetAmount() bc.AssetAmount {
49         switch outp := to.TypedOutput.(type) {
50         case *IntraChainOutput:
51                 return outp.AssetAmount
52
53         case *CrossChainOutput:
54                 return outp.AssetAmount
55
56         default:
57                 return bc.AssetAmount{}
58         }
59 }
60
61 // ControlProgram return the control program of the txoutput
62 func (to *TxOutput) ControlProgram() []byte {
63         switch outp := to.TypedOutput.(type) {
64         case *IntraChainOutput:
65                 return outp.ControlProgram
66
67         case *CrossChainOutput:
68                 return outp.ControlProgram
69
70         default:
71                 return nil
72         }
73 }
74
75 // VMVersion return the VM version of the txoutput
76 func (to *TxOutput) VMVersion() uint64 {
77         switch outp := to.TypedOutput.(type) {
78         case *IntraChainOutput:
79                 return outp.VMVersion
80
81         case *CrossChainOutput:
82                 return outp.VMVersion
83
84         default:
85                 return 0
86         }
87 }
88
89 func (to *TxOutput) readFrom(r *blockchain.Reader) (err error) {
90         if to.AssetVersion, err = blockchain.ReadVarint63(r); err != nil {
91                 return errors.Wrap(err, "reading asset version")
92         }
93
94         to.CommitmentSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error {
95                 if to.AssetVersion != 1 {
96                         return nil
97                 }
98
99                 var outType [1]byte
100                 if _, err = io.ReadFull(r, outType[:]); err != nil {
101                         return errors.Wrap(err, "reading output type")
102                 }
103
104                 switch outType[0] {
105                 case IntraChainOutputType:
106                         out := new(IntraChainOutput)
107                         to.TypedOutput = out
108                         if out.CommitmentSuffix, err = out.OutputCommitment.readFrom(r, to.AssetVersion); err != nil {
109                                 return errors.Wrap(err, "reading intra-chain output commitment")
110                         }
111
112                 case CrossChainOutputType:
113                         out := new(CrossChainOutput)
114                         to.TypedOutput = out
115                         if out.CommitmentSuffix, err = out.OutputCommitment.readFrom(r, to.AssetVersion); err != nil {
116                                 return errors.Wrap(err, "reading cross-chain output commitment")
117                         }
118
119                 default:
120                         return fmt.Errorf("unsupported output type %d", outType[0])
121                 }
122
123                 return nil
124         })
125
126         if err != nil {
127                 return err
128         }
129
130         // read and ignore the (empty) output witness
131         _, err = blockchain.ReadVarstr31(r)
132         return errors.Wrap(err, "reading output witness")
133 }
134
135 func (to *TxOutput) writeTo(w io.Writer) error {
136         if _, err := blockchain.WriteVarint63(w, to.AssetVersion); err != nil {
137                 return errors.Wrap(err, "writing asset version")
138         }
139
140         if _, err := blockchain.WriteExtensibleString(w, to.CommitmentSuffix, to.writeOutputCommitment); err != nil {
141                 return errors.Wrap(err, "writing output commitment")
142         }
143
144         if _, err := blockchain.WriteVarstr31(w, nil); err != nil {
145                 return errors.Wrap(err, "writing witness")
146         }
147
148         return nil
149 }
150
151 func (to *TxOutput) writeOutputCommitment(w io.Writer) error {
152         if to.AssetVersion != 1 {
153                 return nil
154         }
155
156         switch outp := to.TypedOutput.(type) {
157         case *IntraChainOutput:
158                 if _, err := w.Write([]byte{IntraChainOutputType}); err != nil {
159                         return err
160                 }
161                 return outp.OutputCommitment.writeExtensibleString(w, outp.CommitmentSuffix, to.AssetVersion)
162
163         case *CrossChainOutput:
164                 if _, err := w.Write([]byte{CrossChainOutputType}); err != nil {
165                         return err
166                 }
167                 return outp.OutputCommitment.writeExtensibleString(w, outp.CommitmentSuffix, to.AssetVersion)
168
169         default:
170                 return nil
171         }
172 }
173
174 // ComputeOutputID assembles an intra-chain(!) output entry given a spend
175 // commitment and computes and returns its corresponding entry ID.
176 func ComputeOutputID(sc *SpendCommitment) (h bc.Hash, err error) {
177         defer func() {
178                 if r, ok := recover().(error); ok {
179                         err = r
180                 }
181         }()
182         src := &bc.ValueSource{
183                 Ref:      &sc.SourceID,
184                 Value:    &sc.AssetAmount,
185                 Position: sc.SourcePosition,
186         }
187         o := bc.NewIntraChainOutput(src, &bc.Program{VmVersion: sc.VMVersion, Code: sc.ControlProgram}, 0)
188
189         h = bc.EntryID(o)
190         return h, nil
191 }