OSDN Git Service

Add write data to chain
[bytom/vapor.git] / blockchain / txbuilder / actions.go
1 package txbuilder
2
3 import (
4         "context"
5         stdjson "encoding/json"
6         "errors"
7         "fmt"
8         "io"
9         "os"
10         "strings"
11
12         "github.com/vapor/config"
13
14         ipfs "github.com/ipfs/go-ipfs-api"
15         "github.com/vapor/common"
16         "github.com/vapor/consensus"
17         "github.com/vapor/encoding/json"
18         "github.com/vapor/protocol/bc"
19         "github.com/vapor/protocol/bc/types"
20         "github.com/vapor/protocol/vm/vmutil"
21 )
22
23 // DecodeControlAddressAction convert input data to action struct
24 func DecodeControlAddressAction(data []byte) (Action, error) {
25         a := new(controlAddressAction)
26         err := stdjson.Unmarshal(data, a)
27         return a, err
28 }
29
30 type controlAddressAction struct {
31         bc.AssetAmount
32         Address string `json:"address"`
33 }
34
35 func (a *controlAddressAction) Build(ctx context.Context, b *TemplateBuilder) error {
36         var missing []string
37         if a.Address == "" {
38                 missing = append(missing, "address")
39         }
40         if a.AssetId.IsZero() {
41                 missing = append(missing, "asset_id")
42         }
43         if a.Amount == 0 {
44                 missing = append(missing, "amount")
45         }
46         if len(missing) > 0 {
47                 return MissingFieldsError(missing...)
48         }
49
50         address, err := common.DecodeAddress(a.Address, &consensus.ActiveNetParams)
51         if err != nil {
52                 return err
53         }
54         redeemContract := address.ScriptAddress()
55         program := []byte{}
56
57         switch address.(type) {
58         case *common.AddressWitnessPubKeyHash:
59                 program, err = vmutil.P2WPKHProgram(redeemContract)
60         case *common.AddressWitnessScriptHash:
61                 program, err = vmutil.P2WSHProgram(redeemContract)
62         default:
63                 return errors.New("unsupport address type")
64         }
65         if err != nil {
66                 return err
67         }
68
69         out := types.NewTxOutput(*a.AssetId, a.Amount, program)
70         return b.AddOutput(out)
71 }
72
73 func (a *controlAddressAction) ActionType() string {
74         return "control_address"
75 }
76
77 // DecodeControlProgramAction convert input data to action struct
78 func DecodeControlProgramAction(data []byte) (Action, error) {
79         a := new(controlProgramAction)
80         err := stdjson.Unmarshal(data, a)
81         return a, err
82 }
83
84 type controlProgramAction struct {
85         bc.AssetAmount
86         Program json.HexBytes `json:"control_program"`
87 }
88
89 func (a *controlProgramAction) Build(ctx context.Context, b *TemplateBuilder) error {
90         var missing []string
91         if len(a.Program) == 0 {
92                 missing = append(missing, "control_program")
93         }
94         if a.AssetId.IsZero() {
95                 missing = append(missing, "asset_id")
96         }
97         if a.Amount == 0 {
98                 missing = append(missing, "amount")
99         }
100         if len(missing) > 0 {
101                 return MissingFieldsError(missing...)
102         }
103
104         out := types.NewTxOutput(*a.AssetId, a.Amount, a.Program)
105         return b.AddOutput(out)
106 }
107
108 func (a *controlProgramAction) ActionType() string {
109         return "control_program"
110 }
111
112 // DecodeRetireAction convert input data to action struct
113 func DecodeRetireAction(data []byte) (Action, error) {
114         a := new(retireAction)
115         err := stdjson.Unmarshal(data, a)
116         return a, err
117 }
118
119 type retireAction struct {
120         bc.AssetAmount
121         Arbitrary json.HexBytes `json:"arbitrary"`
122 }
123
124 func (a *retireAction) Build(ctx context.Context, b *TemplateBuilder) error {
125         var missing []string
126         if a.AssetId.IsZero() {
127                 missing = append(missing, "asset_id")
128         }
129         if a.Amount == 0 {
130                 missing = append(missing, "amount")
131         }
132         if len(missing) > 0 {
133                 return MissingFieldsError(missing...)
134         }
135
136         program, err := vmutil.RetireProgram(a.Arbitrary)
137         if err != nil {
138                 return err
139         }
140         out := types.NewTxOutput(*a.AssetId, a.Amount, program)
141         return b.AddOutput(out)
142 }
143
144 func (a *retireAction) ActionType() string {
145         return "retire"
146 }
147
148 const (
149         file uint32 = iota
150         data
151 )
152
153 // DecodeIpfsDataAction convert input data to action struct
154 func DecodeIpfsDataAction(data []byte) (Action, error) {
155         a := new(dataAction)
156         err := stdjson.Unmarshal(data, a)
157         return a, err
158 }
159
160 type dataAction struct {
161         bc.AssetAmount
162         Type uint32 `json:"data_type"`
163         Data string `json:"data"`
164 }
165
166 func (a *dataAction) Build(ctx context.Context, b *TemplateBuilder) error {
167         var r io.Reader
168
169         switch a.Type {
170         case file:
171                 fi, err := os.Stat(a.Data)
172                 if os.IsNotExist(err) {
173                         return err
174                 }
175                 if fi.IsDir() {
176                         return fmt.Errorf("data [%s] is directory", a.Data)
177                 }
178                 r, err = os.Open(a.Data)
179                 if err != nil {
180                         return err
181                 }
182         case data:
183                 if a.Data == "" {
184                         return errors.New("data is empty")
185                 }
186                 r = strings.NewReader(a.Data)
187         default:
188         }
189
190         sh := ipfs.NewShell(config.CommonConfig.IpfsAddress)
191         cid, err := sh.Add(r)
192         if err != nil {
193                 return err
194         }
195
196         program, err := vmutil.RetireProgram([]byte(cid))
197         if err != nil {
198                 return err
199         }
200         out := types.NewTxOutput(*a.AssetId, 0, program)
201         return b.AddOutput(out)
202 }
203
204 func (a *dataAction) ActionType() string {
205         return "ipfs_data"
206 }