github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/types/transactions.go (about)

     1  package types
     2  
     3  // transaction.go defines the transaction type and all of the sub-fields of the
     4  // transaction, as well as providing helper functions for working with
     5  // transactions. The various IDs are designed such that, in a legal blockchain,
     6  // it is cryptographically unlikely that any two objects would share an id.
     7  
     8  import (
     9  	"encoding/json"
    10  	"errors"
    11  	"fmt"
    12  
    13  	"github.com/NebulousLabs/Sia/crypto"
    14  )
    15  
    16  const (
    17  	SpecifierLen = 16
    18  )
    19  
    20  // These Specifiers are used internally when calculating a type's ID. See
    21  // Specifier for more details.
    22  var (
    23  	SpecifierMinerPayout          = Specifier{'m', 'i', 'n', 'e', 'r', ' ', 'p', 'a', 'y', 'o', 'u', 't'}
    24  	SpecifierSiacoinInput         = Specifier{'s', 'i', 'a', 'c', 'o', 'i', 'n', ' ', 'i', 'n', 'p', 'u', 't'}
    25  	SpecifierSiacoinOutput        = Specifier{'s', 'i', 'a', 'c', 'o', 'i', 'n', ' ', 'o', 'u', 't', 'p', 'u', 't'}
    26  	SpecifierFileContract         = Specifier{'f', 'i', 'l', 'e', ' ', 'c', 'o', 'n', 't', 'r', 'a', 'c', 't'}
    27  	SpecifierFileContractRevision = Specifier{'f', 'i', 'l', 'e', ' ', 'c', 'o', 'n', 't', 'r', 'a', 'c', 't', ' ', 'r', 'e'}
    28  	SpecifierStorageProof         = Specifier{'s', 't', 'o', 'r', 'a', 'g', 'e', ' ', 'p', 'r', 'o', 'o', 'f'}
    29  	SpecifierStorageProofOutput   = Specifier{'s', 't', 'o', 'r', 'a', 'g', 'e', ' ', 'p', 'r', 'o', 'o', 'f'}
    30  	SpecifierSiafundInput         = Specifier{'s', 'i', 'a', 'f', 'u', 'n', 'd', ' ', 'i', 'n', 'p', 'u', 't'}
    31  	SpecifierSiafundOutput        = Specifier{'s', 'i', 'a', 'f', 'u', 'n', 'd', ' ', 'o', 'u', 't', 'p', 'u', 't'}
    32  	SpecifierClaimOutput          = Specifier{'c', 'l', 'a', 'i', 'm', ' ', 'o', 'u', 't', 'p', 'u', 't'}
    33  	SpecifierMinerFee             = Specifier{'m', 'i', 'n', 'e', 'r', ' ', 'f', 'e', 'e'}
    34  
    35  	ErrTransactionIDWrongLen = errors.New("input has wrong length to be an encoded transaction id")
    36  )
    37  
    38  type (
    39  	// A Specifier is a fixed-length byte-array that serves two purposes. In
    40  	// the wire protocol, they are used to identify a particular encoding
    41  	// algorithm, signature algorithm, etc. This allows nodes to communicate on
    42  	// their own terms; for example, to reduce bandwidth costs, a node might
    43  	// only accept compressed messages.
    44  	//
    45  	// Internally, Specifiers are used to guarantee unique IDs. Various
    46  	// consensus types have an associated ID, calculated by hashing the data
    47  	// contained in the type. By prepending the data with Specifier, we can
    48  	// guarantee that distinct types will never produce the same hash.
    49  	Specifier [SpecifierLen]byte
    50  
    51  	// IDs are used to refer to a type without revealing its contents. They
    52  	// are constructed by hashing specific fields of the type, along with a
    53  	// Specifier. While all of these types are hashes, defining type aliases
    54  	// gives us type safety and makes the code more readable.
    55  	TransactionID   crypto.Hash
    56  	SiacoinOutputID crypto.Hash
    57  	SiafundOutputID crypto.Hash
    58  	FileContractID  crypto.Hash
    59  	OutputID        crypto.Hash
    60  
    61  	// A Transaction is an atomic component of a block. Transactions can contain
    62  	// inputs and outputs, file contracts, storage proofs, and even arbitrary
    63  	// data. They can also contain signatures to prove that a given party has
    64  	// approved the transaction, or at least a particular subset of it.
    65  	//
    66  	// Transactions can depend on other previous transactions in the same block,
    67  	// but transactions cannot spend outputs that they create or otherwise be
    68  	// self-dependent.
    69  	Transaction struct {
    70  		SiacoinInputs         []SiacoinInput         `json:"siacoininputs"`
    71  		SiacoinOutputs        []SiacoinOutput        `json:"siacoinoutputs"`
    72  		FileContracts         []FileContract         `json:"filecontracts"`
    73  		FileContractRevisions []FileContractRevision `json:"filecontractrevisions"`
    74  		StorageProofs         []StorageProof         `json:"storageproofs"`
    75  		SiafundInputs         []SiafundInput         `json:"siafundinputs"`
    76  		SiafundOutputs        []SiafundOutput        `json:"siafundoutputs"`
    77  		MinerFees             []Currency             `json:"minerfees"`
    78  		ArbitraryData         [][]byte               `json:"arbitrarydata"`
    79  		TransactionSignatures []TransactionSignature `json:"transactionsignatures"`
    80  	}
    81  
    82  	// A SiacoinInput consumes a SiacoinOutput and adds the siacoins to the set of
    83  	// siacoins that can be spent in the transaction. The ParentID points to the
    84  	// output that is getting consumed, and the UnlockConditions contain the rules
    85  	// for spending the output. The UnlockConditions must match the UnlockHash of
    86  	// the output.
    87  	SiacoinInput struct {
    88  		ParentID         SiacoinOutputID  `json:"parentid"`
    89  		UnlockConditions UnlockConditions `json:"unlockconditions"`
    90  	}
    91  
    92  	// A SiacoinOutput holds a volume of siacoins. Outputs must be spent
    93  	// atomically; that is, they must all be spent in the same transaction. The
    94  	// UnlockHash is the hash of the UnlockConditions that must be fulfilled
    95  	// in order to spend the output.
    96  	SiacoinOutput struct {
    97  		Value      Currency   `json:"value"`
    98  		UnlockHash UnlockHash `json:"unlockhash"`
    99  	}
   100  
   101  	// A SiafundInput consumes a SiafundOutput and adds the siafunds to the set of
   102  	// siafunds that can be spent in the transaction. The ParentID points to the
   103  	// output that is getting consumed, and the UnlockConditions contain the rules
   104  	// for spending the output. The UnlockConditions must match the UnlockHash of
   105  	// the output.
   106  	SiafundInput struct {
   107  		ParentID         SiafundOutputID  `json:"parentid"`
   108  		UnlockConditions UnlockConditions `json:"unlockconditions"`
   109  		ClaimUnlockHash  UnlockHash       `json:"claimunlockhash"`
   110  	}
   111  
   112  	// A SiafundOutput holds a volume of siafunds. Outputs must be spent
   113  	// atomically; that is, they must all be spent in the same transaction. The
   114  	// UnlockHash is the hash of a set of UnlockConditions that must be fulfilled
   115  	// in order to spend the output.
   116  	//
   117  	// When the SiafundOutput is spent, a SiacoinOutput is created, where:
   118  	//
   119  	//     SiacoinOutput.Value := (SiafundPool - ClaimStart) / 10,000
   120  	//     SiacoinOutput.UnlockHash := SiafundOutput.ClaimUnlockHash
   121  	//
   122  	// When a SiafundOutput is put into a transaction, the ClaimStart must always
   123  	// equal zero. While the transaction is being processed, the ClaimStart is set
   124  	// to the value of the SiafundPool.
   125  	SiafundOutput struct {
   126  		Value      Currency   `json:"value"`
   127  		UnlockHash UnlockHash `json:"unlockhash"`
   128  		ClaimStart Currency   `json:"claimstart"`
   129  	}
   130  )
   131  
   132  // ID returns the id of a transaction, which is taken by marshalling all of the
   133  // fields except for the signatures and taking the hash of the result.
   134  func (t Transaction) ID() TransactionID {
   135  	return TransactionID(crypto.HashAll(
   136  		t.SiacoinInputs,
   137  		t.SiacoinOutputs,
   138  		t.FileContracts,
   139  		t.FileContractRevisions,
   140  		t.StorageProofs,
   141  		t.SiafundInputs,
   142  		t.SiafundOutputs,
   143  		t.MinerFees,
   144  		t.ArbitraryData,
   145  	))
   146  }
   147  
   148  // SiacoinOutputID returns the ID of a siacoin output at the given index,
   149  // which is calculated by hashing the concatenation of the SiacoinOutput
   150  // Specifier, all of the fields in the transaction (except the signatures),
   151  // and output index.
   152  func (t Transaction) SiacoinOutputID(i uint64) SiacoinOutputID {
   153  	return SiacoinOutputID(crypto.HashAll(
   154  		SpecifierSiacoinOutput,
   155  		t.SiacoinInputs,
   156  		t.SiacoinOutputs,
   157  		t.FileContracts,
   158  		t.FileContractRevisions,
   159  		t.StorageProofs,
   160  		t.SiafundInputs,
   161  		t.SiafundOutputs,
   162  		t.MinerFees,
   163  		t.ArbitraryData,
   164  		i,
   165  	))
   166  }
   167  
   168  // FileContractID returns the ID of a file contract at the given index, which
   169  // is calculated by hashing the concatenation of the FileContract Specifier,
   170  // all of the fields in the transaction (except the signatures), and the
   171  // contract index.
   172  func (t Transaction) FileContractID(i uint64) FileContractID {
   173  	return FileContractID(crypto.HashAll(
   174  		SpecifierFileContract,
   175  		t.SiacoinInputs,
   176  		t.SiacoinOutputs,
   177  		t.FileContracts,
   178  		t.FileContractRevisions,
   179  		t.StorageProofs,
   180  		t.SiafundInputs,
   181  		t.SiafundOutputs,
   182  		t.MinerFees,
   183  		t.ArbitraryData,
   184  		i,
   185  	))
   186  }
   187  
   188  // SiafundOutputID returns the ID of a SiafundOutput at the given index, which
   189  // is calculated by hashing the concatenation of the SiafundOutput Specifier,
   190  // all of the fields in the transaction (except the signatures), and output
   191  // index.
   192  func (t Transaction) SiafundOutputID(i uint64) SiafundOutputID {
   193  	return SiafundOutputID(crypto.HashAll(
   194  		SpecifierSiafundOutput,
   195  		t.SiacoinInputs,
   196  		t.SiacoinOutputs,
   197  		t.FileContracts,
   198  		t.FileContractRevisions,
   199  		t.StorageProofs,
   200  		t.SiafundInputs,
   201  		t.SiafundOutputs,
   202  		t.MinerFees,
   203  		t.ArbitraryData,
   204  		i,
   205  	))
   206  }
   207  
   208  // SiacoinOutputSum returns the sum of all the siacoin outputs in the
   209  // transaction, which must match the sum of all the siacoin inputs. Siacoin
   210  // outputs created by storage proofs and siafund outputs are not considered, as
   211  // they were considered when the contract responsible for funding them was
   212  // created.
   213  func (t Transaction) SiacoinOutputSum() (sum Currency) {
   214  	// Add the siacoin outputs.
   215  	for _, sco := range t.SiacoinOutputs {
   216  		sum = sum.Add(sco.Value)
   217  	}
   218  
   219  	// Add the file contract payouts.
   220  	for _, fc := range t.FileContracts {
   221  		sum = sum.Add(fc.Payout)
   222  	}
   223  
   224  	// Add the miner fees.
   225  	for _, fee := range t.MinerFees {
   226  		sum = sum.Add(fee)
   227  	}
   228  
   229  	return
   230  }
   231  
   232  // SiaClaimOutputID returns the ID of the SiacoinOutput that is created when
   233  // the siafund output is spent. The ID is the hash the SiafundOutputID.
   234  func (id SiafundOutputID) SiaClaimOutputID() SiacoinOutputID {
   235  	return SiacoinOutputID(crypto.HashObject(id))
   236  }
   237  
   238  // Below this point is a bunch of repeated definitions so that all type aliases
   239  // of 'crypto.Hash' are printed as hex strings. A notable exception is
   240  // types.Target, which is still printed as a byte array.
   241  
   242  // MarshalJSON marshales a specifier as a hex string.
   243  func (s Specifier) MarshalJSON() ([]byte, error) {
   244  	return json.Marshal(s.String())
   245  }
   246  
   247  // String prints the specifier in hex.
   248  func (s Specifier) String() string {
   249  	var i int
   250  	for i = 0; i < len(s); i++ {
   251  		if s[i] == 0 {
   252  			break
   253  		}
   254  	}
   255  	return string(s[:i])
   256  }
   257  
   258  // UnmarshalJSON decodes the json hex string of the id.
   259  func (s *Specifier) UnmarshalJSON(b []byte) error {
   260  	// Copy b into s, minus the json quotation marks.
   261  	copy(s[:], b[1:len(b)-1])
   262  	return nil
   263  }
   264  
   265  // MarshalJSON marshales an id as a hex string.
   266  func (tid TransactionID) MarshalJSON() ([]byte, error) {
   267  	return json.Marshal(tid.String())
   268  }
   269  
   270  // String prints the id in hex.
   271  func (tid TransactionID) String() string {
   272  	return fmt.Sprintf("%x", tid[:])
   273  }
   274  
   275  // UnmarshalJSON decodes the json hex string of the id.
   276  func (tid *TransactionID) UnmarshalJSON(b []byte) error {
   277  	if len(b) != crypto.HashSize*2+2 {
   278  		return crypto.ErrHashWrongLen
   279  	}
   280  
   281  	var tidBytes []byte
   282  	_, err := fmt.Sscanf(string(b[1:len(b)-1]), "%x", &tidBytes)
   283  	if err != nil {
   284  		return errors.New("could not unmarshal types.BlockID: " + err.Error())
   285  	}
   286  	copy(tid[:], tidBytes)
   287  	return nil
   288  }
   289  
   290  // MarshalJSON marshales an id as a hex string.
   291  func (oid OutputID) MarshalJSON() ([]byte, error) {
   292  	return json.Marshal(oid.String())
   293  }
   294  
   295  // String prints the id in hex.
   296  func (oid OutputID) String() string {
   297  	return fmt.Sprintf("%x", oid[:])
   298  }
   299  
   300  // UnmarshalJSON decodes the json hex string of the id.
   301  func (oid *OutputID) UnmarshalJSON(b []byte) error {
   302  	if len(b) != crypto.HashSize*2+2 {
   303  		return crypto.ErrHashWrongLen
   304  	}
   305  
   306  	var oidBytes []byte
   307  	_, err := fmt.Sscanf(string(b[1:len(b)-1]), "%x", &oidBytes)
   308  	if err != nil {
   309  		return errors.New("could not unmarshal types.BlockID: " + err.Error())
   310  	}
   311  	copy(oid[:], oidBytes)
   312  	return nil
   313  }
   314  
   315  // MarshalJSON marshales an id as a hex string.
   316  func (scoid SiacoinOutputID) MarshalJSON() ([]byte, error) {
   317  	return json.Marshal(scoid.String())
   318  }
   319  
   320  // String prints the id in hex.
   321  func (scoid SiacoinOutputID) String() string {
   322  	return fmt.Sprintf("%x", scoid[:])
   323  }
   324  
   325  // UnmarshalJSON decodes the json hex string of the id.
   326  func (scoid *SiacoinOutputID) UnmarshalJSON(b []byte) error {
   327  	if len(b) != crypto.HashSize*2+2 {
   328  		return crypto.ErrHashWrongLen
   329  	}
   330  
   331  	var scoidBytes []byte
   332  	_, err := fmt.Sscanf(string(b[1:len(b)-1]), "%x", &scoidBytes)
   333  	if err != nil {
   334  		return errors.New("could not unmarshal types.BlockID: " + err.Error())
   335  	}
   336  	copy(scoid[:], scoidBytes)
   337  	return nil
   338  }
   339  
   340  // MarshalJSON marshales an id as a hex string.
   341  func (fcid FileContractID) MarshalJSON() ([]byte, error) {
   342  	return json.Marshal(fcid.String())
   343  }
   344  
   345  // String prints the id in hex.
   346  func (fcid FileContractID) String() string {
   347  	return fmt.Sprintf("%x", fcid[:])
   348  }
   349  
   350  // UnmarshalJSON decodes the json hex string of the id.
   351  func (fcid *FileContractID) UnmarshalJSON(b []byte) error {
   352  	if len(b) != crypto.HashSize*2+2 {
   353  		return crypto.ErrHashWrongLen
   354  	}
   355  
   356  	var fcidBytes []byte
   357  	_, err := fmt.Sscanf(string(b[1:len(b)-1]), "%x", &fcidBytes)
   358  	if err != nil {
   359  		return errors.New("could not unmarshal types.BlockID: " + err.Error())
   360  	}
   361  	copy(fcid[:], fcidBytes)
   362  	return nil
   363  }
   364  
   365  // MarshalJSON marshales an id as a hex string.
   366  func (sfoid SiafundOutputID) MarshalJSON() ([]byte, error) {
   367  	return json.Marshal(sfoid.String())
   368  }
   369  
   370  // String prints the id in hex.
   371  func (sfoid SiafundOutputID) String() string {
   372  	return fmt.Sprintf("%x", sfoid[:])
   373  }
   374  
   375  // UnmarshalJSON decodes the json hex string of the id.
   376  func (sfoid *SiafundOutputID) UnmarshalJSON(b []byte) error {
   377  	if len(b) != crypto.HashSize*2+2 {
   378  		return crypto.ErrHashWrongLen
   379  	}
   380  
   381  	var sfoidBytes []byte
   382  	_, err := fmt.Sscanf(string(b[1:len(b)-1]), "%x", &sfoidBytes)
   383  	if err != nil {
   384  		return errors.New("could not unmarshal types.BlockID: " + err.Error())
   385  	}
   386  	copy(sfoid[:], sfoidBytes)
   387  	return nil
   388  }