|
| 1 | +/** |
| 2 | + * Copyright 2017 Intel Corporation |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + * you may not use this file except in compliance with the License. |
| 6 | + * You may obtain a copy of the License at |
| 7 | + * |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + * |
| 10 | + * Unless required by applicable law or agreed to in writing, software |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + * See the License for the specific language governing permissions and |
| 14 | + * limitations under the License. |
| 15 | + * ------------------------------------------------------------------------------ |
| 16 | + */ |
| 17 | + |
| 18 | +package client |
| 19 | + |
| 20 | +import ( |
| 21 | + "encoding/hex" |
| 22 | + "fmt" |
| 23 | + "github.com/golang/protobuf/proto" |
| 24 | + "sawtooth_sdk/protobuf/batch_pb2" |
| 25 | + "sawtooth_sdk/protobuf/transaction_pb2" |
| 26 | + "time" |
| 27 | +) |
| 28 | + |
| 29 | +type TransactionParams struct { |
| 30 | + FamilyName string |
| 31 | + FamilyVersion string |
| 32 | + PayloadEncoding string |
| 33 | + Nonce string |
| 34 | + BatcherPubkey string |
| 35 | + Dependencies []string |
| 36 | + Inputs []string |
| 37 | + Outputs []string |
| 38 | +} |
| 39 | + |
| 40 | +type Encoder struct { |
| 41 | + privkey []byte |
| 42 | + pubkey string |
| 43 | + defaults TransactionParams |
| 44 | +} |
| 45 | + |
| 46 | +// NewTransactionEncoder constructs a new encoder which can be used to generate |
| 47 | +// transactions and batches, and to serialize batches for submitting to the |
| 48 | +// REST API. |
| 49 | +func NewEncoder(privkey []byte, defaults TransactionParams) *Encoder { |
| 50 | + return &Encoder{ |
| 51 | + privkey: privkey, |
| 52 | + pubkey: hex.EncodeToString(GenPubKey(privkey)), |
| 53 | + defaults: defaults, |
| 54 | + } |
| 55 | +} |
| 56 | + |
| 57 | +// -- Transactions -- |
| 58 | + |
| 59 | +// NewTransaction Creates a new transaction and handles the construction and |
| 60 | +// signing of the transaction header. |
| 61 | +func (self *Encoder) NewTransaction(payload []byte, p TransactionParams) *Transaction { |
| 62 | + h := &transaction_pb2.TransactionHeader{ |
| 63 | + // Load defaults |
| 64 | + FamilyName: self.defaults.FamilyName, |
| 65 | + FamilyVersion: self.defaults.FamilyVersion, |
| 66 | + PayloadEncoding: self.defaults.PayloadEncoding, |
| 67 | + Nonce: self.defaults.Nonce, |
| 68 | + BatcherPubkey: self.defaults.BatcherPubkey, |
| 69 | + |
| 70 | + Inputs: self.defaults.Inputs, |
| 71 | + Outputs: self.defaults.Outputs, |
| 72 | + Dependencies: self.defaults.Dependencies, |
| 73 | + |
| 74 | + // Set unique fields |
| 75 | + PayloadSha512: hex.EncodeToString(SHA512(payload)), |
| 76 | + SignerPubkey: self.pubkey, |
| 77 | + } |
| 78 | + |
| 79 | + // Override defaults if set |
| 80 | + if p.FamilyName != "" { |
| 81 | + h.FamilyName = p.FamilyName |
| 82 | + } |
| 83 | + if p.FamilyVersion != "" { |
| 84 | + h.FamilyVersion = p.FamilyVersion |
| 85 | + } |
| 86 | + if p.PayloadEncoding != "" { |
| 87 | + h.PayloadEncoding = p.PayloadEncoding |
| 88 | + } |
| 89 | + if p.Nonce != "" { |
| 90 | + h.Nonce = p.Nonce |
| 91 | + } |
| 92 | + if p.BatcherPubkey != "" { |
| 93 | + h.BatcherPubkey = p.BatcherPubkey |
| 94 | + } |
| 95 | + |
| 96 | + if p.Inputs != nil { |
| 97 | + h.Inputs = p.Inputs[:] |
| 98 | + } |
| 99 | + if p.Outputs != nil { |
| 100 | + h.Outputs = p.Outputs[:] |
| 101 | + } |
| 102 | + if p.Dependencies != nil { |
| 103 | + h.Dependencies = p.Dependencies[:] |
| 104 | + } |
| 105 | + |
| 106 | + // Generate a nonce if none has been set yet |
| 107 | + if h.Nonce == "" { |
| 108 | + h.Nonce = fmt.Sprintf("%x", time.Now().UTC().UnixNano()) |
| 109 | + } |
| 110 | + |
| 111 | + // If a BatcherPubkey hasn't been set yet, assume its our key |
| 112 | + if h.BatcherPubkey == "" { |
| 113 | + h.BatcherPubkey = self.pubkey |
| 114 | + } |
| 115 | + |
| 116 | + hb, err := proto.Marshal(h) |
| 117 | + if err != nil { |
| 118 | + panic(err) |
| 119 | + } |
| 120 | + hs := hex.EncodeToString(Sign(hb, self.privkey)) |
| 121 | + |
| 122 | + transaction := &transaction_pb2.Transaction{ |
| 123 | + Header: hb, |
| 124 | + HeaderSignature: hs, |
| 125 | + Payload: payload, |
| 126 | + } |
| 127 | + |
| 128 | + return (*Transaction)(transaction) |
| 129 | +} |
| 130 | + |
| 131 | +// SerializeTransactions serializes the given transactions to bytes for |
| 132 | +// transmission to a separate batcher. |
| 133 | +func SerializeTransactions(transactions []*Transaction) []byte { |
| 134 | + txns := make([]*transaction_pb2.Transaction, 0, len(transactions)) |
| 135 | + for _, tx := range transactions { |
| 136 | + txns = append(txns, tx.ToPb()) |
| 137 | + } |
| 138 | + |
| 139 | + tl := &transaction_pb2.TransactionList{ |
| 140 | + Transactions: txns, |
| 141 | + } |
| 142 | + |
| 143 | + tlb, err := proto.Marshal(tl) |
| 144 | + if err != nil { |
| 145 | + panic(err) |
| 146 | + } |
| 147 | + |
| 148 | + return tlb |
| 149 | +} |
| 150 | + |
| 151 | +// ParseTransactions deserializes the given bytes into a list of transactions. |
| 152 | +// The bytes are assumed to be in the format returned by SerializeTransactions. |
| 153 | +func ParseTransactions(b []byte) ([]*Transaction, error) { |
| 154 | + tl := &transaction_pb2.TransactionList{} |
| 155 | + err := proto.Unmarshal(b, tl) |
| 156 | + if err != nil { |
| 157 | + return nil, err |
| 158 | + } |
| 159 | + |
| 160 | + txns := tl.GetTransactions() |
| 161 | + |
| 162 | + transactions := make([]*Transaction, 0, len(txns)) |
| 163 | + for _, tx := range txns { |
| 164 | + transactions = append(transactions, (*Transaction)(tx)) |
| 165 | + } |
| 166 | + |
| 167 | + return transactions, nil |
| 168 | +} |
| 169 | + |
| 170 | +// -- Batches -- |
| 171 | + |
| 172 | +// NewBatch creates a new batch from the given transactions created by |
| 173 | +// NewTransaction. It handles the construction and signing of the batch header. |
| 174 | +func (self *Encoder) NewBatch(transactions []*Transaction) *Batch { |
| 175 | + txnIds := make([]string, 0, len(transactions)) |
| 176 | + txns := make([]*transaction_pb2.Transaction, 0, len(transactions)) |
| 177 | + for _, tx := range transactions { |
| 178 | + txnIds = append(txnIds, tx.Id()) |
| 179 | + txns = append(txns, tx.ToPb()) |
| 180 | + } |
| 181 | + |
| 182 | + h := &batch_pb2.BatchHeader{ |
| 183 | + SignerPubkey: self.pubkey, |
| 184 | + TransactionIds: txnIds, |
| 185 | + } |
| 186 | + |
| 187 | + hb, err := proto.Marshal(h) |
| 188 | + if err != nil { |
| 189 | + panic(err) |
| 190 | + } |
| 191 | + |
| 192 | + hs := hex.EncodeToString(Sign(hb, self.privkey)) |
| 193 | + |
| 194 | + batch := &batch_pb2.Batch{ |
| 195 | + Header: hb, |
| 196 | + HeaderSignature: hs, |
| 197 | + Transactions: txns, |
| 198 | + } |
| 199 | + |
| 200 | + return (*Batch)(batch) |
| 201 | +} |
| 202 | + |
| 203 | +// SerializeBatches serializes the given batches to bytes in the form expected |
| 204 | +// by the REST API. |
| 205 | +func SerializeBatches(batches []*Batch) []byte { |
| 206 | + bs := make([]*batch_pb2.Batch, 0, len(batches)) |
| 207 | + for _, b := range batches { |
| 208 | + bs = append(bs, b.ToPb()) |
| 209 | + } |
| 210 | + bl := &batch_pb2.BatchList{ |
| 211 | + Batches: bs, |
| 212 | + } |
| 213 | + |
| 214 | + blb, err := proto.Marshal(bl) |
| 215 | + if err != nil { |
| 216 | + panic(err) |
| 217 | + } |
| 218 | + |
| 219 | + return blb |
| 220 | +} |
| 221 | + |
| 222 | +// ParseBatches deserializes the given bytes into a list of batches. The bytes |
| 223 | +// are assumed to be in the format returned by SerializeBatches. |
| 224 | +func ParseBatches(b []byte) ([]*Batch, error) { |
| 225 | + bl := &batch_pb2.BatchList{} |
| 226 | + err := proto.Unmarshal(b, bl) |
| 227 | + if err != nil { |
| 228 | + return nil, err |
| 229 | + } |
| 230 | + |
| 231 | + bs := bl.GetBatches() |
| 232 | + |
| 233 | + batches := make([]*Batch, 0, len(bs)) |
| 234 | + for _, b := range bs { |
| 235 | + batches = append(batches, (*Batch)(b)) |
| 236 | + } |
| 237 | + |
| 238 | + return batches, nil |
| 239 | +} |
| 240 | + |
| 241 | +// -- Wrap Protobuf -- |
| 242 | + |
| 243 | +// Wrap the protobuf types so that they do not need to be imported separately. |
| 244 | +type Transaction transaction_pb2.Transaction |
| 245 | + |
| 246 | +func (t *Transaction) ToPb() *transaction_pb2.Transaction { |
| 247 | + return (*transaction_pb2.Transaction)(t) |
| 248 | +} |
| 249 | + |
| 250 | +// GetId Returns the Transaction ID which can be used to specify this |
| 251 | +// transaction as a dependency for other transactions. |
| 252 | +func (t *Transaction) Id() string { |
| 253 | + return t.ToPb().GetHeaderSignature() |
| 254 | +} |
| 255 | + |
| 256 | +type Batch batch_pb2.Batch |
| 257 | + |
| 258 | +func (b *Batch) ToPb() *batch_pb2.Batch { |
| 259 | + return (*batch_pb2.Batch)(b) |
| 260 | +} |
0 commit comments