github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/protocol/bc/types/map.go (about)

     1  package types
     2  
     3  import (
     4  	"github.com/bytom/bytom/consensus"
     5  	"github.com/bytom/bytom/protocol/bc"
     6  	"github.com/bytom/bytom/protocol/vm"
     7  	"github.com/bytom/bytom/protocol/vm/vmutil"
     8  )
     9  
    10  // MapTx converts a types TxData object into its entries-based
    11  // representation.
    12  func MapTx(oldTx *TxData) *bc.Tx {
    13  	txID, txHeader, entries := mapTx(oldTx)
    14  	tx := &bc.Tx{
    15  		TxHeader: txHeader,
    16  		ID:       txID,
    17  		Entries:  entries,
    18  		InputIDs: make([]bc.Hash, len(oldTx.Inputs)),
    19  	}
    20  
    21  	spentOutputIDs := make(map[bc.Hash]bool)
    22  	for id, e := range entries {
    23  		var ord uint64
    24  		switch e := e.(type) {
    25  		case *bc.Issuance:
    26  			ord = e.Ordinal
    27  
    28  		case *bc.Spend:
    29  			ord = e.Ordinal
    30  			spentOutputIDs[*e.SpentOutputId] = true
    31  			if *e.WitnessDestination.Value.AssetId == *consensus.BTMAssetID {
    32  				tx.GasInputIDs = append(tx.GasInputIDs, id)
    33  			}
    34  
    35  		case *bc.Coinbase:
    36  			ord = 0
    37  			tx.GasInputIDs = append(tx.GasInputIDs, id)
    38  
    39  		default:
    40  			continue
    41  		}
    42  
    43  		if ord >= uint64(len(oldTx.Inputs)) {
    44  			continue
    45  		}
    46  		tx.InputIDs[ord] = id
    47  	}
    48  
    49  	for id := range spentOutputIDs {
    50  		tx.SpentOutputIDs = append(tx.SpentOutputIDs, id)
    51  	}
    52  	return tx
    53  }
    54  
    55  func mapTx(tx *TxData) (headerID bc.Hash, hdr *bc.TxHeader, entryMap map[bc.Hash]bc.Entry) {
    56  	entryMap = make(map[bc.Hash]bc.Entry)
    57  	addEntry := func(e bc.Entry) bc.Hash {
    58  		id := bc.EntryID(e)
    59  		entryMap[id] = e
    60  		return id
    61  	}
    62  
    63  	var (
    64  		spends    []*bc.Spend
    65  		issuances []*bc.Issuance
    66  		coinbase  *bc.Coinbase
    67  	)
    68  
    69  	muxSources := make([]*bc.ValueSource, len(tx.Inputs))
    70  	for i, input := range tx.Inputs {
    71  		switch inp := input.TypedInput.(type) {
    72  		case *IssuanceInput:
    73  			nonceHash := inp.NonceHash()
    74  			assetDefHash := inp.AssetDefinitionHash()
    75  			value := input.AssetAmount()
    76  
    77  			issuance := bc.NewIssuance(&nonceHash, &value, uint64(i))
    78  			issuance.WitnessAssetDefinition = &bc.AssetDefinition{
    79  				Data: &assetDefHash,
    80  				IssuanceProgram: &bc.Program{
    81  					VmVersion: inp.VMVersion,
    82  					Code:      inp.IssuanceProgram,
    83  				},
    84  			}
    85  			issuance.WitnessArguments = inp.Arguments
    86  			issuanceID := addEntry(issuance)
    87  
    88  			muxSources[i] = &bc.ValueSource{
    89  				Ref:   &issuanceID,
    90  				Value: &value,
    91  			}
    92  			issuances = append(issuances, issuance)
    93  
    94  		case *SpendInput:
    95  			// create entry for prevout
    96  			prog := &bc.Program{VmVersion: inp.VMVersion, Code: inp.ControlProgram}
    97  			src := &bc.ValueSource{
    98  				Ref:      &inp.SourceID,
    99  				Value:    &inp.AssetAmount,
   100  				Position: inp.SourcePosition,
   101  			}
   102  			prevout := bc.NewOutput(src, prog, 0) // ordinal doesn't matter for prevouts, only for result outputs
   103  			prevoutID := addEntry(prevout)
   104  			// create entry for spend
   105  			spend := bc.NewSpend(&prevoutID, uint64(i))
   106  			spend.WitnessArguments = inp.Arguments
   107  			spendID := addEntry(spend)
   108  			// setup mux
   109  			muxSources[i] = &bc.ValueSource{
   110  				Ref:   &spendID,
   111  				Value: &inp.AssetAmount,
   112  			}
   113  			spends = append(spends, spend)
   114  
   115  		case *CoinbaseInput:
   116  			coinbase = bc.NewCoinbase(inp.Arbitrary)
   117  			coinbaseID := addEntry(coinbase)
   118  
   119  			out := tx.Outputs[0]
   120  			muxSources[i] = &bc.ValueSource{
   121  				Ref:   &coinbaseID,
   122  				Value: &out.AssetAmount,
   123  			}
   124  		}
   125  	}
   126  
   127  	mux := bc.NewMux(muxSources, &bc.Program{VmVersion: 1, Code: []byte{byte(vm.OP_TRUE)}})
   128  	muxID := addEntry(mux)
   129  
   130  	// connect the inputs to the mux
   131  	for _, spend := range spends {
   132  		spentOutput := entryMap[*spend.SpentOutputId].(*bc.Output)
   133  		spend.SetDestination(&muxID, spentOutput.Source.Value, spend.Ordinal)
   134  	}
   135  	for _, issuance := range issuances {
   136  		issuance.SetDestination(&muxID, issuance.Value, issuance.Ordinal)
   137  	}
   138  
   139  	if coinbase != nil {
   140  		coinbase.SetDestination(&muxID, mux.Sources[0].Value, 0)
   141  	}
   142  
   143  	// convert types.outputs to the bc.output
   144  	var resultIDs []*bc.Hash
   145  	for i, out := range tx.Outputs {
   146  		src := &bc.ValueSource{
   147  			Ref:      &muxID,
   148  			Value:    &out.AssetAmount,
   149  			Position: uint64(i),
   150  		}
   151  		var resultID bc.Hash
   152  		if vmutil.IsUnspendable(out.ControlProgram) {
   153  			// retirement
   154  			r := bc.NewRetirement(src, uint64(i))
   155  			resultID = addEntry(r)
   156  		} else {
   157  			// non-retirement
   158  			prog := &bc.Program{out.VMVersion, out.ControlProgram}
   159  			o := bc.NewOutput(src, prog, uint64(i))
   160  			resultID = addEntry(o)
   161  		}
   162  
   163  		dest := &bc.ValueDestination{
   164  			Value:    src.Value,
   165  			Ref:      &resultID,
   166  			Position: 0,
   167  		}
   168  		resultIDs = append(resultIDs, &resultID)
   169  		mux.WitnessDestinations = append(mux.WitnessDestinations, dest)
   170  	}
   171  
   172  	h := bc.NewTxHeader(tx.Version, tx.SerializedSize, tx.TimeRange, resultIDs)
   173  	return addEntry(h), h, entryMap
   174  }
   175  
   176  func mapBlockHeader(old *BlockHeader) (bc.Hash, *bc.BlockHeader) {
   177  	bh := bc.NewBlockHeader(old.Version, old.Height, &old.PreviousBlockHash, old.Timestamp, &old.TransactionsMerkleRoot, &old.TransactionStatusHash, old.Nonce, old.Bits)
   178  	return bc.EntryID(bh), bh
   179  }
   180  
   181  // MapBlock converts a types block to bc block
   182  func MapBlock(old *Block) *bc.Block {
   183  	if old == nil {
   184  		return nil
   185  	}
   186  
   187  	b := new(bc.Block)
   188  	b.ID, b.BlockHeader = mapBlockHeader(&old.BlockHeader)
   189  	for _, oldTx := range old.Transactions {
   190  		b.Transactions = append(b.Transactions, oldTx.Tx)
   191  	}
   192  	return b
   193  }