read.go raw

   1  package psbt
   2  
   3  import (
   4  	"bytes"
   5  	"encoding/binary"
   6  	"errors"
   7  	"fmt"
   8  )
   9  
  10  // Errors the reader refuses.
  11  var (
  12  	// ErrNotPSBT is a blob that is not a BIP174 file.
  13  	ErrNotPSBT = errors.New("psbt: not a psbt")
  14  	// ErrTruncated is a record whose length runs past the end of the file.
  15  	ErrTruncated = errors.New("psbt: truncated")
  16  )
  17  
  18  // Preimage is one SHA-256 preimage record: the hash it opens and the preimage.
  19  type Preimage struct {
  20  	// SecretHash is the hash the record is keyed by.
  21  	SecretHash [32]byte
  22  	// Preimage is the preimage.
  23  	Preimage []byte
  24  }
  25  
  26  // Input is what a reader got out of one input map.
  27  type Input struct {
  28  	// Amount and ScriptPubKey come from the witness UTXO record, if it is there.
  29  	Amount       uint64
  30  	ScriptPubKey []byte
  31  	// WitnessScript is the P2WSH script, if the map carries one.
  32  	WitnessScript []byte
  33  	// Preimages are the preimage records.
  34  	Preimages []Preimage
  35  }
  36  
  37  // File is a parsed PSBT.
  38  type File struct {
  39  	// Unsigned is the global unsigned transaction, as serialised.
  40  	Unsigned []byte
  41  	// Inputs are the input maps, in order.
  42  	Inputs []Input
  43  	// Outputs is how many output maps were present.
  44  	Outputs int
  45  }
  46  
  47  // Read parses a PSBT.
  48  //
  49  // It is the other direction of Write, and it checks what it can check: magic, that every length
  50  // fits, and that a preimage record's key really is the hash of its value. A record whose
  51  // meaning it does not know is skipped rather than guessed at — a PSBT that carries something
  52  // this wallet did not write is not an error, it is another signer's business.
  53  func Read(raw []byte) (*File, error) {
  54  	if !bytes.HasPrefix(raw, magic) {
  55  		return nil, ErrNotPSBT
  56  	}
  57  
  58  	reader := &cursor{raw: raw, at: len(magic)}
  59  	file := &File{}
  60  
  61  	// The global map.
  62  	for {
  63  		key, value, err := reader.record()
  64  
  65  		if err != nil {
  66  			return nil, err
  67  		}
  68  
  69  		if key == nil {
  70  			break
  71  		}
  72  
  73  		switch key[0] {
  74  		case globalUnsignedTx:
  75  			file.Unsigned = value
  76  		}
  77  	}
  78  
  79  	// BIP174 does not count the maps, so the unsigned transaction does: it says how many input
  80  	// maps and how many output maps follow.
  81  	inputs, outputs, err := counts(file.Unsigned)
  82  
  83  	if err != nil {
  84  		return nil, err
  85  	}
  86  
  87  	for index := 0; index < inputs; index++ {
  88  		input := Input{}
  89  		emptied := false
  90  
  91  		for {
  92  			if !reader.more() {
  93  				return nil, fmt.Errorf("%w: an unclosed input map", ErrTruncated)
  94  			}
  95  
  96  			key, value, err := reader.record()
  97  
  98  			if err != nil {
  99  				return nil, err
 100  			}
 101  
 102  			if key == nil {
 103  				emptied = true
 104  
 105  				break
 106  			}
 107  
 108  			switch key[0] {
 109  			case inputWitnessUTXO:
 110  				if len(value) < 9 {
 111  					return nil, fmt.Errorf("%w: a witness utxo of %d bytes", ErrTruncated, len(value))
 112  				}
 113  
 114  				input.Amount = binary.LittleEndian.Uint64(value[0:8])
 115  
 116  				length := int(value[8])
 117  
 118  				if len(value) < 9+length {
 119  					return nil, fmt.Errorf("%w: a witness utxo script", ErrTruncated)
 120  				}
 121  
 122  				input.ScriptPubKey = value[9 : 9+length]
 123  			case inputWitnessScript:
 124  				input.WitnessScript = value
 125  			case inputSHA256Preimage:
 126  				if len(key) != 33 {
 127  					return nil, fmt.Errorf("%w: a preimage key of %d bytes", ErrTruncated, len(key))
 128  				}
 129  
 130  				record := Preimage{Preimage: value}
 131  				copy(record.SecretHash[:], key[1:])
 132  
 133  				if !bytes.Equal(sha256Of(value), record.SecretHash[:]) {
 134  					return nil, fmt.Errorf("%w: a preimage does not open %x", ErrTruncated, record.SecretHash)
 135  				}
 136  
 137  				input.Preimages = append(input.Preimages, record)
 138  			}
 139  		}
 140  
 141  		if !emptied {
 142  			break
 143  		}
 144  
 145  		file.Inputs = append(file.Inputs, input)
 146  	}
 147  
 148  	for index := 0; index < outputs; index++ {
 149  		for {
 150  			if !reader.more() {
 151  				return nil, fmt.Errorf("%w: an unclosed output map", ErrTruncated)
 152  			}
 153  
 154  			key, _, err := reader.record()
 155  
 156  			if err != nil {
 157  				return nil, err
 158  			}
 159  
 160  			if key == nil {
 161  				file.Outputs++
 162  
 163  				break
 164  			}
 165  		}
 166  	}
 167  
 168  	if reader.more() {
 169  		return nil, fmt.Errorf("%w: %d trailing bytes", ErrTruncated, len(reader.raw)-reader.at)
 170  	}
 171  
 172  	return file, nil
 173  }
 174  
 175  // cursor walks the file.
 176  type cursor struct {
 177  	raw []byte
 178  	at  int
 179  }
 180  
 181  // more reports whether there are bytes left.
 182  func (c *cursor) more() bool { return c.at < len(c.raw) }
 183  
 184  // record reads one key and value, or a nil key at a map's end.
 185  func (c *cursor) record() ([]byte, []byte, error) {
 186  	if !c.more() {
 187  		return nil, nil, nil
 188  	}
 189  
 190  	if c.raw[c.at] == 0x00 {
 191  		c.at++
 192  
 193  		return nil, nil, nil
 194  	}
 195  
 196  	length, err := c.size()
 197  
 198  	if err != nil {
 199  		return nil, nil, err
 200  	}
 201  
 202  	key, err := c.take(length)
 203  
 204  	if err != nil {
 205  		return nil, nil, err
 206  	}
 207  
 208  	length, err = c.size()
 209  
 210  	if err != nil {
 211  		return nil, nil, err
 212  	}
 213  
 214  	value, err := c.take(length)
 215  
 216  	if err != nil {
 217  		return nil, nil, err
 218  	}
 219  
 220  	return key, value, nil
 221  }
 222  
 223  // size reads a compact size.
 224  func (c *cursor) size() (int, error) {
 225  	if !c.more() {
 226  		return 0, ErrTruncated
 227  	}
 228  
 229  	first := c.raw[c.at]
 230  	c.at++
 231  
 232  	switch first {
 233  	case 0xfd:
 234  		raw, err := c.take(2)
 235  
 236  		if err != nil {
 237  			return 0, err
 238  		}
 239  
 240  		return int(binary.LittleEndian.Uint16(raw)), nil
 241  	case 0xfe:
 242  		raw, err := c.take(4)
 243  
 244  		if err != nil {
 245  			return 0, err
 246  		}
 247  
 248  		return int(binary.LittleEndian.Uint32(raw)), nil
 249  	}
 250  
 251  	return int(first), nil
 252  }
 253  
 254  // take reads n bytes.
 255  func (c *cursor) take(n int) ([]byte, error) {
 256  	if n < 0 || c.at+n > len(c.raw) {
 257  		return nil, fmt.Errorf("%w: %d bytes at %d", ErrTruncated, n, c.at)
 258  	}
 259  
 260  	out := c.raw[c.at : c.at+n]
 261  	c.at += n
 262  
 263  	return out, nil
 264  }
 265  
 266  // counts walks the unsigned transaction and reports how many inputs and outputs it has, which
 267  // is how many maps must follow.
 268  func counts(unsigned []byte) (int, int, error) {
 269  	cursor := &cursor{raw: unsigned}
 270  
 271  	if _, err := cursor.take(4); err != nil {
 272  		return 0, 0, err
 273  	}
 274  
 275  	inputs, err := cursor.size()
 276  
 277  	if err != nil {
 278  		return 0, 0, err
 279  	}
 280  
 281  	for index := 0; index < inputs; index++ {
 282  		if _, err := cursor.take(36); err != nil { // outpoint
 283  			return 0, 0, err
 284  		}
 285  
 286  		length, err := cursor.size()
 287  
 288  		if err != nil {
 289  			return 0, 0, err
 290  		}
 291  
 292  		if _, err := cursor.take(length); err != nil { // scriptSig
 293  			return 0, 0, err
 294  		}
 295  
 296  		if _, err := cursor.take(4); err != nil { // sequence
 297  			return 0, 0, err
 298  		}
 299  	}
 300  
 301  	outputs, err := cursor.size()
 302  
 303  	if err != nil {
 304  		return 0, 0, err
 305  	}
 306  
 307  	for index := 0; index < outputs; index++ {
 308  		if _, err := cursor.take(8); err != nil { // value
 309  			return 0, 0, err
 310  		}
 311  
 312  		length, err := cursor.size()
 313  
 314  		if err != nil {
 315  			return 0, 0, err
 316  		}
 317  
 318  		if _, err := cursor.take(length); err != nil { // script
 319  			return 0, 0, err
 320  		}
 321  	}
 322  
 323  	return inputs, outputs, nil
 324  }
 325