4 "github.com/bytom/consensus"
5 "github.com/bytom/crypto/sha3pool"
6 "github.com/bytom/protocol/bc"
7 "github.com/bytom/protocol/vm"
8 "github.com/bytom/protocol/vm/vmutil"
11 // MapTx converts a legacy TxData object into its entries-based
13 func MapTx(oldTx *TxData) *bc.Tx {
14 txid, header, entries := mapTx(oldTx)
20 InputIDs: make([]bc.Hash, len(oldTx.Inputs)),
24 nonceIDs = make(map[bc.Hash]bool)
25 spentOutputIDs = make(map[bc.Hash]bool)
27 for id, e := range entries {
29 switch e := e.(type) {
31 anchor, ok := entries[*e.AnchorId]
33 // this tx will be invalid because this issuance is
37 if _, ok := anchor.(*bc.Nonce); ok {
38 nonceIDs[*e.AnchorId] = true
41 // resume below after the switch
44 spentOutputIDs[*e.SpentOutputId] = true
46 // resume below after the switch
47 if *e.WitnessDestination.Value.AssetId == *consensus.BTMAssetID {
48 tx.GasInputIDs = append(tx.GasInputIDs, id)
55 if ord >= uint64(len(oldTx.Inputs)) {
56 continue // poorly-formed transaction
61 for id := range nonceIDs {
62 tx.NonceIDs = append(tx.NonceIDs, id)
64 for id := range spentOutputIDs {
65 tx.SpentOutputIDs = append(tx.SpentOutputIDs, id)
70 func mapTx(tx *TxData) (headerID bc.Hash, hdr *bc.TxHeader, entryMap map[bc.Hash]bc.Entry) {
71 entryMap = make(map[bc.Hash]bc.Entry)
73 addEntry := func(e bc.Entry) bc.Hash {
79 // Loop twice over tx.Inputs, once for spends and once for
80 // issuances. Do spends first so the entry ID of the first spend is
81 // available in case an issuance needs it for its anchor.
87 issuances []*bc.Issuance
89 muxSources = make([]*bc.ValueSource, len(tx.Inputs))
92 for i, inp := range tx.Inputs {
93 if oldSp, ok := inp.TypedInput.(*SpendInput); ok {
94 prog := &bc.Program{VmVersion: oldSp.VMVersion, Code: oldSp.ControlProgram}
95 src := &bc.ValueSource{
97 Value: &oldSp.AssetAmount,
98 Position: oldSp.SourcePosition,
100 out := bc.NewOutput(src, prog, &oldSp.RefDataHash, 0) // ordinal doesn't matter for prevouts, only for result outputs
101 prevoutID := addEntry(out)
102 refdatahash := hashData(inp.ReferenceData)
103 sp := bc.NewSpend(&prevoutID, &refdatahash, uint64(i))
104 sp.WitnessArguments = oldSp.Arguments
106 muxSources[i] = &bc.ValueSource{
108 Value: &oldSp.AssetAmount,
110 if firstSpend == nil {
114 spends = append(spends, sp)
118 for i, inp := range tx.Inputs {
119 if oldIss, ok := inp.TypedInput.(*IssuanceInput); ok {
120 // Note: asset definitions, initial block ids, and issuance
121 // programs are omitted here because they do not contribute to
122 // the body hash of an issuance.
126 setAnchored = func(*bc.Hash) {}
129 if len(oldIss.Nonce) > 0 {
130 assetID := oldIss.AssetID()
132 builder := vmutil.NewBuilder()
133 builder.AddData(oldIss.Nonce).AddOp(vm.OP_DROP)
134 builder.AddOp(vm.OP_ASSET).AddData(assetID.Bytes()).AddOp(vm.OP_EQUAL)
135 prog, _ := builder.Build() // error is impossible
137 nonce := bc.NewNonce(&bc.Program{VmVersion: 1, Code: prog})
138 anchorID = addEntry(nonce)
139 setAnchored = nonce.SetAnchored
140 } else if firstSpend != nil {
141 anchorID = firstSpendID
142 setAnchored = firstSpend.SetAnchored
145 val := inp.AssetAmount()
147 refdatahash := hashData(inp.ReferenceData)
148 assetdefhash := hashData(oldIss.AssetDefinition)
149 iss := bc.NewIssuance(&anchorID, &val, &refdatahash, uint64(i))
150 iss.WitnessAssetDefinition = &bc.AssetDefinition{
151 InitialBlockId: &oldIss.InitialBlock,
153 IssuanceProgram: &bc.Program{
154 VmVersion: oldIss.VMVersion,
155 Code: oldIss.IssuanceProgram,
158 iss.WitnessArguments = oldIss.Arguments
159 issID := addEntry(iss)
162 muxSources[i] = &bc.ValueSource{
166 issuances = append(issuances, iss)
170 if len(tx.Inputs) == 1 {
171 if oldCB, ok := tx.Inputs[0].TypedInput.(*CoinbaseInput); ok {
172 cb := bc.NewCoinbase(oldCB.Arbitrary)
176 muxSources = []*bc.ValueSource{{
178 Value: &out.AssetAmount,
184 mux := bc.NewMux(muxSources, &bc.Program{VmVersion: 1, Code: []byte{byte(vm.OP_TRUE)}})
185 muxID := addEntry(mux)
187 for _, sp := range spends {
188 spentOutput := entryMap[*sp.SpentOutputId].(*bc.Output)
189 sp.SetDestination(&muxID, spentOutput.Source.Value, sp.Ordinal)
191 for _, iss := range issuances {
192 iss.SetDestination(&muxID, iss.Value, iss.Ordinal)
196 muxSource := mux.Sources[0]
197 cb := entryMap[*muxSource.Ref].(*bc.Coinbase)
198 cb.SetDestination(&muxID, muxSource.Value, 0)
201 var resultIDs []*bc.Hash
203 for i, out := range tx.Outputs {
204 src := &bc.ValueSource{
206 Value: &out.AssetAmount,
209 var dest *bc.ValueDestination
210 if vmutil.IsUnspendable(out.ControlProgram) {
212 refdatahash := hashData(out.ReferenceData)
213 r := bc.NewRetirement(src, &refdatahash, uint64(i))
215 resultIDs = append(resultIDs, &rID)
216 dest = &bc.ValueDestination{
222 prog := &bc.Program{out.VMVersion, out.ControlProgram}
223 refdatahash := hashData(out.ReferenceData)
224 o := bc.NewOutput(src, prog, &refdatahash, uint64(i))
226 resultIDs = append(resultIDs, &oID)
227 dest = &bc.ValueDestination{
232 dest.Value = src.Value
233 mux.WitnessDestinations = append(mux.WitnessDestinations, dest)
236 refdatahash := hashData(tx.ReferenceData)
237 h := bc.NewTxHeader(tx.Version, tx.SerializedSize, tx.TimeRange, resultIDs, &refdatahash)
238 headerID = addEntry(h)
240 return headerID, h, entryMap
243 func mapBlockHeader(old *BlockHeader) (bhID bc.Hash, bh *bc.BlockHeader) {
244 bh = bc.NewBlockHeader(old.Version, old.Height, &old.PreviousBlockHash, &old.Seed, old.Timestamp, &old.TransactionsMerkleRoot, &old.AssetsMerkleRoot, &old.TransactionStatus, old.Nonce, old.Bits)
245 bhID = bc.EntryID(bh)
249 // MapBlock converts a legacy block to bc block
250 func MapBlock(old *Block) *bc.Block {
252 return nil // if old is nil, so should new be
255 b.ID, b.BlockHeader = mapBlockHeader(&old.BlockHeader)
256 for _, oldTx := range old.Transactions {
257 b.Transactions = append(b.Transactions, oldTx.Tx)
258 b.BlockHeader.SerializedSize += oldTx.TxData.SerializedSize
263 func hashData(data []byte) bc.Hash {
265 sha3pool.Sum256(b32[:], data)
266 return bc.NewHash(b32)