diff --git a/transaction.go b/transaction.go index 13f1a3a..6e16fe1 100644 --- a/transaction.go +++ b/transaction.go @@ -69,6 +69,7 @@ type txInToKey struct { type TxInSerializer interface { TxInSerialize() []byte + MixinLen() int } type TxOut struct { @@ -86,10 +87,7 @@ type TransactionPrefix struct { type Transaction struct { TransactionPrefix - signatures []Signature - ringSignatures []RingSignature - hash []byte - blobSize uint32 + signatures [][]*Signature } func (h *Hash) Serialize() (result []byte) { @@ -151,6 +149,10 @@ func (t *txInGen) TxInSerialize() (result []byte) { return } +func (t *txInGen) MixinLen() int { + return 0 +} + func (t *txInToScript) TxInSerialize() (result []byte) { result = append([]byte{txInToScriptMarker}, t.prev...) result = append(result, Uint64ToBytes(t.prevOut)...) @@ -158,6 +160,10 @@ func (t *txInToScript) TxInSerialize() (result []byte) { return } +func (t *txInToScript) MixinLen() int { + return 0 +} + func (t *txInToScriptHash) TxInSerialize() (result []byte) { result = append([]byte{txInToScriptHashMarker}, t.prev.Serialize()...) result = append(result, Uint64ToBytes(t.prevOut)...) @@ -166,6 +172,10 @@ func (t *txInToScriptHash) TxInSerialize() (result []byte) { return } +func (t *txInToScriptHash) MixinLen() int { + return 0 +} + func (t *txInToKey) TxInSerialize() (result []byte) { result = append([]byte{txInToKeyMarker}, Uint64ToBytes(t.amount)...) result = append(result, Uint64ToBytes(uint64(len(t.keyOffsets)))...) @@ -176,12 +186,16 @@ func (t *txInToKey) TxInSerialize() (result []byte) { return } +func (t *txInToKey) MixinLen() int { + return len(t.keyOffsets) +} + func (t *TxOut) Serialize() (result []byte) { result = append(Uint64ToBytes(t.amount), t.target.TargetSerialize()...) return } -func (t *TransactionPrefix) Serialize() (result []byte) { +func (t *TransactionPrefix) SerializePrefix() (result []byte) { result = append(Uint64ToBytes(uint64(t.version)), Uint64ToBytes(t.unlockTime)...) result = append(result, Uint64ToBytes(uint64(len(t.vin)))...) for _, txIn := range t.vin { @@ -196,6 +210,16 @@ func (t *TransactionPrefix) Serialize() (result []byte) { return } +func (t *Transaction) Serialize() (result []byte) { + result = t.SerializePrefix() + for i := 0; i < len(t.signatures); i++ { + for j := 0; j < len(t.signatures[i]); j++ { + result = append(result, t.signatures[i][j].Serialize()...) + } + } + return +} + func ParseTxInGen(buf *bytes.Buffer) (txIn *txInGen, err error) { t := new(txInGen) t.height, err = ReadVarInt(buf) @@ -325,8 +349,42 @@ func ParseExtra(buf *bytes.Buffer) (extra []byte, err error) { return } -func ParseTransaction(buf *bytes.Buffer) (transaction *TransactionPrefix, err error) { - t := new(TransactionPrefix) +func ParseSignature(buf *bytes.Buffer) (signature *Signature, err error) { + s := new(Signature) + c := buf.Next(PubKeyLength) + if len(c) != PubKeyLength { + err = errors.New("Not enough bytes for signature c") + return + } + copy(s.c[:], c) + r := buf.Next(PubKeyLength) + if len(r) != PubKeyLength { + err = errors.New("Not enough bytes for signature r") + return + } + copy(s.r[:], r) + signature = s + return +} + +func ParseSignatures(mixinLengths []int, buf *bytes.Buffer) (signatures [][]*Signature, err error) { + // mixinLengths is the number of mixins at each input position + sigs := make([][]*Signature, len(mixinLengths), len(mixinLengths)) + for i, nMixin := range mixinLengths { + sigs[i] = make([]*Signature, nMixin, nMixin) + for j := 0; j < nMixin; j++ { + sigs[i][j], err = ParseSignature(buf) + if err != nil { + return + } + } + } + signatures = sigs + return +} + +func ParseTransaction(buf *bytes.Buffer) (transaction *Transaction, err error) { + t := new(Transaction) version, err := ReadVarInt(buf) if err != nil { return @@ -340,12 +398,17 @@ func ParseTransaction(buf *bytes.Buffer) (transaction *TransactionPrefix, err er if err != nil { return } + var mixinLengths []int t.vin = make([]TxInSerializer, int(numInputs), int(numInputs)) for i := 0; i < int(numInputs); i++ { t.vin[i], err = ParseTxIn(buf) if err != nil { return } + mixinLen := t.vin[i].MixinLen() + if mixinLen > 0 { + mixinLengths = append(mixinLengths, mixinLen) + } } numOutputs, err := ReadVarInt(buf) if err != nil { @@ -362,6 +425,14 @@ func ParseTransaction(buf *bytes.Buffer) (transaction *TransactionPrefix, err er if err != nil { return } + t.signatures, err = ParseSignatures(mixinLengths, buf) + if err != nil { + return + } + if buf.Len() != 0 { + err = errors.New("Buffer has extra data") + return + } transaction = t return } diff --git a/transaction_test.go b/transaction_test.go index 7ab8d1c..6dd0883 100644 --- a/transaction_test.go +++ b/transaction_test.go @@ -157,6 +157,7 @@ func TestTxInToKeyTransaction(t *testing.T) { outputSum uint64 inputKeyImages []string outputs []string + mixinLength int }{ { name: "50 inputs from block 58272", @@ -225,6 +226,7 @@ func TestTxInToKeyTransaction(t *testing.T) { "02bfae014409db5bbb21cfe04224b42a6476697df719ecc8b0d8ac65245c05482a", "022898f95d657d0fb76ebf551bea861d63657d71d53887df7f2aaa455c46488aaa", }, + mixinLength: 11, }, } for _, test := range tests { @@ -285,8 +287,20 @@ func TestTxInToKeyTransaction(t *testing.T) { t.Errorf("%s: output %d: want %x, got %x", test.name, i, wantOut, gotOut) } } + wantLen = len(test.inputKeyImages) + gotLen = len(transaction.signatures) + if wantLen != gotLen { + t.Errorf("%s: signature len: want %d, got %d", test.name, wantLen, gotLen) + } + for i, mixins := range transaction.signatures { + wantLen = test.mixinLength + gotLen = len(mixins) + if wantLen != gotLen { + t.Errorf("%s: mixin len for %d: want %d, got %d", test.name, i, wantLen, gotLen) + } + } gotSerialized := transaction.Serialize() - wantSerialized := serializedTx[:len(gotSerialized)] + wantSerialized := serializedTx if bytes.Compare(wantSerialized, gotSerialized) != 0 { t.Errorf("%s: serialized: want %x, got %x", test.name, wantSerialized, gotSerialized) }