4 "github.com/bytom/crypto/sha3pool"
5 "github.com/bytom/protocol/bc"
6 "github.com/bytom/protocol/vm"
7 "github.com/bytom/protocol/vm/vmutil"
10 // MapTx converts a legacy TxData object into its entries-based
12 func MapTx(oldTx *TxData) *bc.Tx {
13 txid, header, entries := mapTx(oldTx)
19 InputIDs: make([]bc.Hash, len(oldTx.Inputs)),
23 nonceIDs = make(map[bc.Hash]bool)
24 spentOutputIDs = make(map[bc.Hash]bool)
26 for id, e := range entries {
28 switch e := e.(type) {
30 anchor, ok := entries[*e.AnchorId]
32 // this tx will be invalid because this issuance is
36 if _, ok := anchor.(*bc.Nonce); ok {
37 nonceIDs[*e.AnchorId] = true
40 // resume below after the switch
43 spentOutputIDs[*e.SpentOutputId] = true
45 // resume below after the switch
50 if ord >= uint64(len(oldTx.Inputs)) {
51 continue // poorly-formed transaction
56 for id := range nonceIDs {
57 tx.NonceIDs = append(tx.NonceIDs, id)
59 for id := range spentOutputIDs {
60 tx.SpentOutputIDs = append(tx.SpentOutputIDs, id)
65 func mapTx(tx *TxData) (headerID bc.Hash, hdr *bc.TxHeader, entryMap map[bc.Hash]bc.Entry) {
66 entryMap = make(map[bc.Hash]bc.Entry)
68 addEntry := func(e bc.Entry) bc.Hash {
74 // Loop twice over tx.Inputs, once for spends and once for
75 // issuances. Do spends first so the entry ID of the first spend is
76 // available in case an issuance needs it for its anchor.
82 issuances []*bc.Issuance
83 muxSources = make([]*bc.ValueSource, len(tx.Inputs))
86 for i, inp := range tx.Inputs {
87 if oldSp, ok := inp.TypedInput.(*SpendInput); ok {
88 prog := &bc.Program{VmVersion: oldSp.VMVersion, Code: oldSp.ControlProgram}
89 src := &bc.ValueSource{
91 Value: &oldSp.AssetAmount,
92 Position: oldSp.SourcePosition,
94 out := bc.NewOutput(src, prog, &oldSp.RefDataHash, 0) // ordinal doesn't matter for prevouts, only for result outputs
95 prevoutID := addEntry(out)
96 refdatahash := hashData(inp.ReferenceData)
97 sp := bc.NewSpend(&prevoutID, &refdatahash, uint64(i))
98 sp.WitnessArguments = oldSp.Arguments
100 muxSources[i] = &bc.ValueSource{
102 Value: &oldSp.AssetAmount,
104 if firstSpend == nil {
108 spends = append(spends, sp)
112 for i, inp := range tx.Inputs {
113 if oldIss, ok := inp.TypedInput.(*IssuanceInput); ok {
114 // Note: asset definitions, initial block ids, and issuance
115 // programs are omitted here because they do not contribute to
116 // the body hash of an issuance.
120 setAnchored = func(*bc.Hash) {}
123 if len(oldIss.Nonce) > 0 {
124 assetID := oldIss.AssetID()
126 builder := vmutil.NewBuilder()
127 builder.AddData(oldIss.Nonce).AddOp(vm.OP_DROP)
128 builder.AddOp(vm.OP_ASSET).AddData(assetID.Bytes()).AddOp(vm.OP_EQUAL)
129 prog, _ := builder.Build() // error is impossible
131 nonce := bc.NewNonce(&bc.Program{VmVersion: 1, Code: prog})
132 anchorID = addEntry(nonce)
133 setAnchored = nonce.SetAnchored
134 } else if firstSpend != nil {
135 anchorID = firstSpendID
136 setAnchored = firstSpend.SetAnchored
139 val := inp.AssetAmount()
141 refdatahash := hashData(inp.ReferenceData)
142 assetdefhash := hashData(oldIss.AssetDefinition)
143 iss := bc.NewIssuance(&anchorID, &val, &refdatahash, uint64(i))
144 iss.WitnessAssetDefinition = &bc.AssetDefinition{
145 InitialBlockId: &oldIss.InitialBlock,
147 IssuanceProgram: &bc.Program{
148 VmVersion: oldIss.VMVersion,
149 Code: oldIss.IssuanceProgram,
152 iss.WitnessArguments = oldIss.Arguments
153 issID := addEntry(iss)
156 muxSources[i] = &bc.ValueSource{
160 issuances = append(issuances, iss)
164 mux := bc.NewMux(muxSources, &bc.Program{VmVersion: 1, Code: []byte{byte(vm.OP_TRUE)}})
165 muxID := addEntry(mux)
167 for _, sp := range spends {
168 spentOutput := entryMap[*sp.SpentOutputId].(*bc.Output)
169 sp.SetDestination(&muxID, spentOutput.Source.Value, sp.Ordinal)
171 for _, iss := range issuances {
172 iss.SetDestination(&muxID, iss.Value, iss.Ordinal)
175 var resultIDs []*bc.Hash
177 for i, out := range tx.Outputs {
178 src := &bc.ValueSource{
180 Value: &out.AssetAmount,
183 var dest *bc.ValueDestination
184 if vmutil.IsUnspendable(out.ControlProgram) {
186 refdatahash := hashData(out.ReferenceData)
187 r := bc.NewRetirement(src, &refdatahash, uint64(i))
189 resultIDs = append(resultIDs, &rID)
190 dest = &bc.ValueDestination{
196 prog := &bc.Program{out.VMVersion, out.ControlProgram}
197 refdatahash := hashData(out.ReferenceData)
198 o := bc.NewOutput(src, prog, &refdatahash, uint64(i))
200 resultIDs = append(resultIDs, &oID)
201 dest = &bc.ValueDestination{
206 dest.Value = src.Value
207 mux.WitnessDestinations = append(mux.WitnessDestinations, dest)
210 refdatahash := hashData(tx.ReferenceData)
211 h := bc.NewTxHeader(tx.Version, resultIDs, &refdatahash, tx.MinTime, tx.MaxTime)
212 headerID = addEntry(h)
214 return headerID, h, entryMap
217 func mapBlockHeader(old *BlockHeader) (bhID bc.Hash, bh *bc.BlockHeader) {
218 bh = bc.NewBlockHeader(old.Version, old.Height, &old.PreviousBlockHash, old.TimestampMS, &old.TransactionsMerkleRoot, &old.AssetsMerkleRoot, old.Nonce, old.Bits)
219 bhID = bc.EntryID(bh)
223 func MapBlock(old *Block) *bc.Block {
225 return nil // if old is nil, so should new be
228 b.ID, b.BlockHeader = mapBlockHeader(&old.BlockHeader)
229 for _, oldTx := range old.Transactions {
230 b.Transactions = append(b.Transactions, oldTx.Tx)
235 func hashData(data []byte) bc.Hash {
237 sha3pool.Sum256(b32[:], data)
238 return bc.NewHash(b32)