Added serialization/deserialization of ringct transactions
This commit is contained in:
parent
39dcd3a13f
commit
e5151327a2
|
@ -27,6 +27,11 @@ func TestKeccak256(t *testing.T) {
|
|||
messageHex: "0f3fe9c20b24a11bf4d6d1acd335c6a80543f1f0380590d7323caf1390c78e88",
|
||||
wantHex: "73b7a236f2a97c4e1805f7a319f1283e3276598567757186c526caf9a49e0a92",
|
||||
},
|
||||
{
|
||||
name: "hello",
|
||||
messageHex: "68656c6c6f",
|
||||
wantHex: "1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
message, _ := hex.DecodeString(test.messageHex)
|
||||
|
|
197
ringct.go
197
ringct.go
|
@ -1,13 +1,20 @@
|
|||
package moneroutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
RCTTypeNull = iota
|
||||
RCTTypeFull
|
||||
RCTTypeSimple
|
||||
|
||||
KeyLength = 32
|
||||
)
|
||||
|
||||
// Key for Confidential Transactions, can be private or public
|
||||
type Key [32]byte
|
||||
type Key [KeyLength]byte
|
||||
|
||||
// V = Vector, M = Matrix
|
||||
type KeyV []Key
|
||||
|
@ -63,15 +70,199 @@ type RctSigBase struct {
|
|||
pseudoOuts KeyV
|
||||
ecdhInfo []ecdhTuple
|
||||
outPk CtKeyV
|
||||
txnFee uint64
|
||||
txFee uint64
|
||||
}
|
||||
|
||||
type RctSigPrunable struct {
|
||||
rangeSigs []RangeSig
|
||||
MGs []MgSig
|
||||
mgSigs []MgSig
|
||||
}
|
||||
|
||||
type RctSig struct {
|
||||
RctSigBase
|
||||
RctSigPrunable
|
||||
}
|
||||
|
||||
func (k *Key64) Serialize() (result []byte) {
|
||||
for _, key := range k {
|
||||
result = append(result, key[:]...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (b *BoroSig) Serialize() (result []byte) {
|
||||
result = append(b.s0.Serialize(), b.s1.Serialize()...)
|
||||
result = append(result, b.ee[:]...)
|
||||
return
|
||||
}
|
||||
|
||||
func (r *RangeSig) Serialize() (result []byte) {
|
||||
result = append(r.asig.Serialize(), r.ci.Serialize()...)
|
||||
return
|
||||
}
|
||||
|
||||
func (m *MgSig) Serialize() (result []byte) {
|
||||
for i := 0; i < len(m.ss); i++ {
|
||||
for j := 0; j < len(m.ss[i]); j++ {
|
||||
result = append(result, m.ss[i][j][:]...)
|
||||
}
|
||||
}
|
||||
result = append(result, m.cc[:]...)
|
||||
return
|
||||
}
|
||||
|
||||
func (r *RctSigBase) SerializeBase() (result []byte) {
|
||||
result = []byte{r.sigType}
|
||||
// Null type returns right away
|
||||
if r.sigType == RCTTypeNull {
|
||||
return
|
||||
}
|
||||
result = append(result, Uint64ToBytes(r.txFee)...)
|
||||
if r.sigType == RCTTypeSimple {
|
||||
for _, input := range r.pseudoOuts {
|
||||
result = append(result, input[:]...)
|
||||
}
|
||||
}
|
||||
for _, ecdh := range r.ecdhInfo {
|
||||
result = append(result, ecdh.mask[:]...)
|
||||
result = append(result, ecdh.amount[:]...)
|
||||
}
|
||||
for _, ctKey := range r.outPk {
|
||||
result = append(result, ctKey.mask[:]...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r *RctSig) SerializePrunable() (result []byte) {
|
||||
if r.sigType == RCTTypeNull {
|
||||
return
|
||||
}
|
||||
for _, rangeSig := range r.rangeSigs {
|
||||
result = append(result, rangeSig.Serialize()...)
|
||||
}
|
||||
for _, mgSig := range r.mgSigs {
|
||||
result = append(result, mgSig.Serialize()...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ParseKey(buf io.Reader) (result Key, err error) {
|
||||
key := make([]byte, KeyLength)
|
||||
if _, err = buf.Read(key); err != nil {
|
||||
return
|
||||
}
|
||||
copy(result[:], key)
|
||||
return
|
||||
}
|
||||
|
||||
func ParseCtKey(buf io.Reader) (result CtKey, err error) {
|
||||
if result.mask, err = ParseKey(buf); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ParseKey64(buf io.Reader) (result Key64, err error) {
|
||||
for i := 0; i < 64; i++ {
|
||||
if result[i], err = ParseKey(buf); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ParseBoroSig(buf io.Reader) (result BoroSig, err error) {
|
||||
if result.s0, err = ParseKey64(buf); err != nil {
|
||||
return
|
||||
}
|
||||
if result.s1, err = ParseKey64(buf); err != nil {
|
||||
return
|
||||
}
|
||||
if result.ee, err = ParseKey(buf); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ParseRangeSig(buf io.Reader) (result RangeSig, err error) {
|
||||
if result.asig, err = ParseBoroSig(buf); err != nil {
|
||||
return
|
||||
}
|
||||
if result.ci, err = ParseKey64(buf); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ParseRingCtSignature(buf io.Reader, nInputs, nOutputs, nMixin int) (result *RctSig, err error) {
|
||||
r := new(RctSig)
|
||||
sigType := make([]byte, 1)
|
||||
_, err = buf.Read(sigType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r.sigType = uint8(sigType[0])
|
||||
if r.sigType != RCTTypeNull || r.sigType != RCTTypeFull || r.sigType != RCTTypeSimple {
|
||||
err = fmt.Errorf("Bad sigType %d", r.sigType)
|
||||
}
|
||||
if r.sigType == RCTTypeNull {
|
||||
result = r
|
||||
return
|
||||
}
|
||||
r.txFee, err = ReadVarInt(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var nMg, nSS int
|
||||
if r.sigType == RCTTypeSimple {
|
||||
nMg = nInputs
|
||||
nSS = 2
|
||||
r.pseudoOuts = make([]Key, nInputs)
|
||||
for i := 0; i < nInputs; i++ {
|
||||
if r.pseudoOuts[i], err = ParseKey(buf); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nMg = 1
|
||||
nSS = nInputs + 1
|
||||
}
|
||||
r.ecdhInfo = make([]ecdhTuple, nOutputs)
|
||||
for i := 0; i < nOutputs; i++ {
|
||||
if r.ecdhInfo[i].mask, err = ParseKey(buf); err != nil {
|
||||
return
|
||||
}
|
||||
if r.ecdhInfo[i].amount, err = ParseKey(buf); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
r.outPk = make([]CtKey, nOutputs)
|
||||
for i := 0; i < nOutputs; i++ {
|
||||
if r.outPk[i], err = ParseCtKey(buf); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
r.rangeSigs = make([]RangeSig, nOutputs)
|
||||
for i := 0; i < nOutputs; i++ {
|
||||
if r.rangeSigs[i], err = ParseRangeSig(buf); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
r.mgSigs = make([]MgSig, nMg)
|
||||
for i := 0; i < nMg; i++ {
|
||||
r.mgSigs[i].ss = make([]KeyV, nMixin+1)
|
||||
for j := 0; j < nMixin+1; j++ {
|
||||
r.mgSigs[i].ss[j] = make([]Key, nSS)
|
||||
for k := 0; k < nSS; k++ {
|
||||
if r.mgSigs[i].ss[j][k], err = ParseKey(buf); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if r.mgSigs[i].cc, err = ParseKey(buf); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
result = r
|
||||
return
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ const (
|
|||
txOutToKeyMarker = 2
|
||||
)
|
||||
|
||||
var UnimplementedError = fmt.Errorf("Unimplemented")
|
||||
|
||||
type txOutToScript struct {
|
||||
pubKeys []PubKey
|
||||
script []byte
|
||||
|
@ -76,7 +78,8 @@ type TransactionPrefix struct {
|
|||
|
||||
type Transaction struct {
|
||||
TransactionPrefix
|
||||
signatures []RingSignature
|
||||
signatures []RingSignature
|
||||
rctSignature *RctSig
|
||||
}
|
||||
|
||||
func (h *Hash) Serialize() (result []byte) {
|
||||
|
@ -90,7 +93,7 @@ func (p *PubKey) Serialize() (result []byte) {
|
|||
}
|
||||
|
||||
func (t *txOutToScript) TargetSerialize() (result []byte) {
|
||||
result = []byte{txInToScriptMarker}
|
||||
result = []byte{txOutToScriptMarker}
|
||||
for i, pubkey := range t.pubKeys {
|
||||
if i != 0 {
|
||||
result = append(result, byte(txOutToScriptMarker))
|
||||
|
@ -107,7 +110,7 @@ func (t *txOutToScript) String() (result string) {
|
|||
}
|
||||
|
||||
func (t *txOutToScriptHash) TargetSerialize() (result []byte) {
|
||||
result = append([]byte{txInToScriptHashMarker}, t.hash.Serialize()...)
|
||||
result = append([]byte{txOutToScriptHashMarker}, t.hash.Serialize()...)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -117,7 +120,7 @@ func (t *txOutToScriptHash) String() (result string) {
|
|||
}
|
||||
|
||||
func (t *txOutToKey) TargetSerialize() (result []byte) {
|
||||
result = append([]byte{txInToKeyMarker}, t.key.Serialize()...)
|
||||
result = append([]byte{txOutToKeyMarker}, t.key.Serialize()...)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -206,8 +209,37 @@ func (t *TransactionPrefix) OutputSum() (sum uint64) {
|
|||
|
||||
func (t *Transaction) Serialize() (result []byte) {
|
||||
result = t.SerializePrefix()
|
||||
for i := 0; i < len(t.signatures); i++ {
|
||||
result = append(result, t.signatures[i].Serialize()...)
|
||||
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
|
||||
prefixHash := Keccak256(t.SerializePrefix())
|
||||
rctBaseHash := Keccak256(t.rctSignature.SerializeBase())
|
||||
rctPrunableHash := Keccak256(t.rctSignature.SerializePrunable())
|
||||
result = Keccak256(prefixHash[:], rctBaseHash[:], rctPrunableHash[:])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -223,12 +255,12 @@ func ParseTxInGen(buf io.Reader) (txIn *txInGen, err error) {
|
|||
}
|
||||
|
||||
func ParseTxInToScript(buf io.Reader) (txIn *txInToScript, err error) {
|
||||
err = fmt.Errorf("Unimplemented")
|
||||
err = UnimplementedError
|
||||
return
|
||||
}
|
||||
|
||||
func ParseTxInToScriptHash(buf io.Reader) (txIn *txInToScriptHash, err error) {
|
||||
err = fmt.Errorf("Unimplemented")
|
||||
err = UnimplementedError
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -287,12 +319,12 @@ func ParseTxIn(buf io.Reader) (txIn TxInSerializer, err error) {
|
|||
}
|
||||
|
||||
func ParseTxOutToScript(buf io.Reader) (txOutTarget *txOutToScript, err error) {
|
||||
err = fmt.Errorf("Unimplemented")
|
||||
err = UnimplementedError
|
||||
return
|
||||
}
|
||||
|
||||
func ParseTxOutToScriptHash(buf io.Reader) (txOutTarget *txOutToScriptHash, err error) {
|
||||
err = fmt.Errorf("Unimplemented")
|
||||
err = UnimplementedError
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -405,9 +437,16 @@ func ParseTransaction(buf io.Reader) (transaction *Transaction, err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
t.signatures, err = ParseSignatures(mixinLengths, buf)
|
||||
if err != nil {
|
||||
return
|
||||
if version == 1 {
|
||||
t.signatures, err = ParseSignatures(mixinLengths, buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
t.rctSignature, err = ParseRingCtSignature(buf, int(numInputs), int(numOutputs), mixinLengths[0]-1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
transaction = t
|
||||
return
|
||||
|
|
File diff suppressed because one or more lines are too long
Reference in a new issue