This repository has been archived on 2024-04-07. You can view files and clone it, but cannot push or open issues or pull requests.
moneroutil/transaction.go

354 lines
7.2 KiB
Go
Raw Normal View History

2017-04-25 00:19:04 +00:00
package moneroutil
2017-04-27 23:21:44 +00:00
import (
"fmt"
2017-05-11 19:15:06 +00:00
"io"
2017-04-27 23:21:44 +00:00
)
const (
2017-05-14 00:41:43 +00:00
txInGenMarker = 0xff
txInToKeyMarker = 2
txOutToKeyMarker = 2
2017-04-27 23:21:44 +00:00
)
var UnimplementedError = fmt.Errorf("Unimplemented")
2017-04-25 00:19:04 +00:00
type txOutToKey struct {
2017-05-14 00:36:32 +00:00
key Key
2017-04-25 00:19:04 +00:00
}
2017-05-03 02:55:03 +00:00
type TxOutTargetSerializer interface {
TargetSerialize() []byte
2017-04-27 23:21:44 +00:00
String() string
2017-04-25 00:19:04 +00:00
}
type txInGen struct {
height uint64
}
type txInToKey struct {
amount uint64
keyOffsets []uint64
2017-05-14 00:36:32 +00:00
keyImage Key
2017-04-25 00:19:04 +00:00
}
type TxInSerializer interface {
TxInSerialize() []byte
2017-05-03 19:31:17 +00:00
MixinLen() int
2017-04-25 00:19:04 +00:00
}
type TxOut struct {
2017-04-27 23:21:44 +00:00
amount uint64
2017-05-03 02:55:03 +00:00
target TxOutTargetSerializer
2017-04-25 00:19:04 +00:00
}
type TransactionPrefix struct {
version uint32
unlockTime uint64
vin []TxInSerializer
2017-04-27 23:21:44 +00:00
vout []*TxOut
2017-04-25 00:19:04 +00:00
extra []byte
}
type Transaction struct {
TransactionPrefix
signatures []RingSignature
rctSignature *RctSig
2017-04-25 00:19:04 +00:00
}
2017-04-27 23:21:44 +00:00
func (h *Hash) Serialize() (result []byte) {
result = h[:]
return
}
2017-05-14 00:36:32 +00:00
func (p *Key) Serialize() (result []byte) {
2017-04-27 23:21:44 +00:00
result = p[:]
return
}
2017-05-03 02:55:03 +00:00
func (t *txOutToKey) TargetSerialize() (result []byte) {
result = append([]byte{txOutToKeyMarker}, t.key.Serialize()...)
2017-04-27 23:21:44 +00:00
return
}
func (t *txOutToKey) String() (result string) {
result = fmt.Sprintf("key: %x", t.key)
return
}
func (t *txInGen) TxInSerialize() (result []byte) {
2017-04-27 23:21:44 +00:00
result = append([]byte{txInGenMarker}, Uint64ToBytes(t.height)...)
return
}
2017-05-03 19:31:17 +00:00
func (t *txInGen) MixinLen() int {
return 0
}
func (t *txInToKey) TxInSerialize() (result []byte) {
2017-04-27 23:21:44 +00:00
result = append([]byte{txInToKeyMarker}, Uint64ToBytes(t.amount)...)
result = append(result, Uint64ToBytes(uint64(len(t.keyOffsets)))...)
2017-04-27 23:21:44 +00:00
for _, keyOffset := range t.keyOffsets {
result = append(result, Uint64ToBytes(keyOffset)...)
}
result = append(result, t.keyImage[:]...)
return
}
2017-05-03 19:31:17 +00:00
func (t *txInToKey) MixinLen() int {
return len(t.keyOffsets)
}
2017-04-27 23:21:44 +00:00
func (t *TxOut) Serialize() (result []byte) {
2017-05-03 02:55:03 +00:00
result = append(Uint64ToBytes(t.amount), t.target.TargetSerialize()...)
2017-04-27 23:21:44 +00:00
return
}
2017-05-03 19:31:17 +00:00
func (t *TransactionPrefix) SerializePrefix() (result []byte) {
2017-04-27 23:21:44 +00:00
result = append(Uint64ToBytes(uint64(t.version)), Uint64ToBytes(t.unlockTime)...)
result = append(result, Uint64ToBytes(uint64(len(t.vin)))...)
for _, txIn := range t.vin {
result = append(result, txIn.TxInSerialize()...)
2017-04-27 23:21:44 +00:00
}
result = append(result, Uint64ToBytes(uint64(len(t.vout)))...)
for _, txOut := range t.vout {
result = append(result, txOut.Serialize()...)
}
result = append(result, Uint64ToBytes(uint64(len(t.extra)))...)
result = append(result, t.extra...)
return
}
2017-05-05 03:11:50 +00:00
func (t *TransactionPrefix) PrefixHash() (hash Hash) {
hash = Keccak256(t.SerializePrefix())
return
}
func (t *TransactionPrefix) OutputSum() (sum uint64) {
for _, output := range t.vout {
sum += output.amount
}
return
}
2017-05-03 19:31:17 +00:00
func (t *Transaction) Serialize() (result []byte) {
result = t.SerializePrefix()
if t.version == 1 {
for i := 0; i < len(t.signatures); i++ {
result = append(result, t.signatures[i].Serialize()...)
}
} else {
result = append(result, t.rctSignature.SerializeBase()...)
result = append(result, t.rctSignature.SerializePrunable()...)
}
return
}
func (t *Transaction) SerializeBase() (result []byte) {
if t.version == 1 {
result = t.Serialize()
} else {
result = append(t.SerializePrefix(), t.rctSignature.SerializeBase()...)
}
return
}
func (t *Transaction) GetHash() (result Hash) {
if t.version == 1 {
result = Keccak256(t.Serialize())
} else {
// version 2 requires first computing 3 separate hashes
// prefix, rctBase and rctPrunable
// and then hashing the hashes together to get the final hash
2017-05-13 03:37:42 +00:00
prefixHash := t.PrefixHash()
rctBaseHash := t.rctSignature.BaseHash()
rctPrunableHash := t.rctSignature.PrunableHash()
result = Keccak256(prefixHash[:], rctBaseHash[:], rctPrunableHash[:])
2017-05-03 19:31:17 +00:00
}
return
}
2017-05-11 19:15:06 +00:00
func ParseTxInGen(buf io.Reader) (txIn *txInGen, err error) {
2017-04-27 23:21:44 +00:00
t := new(txInGen)
t.height, err = ReadVarInt(buf)
if err != nil {
return
}
txIn = t
return
}
2017-05-11 19:15:06 +00:00
func ParseTxInToKey(buf io.Reader) (txIn *txInToKey, err error) {
t := new(txInToKey)
t.amount, err = ReadVarInt(buf)
if err != nil {
return
}
keyOffsetLen, err := ReadVarInt(buf)
if err != nil {
return
}
t.keyOffsets = make([]uint64, keyOffsetLen, keyOffsetLen)
for i := 0; i < int(keyOffsetLen); i++ {
t.keyOffsets[i], err = ReadVarInt(buf)
if err != nil {
return
}
}
2017-05-14 00:36:32 +00:00
pubKey := make([]byte, KeyLength)
2017-05-11 19:15:06 +00:00
n, err := buf.Read(pubKey)
if err != nil {
return
}
2017-05-14 00:36:32 +00:00
if n != KeyLength {
2017-05-11 19:15:06 +00:00
err = fmt.Errorf("Buffer not long enough for public key")
return
}
copy(t.keyImage[:], pubKey)
txIn = t
2017-04-27 23:21:44 +00:00
return
}
2017-05-11 19:15:06 +00:00
func ParseTxIn(buf io.Reader) (txIn TxInSerializer, err error) {
marker := make([]byte, 1)
n, err := buf.Read(marker)
if n != 1 {
err = fmt.Errorf("Buffer not enough for TxIn")
return
}
2017-04-27 23:21:44 +00:00
if err != nil {
return
}
switch {
2017-05-11 19:15:06 +00:00
case marker[0] == txInGenMarker:
2017-04-27 23:21:44 +00:00
txIn, err = ParseTxInGen(buf)
2017-05-11 19:15:06 +00:00
case marker[0] == txInToKeyMarker:
2017-04-27 23:21:44 +00:00
txIn, err = ParseTxInToKey(buf)
}
return
}
2017-05-11 19:15:06 +00:00
func ParseTxOutToKey(buf io.Reader) (txOutTarget *txOutToKey, err error) {
2017-04-27 23:21:44 +00:00
t := new(txOutToKey)
2017-05-14 00:36:32 +00:00
pubKey := make([]byte, KeyLength)
2017-05-11 19:15:06 +00:00
n, err := buf.Read(pubKey)
if err != nil {
return
}
2017-05-14 00:36:32 +00:00
if n != KeyLength {
2017-05-11 19:15:06 +00:00
err = fmt.Errorf("Buffer not long enough for public key")
2017-04-27 23:21:44 +00:00
return
}
copy(t.key[:], pubKey)
txOutTarget = t
return
}
2017-05-11 19:15:06 +00:00
func ParseTxOut(buf io.Reader) (txOut *TxOut, err error) {
2017-04-27 23:21:44 +00:00
t := new(TxOut)
t.amount, err = ReadVarInt(buf)
if err != nil {
return
}
2017-05-11 19:15:06 +00:00
marker := make([]byte, 1)
n, err := buf.Read(marker)
2017-04-27 23:21:44 +00:00
if err != nil {
return
}
2017-05-11 19:15:06 +00:00
if n != 1 {
err = fmt.Errorf("Buffer not long enough for TxOut")
return
}
2017-04-27 23:21:44 +00:00
switch {
2017-05-11 19:15:06 +00:00
case marker[0] == txOutToKeyMarker:
2017-04-27 23:21:44 +00:00
t.target, err = ParseTxOutToKey(buf)
default:
2017-05-11 19:15:06 +00:00
err = fmt.Errorf("Bad Marker")
2017-04-27 23:21:44 +00:00
return
}
if err != nil {
return
}
txOut = t
return
}
2017-05-11 19:15:06 +00:00
func ParseExtra(buf io.Reader) (extra []byte, err error) {
2017-04-27 23:21:44 +00:00
length, err := ReadVarInt(buf)
if err != nil {
return
}
2017-05-11 19:15:06 +00:00
e := make([]byte, int(length))
n, err := buf.Read(e)
if err != nil {
return
}
if n != int(length) {
err = fmt.Errorf("Not enough bytes for extra")
2017-04-27 23:21:44 +00:00
return
}
extra = e
return
}
2017-05-11 19:15:06 +00:00
func ParseTransaction(buf io.Reader) (transaction *Transaction, err error) {
2017-05-03 19:31:17 +00:00
t := new(Transaction)
2017-04-27 23:21:44 +00:00
version, err := ReadVarInt(buf)
if err != nil {
return
}
t.version = uint32(version)
t.unlockTime, err = ReadVarInt(buf)
if err != nil {
return
}
numInputs, err := ReadVarInt(buf)
if err != nil {
return
}
2017-05-03 19:31:17 +00:00
var mixinLengths []int
t.vin = make([]TxInSerializer, int(numInputs), int(numInputs))
2017-04-27 23:21:44 +00:00
for i := 0; i < int(numInputs); i++ {
t.vin[i], err = ParseTxIn(buf)
if err != nil {
return
}
2017-05-03 19:31:17 +00:00
mixinLen := t.vin[i].MixinLen()
if mixinLen > 0 {
mixinLengths = append(mixinLengths, mixinLen)
}
2017-04-27 23:21:44 +00:00
}
numOutputs, err := ReadVarInt(buf)
if err != nil {
return
}
t.vout = make([]*TxOut, int(numOutputs), int(numOutputs))
for i := 0; i < int(numOutputs); i++ {
t.vout[i], err = ParseTxOut(buf)
if err != nil {
return
}
}
t.extra, err = ParseExtra(buf)
if err != nil {
return
}
2017-05-13 03:37:42 +00:00
if t.version == 1 {
t.signatures, err = ParseSignatures(mixinLengths, buf)
if err != nil {
return
}
} else {
2017-05-13 03:37:42 +00:00
var nMixins int
if len(mixinLengths) > 0 {
nMixins = mixinLengths[0] - 1
}
t.rctSignature, err = ParseRingCtSignature(buf, int(numInputs), int(numOutputs), nMixins)
if err != nil {
return
}
2017-05-03 19:31:17 +00:00
}
2017-04-27 23:21:44 +00:00
transaction = t
return
}