gitlab.com/SiaPrime/SiaPrime@v1.4.1/types/transaction_helpers.go (about)

     1  package types
     2  
     3  import (
     4  	"errors"
     5  )
     6  
     7  // TransactionGraphEdge defines an edge in a TransactionGraph, containing a
     8  // source transaction, a destination transaction, a value, and a miner fee.
     9  type TransactionGraphEdge struct {
    10  	Dest   int
    11  	Fee    Currency
    12  	Source int
    13  	Value  Currency
    14  }
    15  
    16  // TransactionGraph will return a set of valid transactions that all spend
    17  // outputs according to the input graph. Each [source, dest] pair defines an
    18  // edge of the graph. The graph must be fully connected and the granparent of
    19  // the graph must be the sourceOutput. '0' refers to an edge from the source
    20  // output. Each edge also specifies a value for the output, and an amount of
    21  // fees. If the fees are zero, no fees will be added for that edge. 'sources'
    22  // must be sorted.
    23  //
    24  // Example of acceptable input:
    25  //
    26  // sourceOutput: // a valid siacoin output spending to UnlockConditions{}.UnlockHash()
    27  //
    28  // Sources: [0, 0, 1, 2, 3, 3, 3, 4]
    29  // Dests:   [1, 2, 3, 3, 4, 4, 5, 6]
    30  //
    31  // Resulting Graph:
    32  //
    33  //    o
    34  //   / \
    35  //  o   o
    36  //   \ /
    37  //    o
    38  //   /|\
    39  //   \| \
    40  //    o  x // 'x' transactions are symbolic, not actually created
    41  //    |
    42  //    x
    43  //
    44  func TransactionGraph(sourceOutput SiacoinOutputID, edges []TransactionGraphEdge) ([]Transaction, error) {
    45  	// Basic input validation.
    46  	if len(edges) < 1 {
    47  		return nil, errors.New("no graph specificed")
    48  	}
    49  
    50  	// Check that the first value of 'sources' is zero, and that the rest of the
    51  	// array is sorted.
    52  	if edges[0].Source != 0 {
    53  		return nil, errors.New("first edge must speficy node 0 as the parent")
    54  	}
    55  	if edges[0].Dest != 1 {
    56  		return nil, errors.New("first edge must speficy node 1 as the child")
    57  	}
    58  	latest := edges[0].Source
    59  	for _, edge := range edges {
    60  		if edge.Source < latest {
    61  			return nil, errors.New("'sources' input is not sorted")
    62  		}
    63  		latest = edge.Source
    64  	}
    65  
    66  	// Create the set of output ids, and fill out the input ids for the source
    67  	// transaction.
    68  	biggest := 0
    69  	for _, edge := range edges {
    70  		if edge.Dest > biggest {
    71  			biggest = edge.Dest
    72  		}
    73  	}
    74  	txnInputs := make([][]SiacoinOutputID, biggest+1)
    75  	txnInputs[0] = []SiacoinOutputID{sourceOutput}
    76  
    77  	// Go through the nodes bit by bit and create outputs.
    78  	// Fill out the outputs for the source.
    79  	i, j := 0, 0
    80  	ts := make([]Transaction, edges[len(edges)-1].Source+1)
    81  	for i < len(edges) {
    82  		var t Transaction
    83  
    84  		// Grab the inputs for this transaction.
    85  		for _, outputID := range txnInputs[j] {
    86  			t.SiacoinInputs = append(t.SiacoinInputs, SiacoinInput{
    87  				ParentID: outputID,
    88  			})
    89  		}
    90  
    91  		// Grab the outputs for this transaction.
    92  		startingPoint := i
    93  		current := edges[i].Source
    94  		for i < len(edges) && edges[i].Source == current {
    95  			t.SiacoinOutputs = append(t.SiacoinOutputs, SiacoinOutput{
    96  				Value:      edges[i].Value,
    97  				UnlockHash: UnlockConditions{}.UnlockHash(),
    98  			})
    99  			if !edges[i].Fee.IsZero() {
   100  				t.MinerFees = append(t.MinerFees, edges[i].Fee)
   101  			}
   102  			i++
   103  		}
   104  
   105  		// Record the inputs for the next transactions.
   106  		for k := startingPoint; k < i; k++ {
   107  			txnInputs[edges[k].Dest] = append(txnInputs[edges[k].Dest], t.SiacoinOutputID(uint64(k-startingPoint)))
   108  		}
   109  		ts[j] = t
   110  		j++
   111  	}
   112  
   113  	return ts, nil
   114  }