-
Notifications
You must be signed in to change notification settings - Fork 13
/
mixinnet_address.go
151 lines (129 loc) · 4.19 KB
/
mixinnet_address.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package mixin
import (
"bytes"
"context"
"crypto/rand"
"errors"
"io"
"strconv"
"strings"
"github.com/btcsuite/btcutil/base58"
"github.com/shopspring/decimal"
)
const MainNetworkID = "XIN"
type (
MixinnetAddress struct {
PrivateSpendKey Key `json:"private_spend_key"`
PrivateViewKey Key `json:"private_view_key"`
PublicSpendKey Key `json:"public_spend_key"`
PublicViewKey Key `json:"public_view_key"`
}
)
func NewMixinnetAddress(rand io.Reader, public ...bool) *MixinnetAddress {
var a = MixinnetAddress{
PrivateSpendKey: NewKey(rand),
PrivateViewKey: NewKey(rand),
}
if len(public) > 0 && public[0] {
a.PrivateViewKey = a.PublicSpendKey.DeterministicHashDerive()
}
a.PublicSpendKey = a.PrivateSpendKey.Public()
a.PublicViewKey = a.PrivateViewKey.Public()
return &a
}
func NewMixinnetAddressFromString(s string) (MixinnetAddress, error) {
var a MixinnetAddress
if !strings.HasPrefix(s, MainNetworkID) {
return a, errors.New("invalid address network")
}
data := base58.Decode(s[len(MainNetworkID):])
if len(data) != 68 {
return a, errors.New("invalid address format")
}
checksum := NewHash(append([]byte(MainNetworkID), data[:64]...))
if !bytes.Equal(checksum[:4], data[64:]) {
return a, errors.New("invalid address checksum")
}
copy(a.PublicSpendKey[:], data[:32])
copy(a.PublicViewKey[:], data[32:])
return a, nil
}
func NewMixinnetAddressFromPublicSpend(publicSpend Key) *MixinnetAddress {
var a = MixinnetAddress{
PublicSpendKey: publicSpend,
}
a.PrivateViewKey = publicSpend.DeterministicHashDerive()
a.PublicViewKey = a.PrivateViewKey.Public()
return &a
}
func (a MixinnetAddress) String() string {
data := append([]byte(MainNetworkID), a.PublicSpendKey[:]...)
data = append(data, a.PublicViewKey[:]...)
checksum := NewHash(data)
data = append(a.PublicSpendKey[:], a.PublicViewKey[:]...)
data = append(data, checksum[:4]...)
return MainNetworkID + base58.Encode(data)
}
func (a MixinnetAddress) Hash() Hash {
return NewHash(append(a.PublicSpendKey[:], a.PublicViewKey[:]...))
}
func (a MixinnetAddress) MarshalJSON() ([]byte, error) {
return []byte(strconv.Quote(a.String())), nil
}
func (a *MixinnetAddress) UnmarshalJSON(b []byte) error {
unquoted, err := strconv.Unquote(string(b))
if err != nil {
return err
}
m, err := NewMixinnetAddressFromString(unquoted)
if err != nil {
return err
}
a.PrivateSpendKey = m.PrivateSpendKey
a.PrivateViewKey = m.PrivateViewKey
a.PublicSpendKey = m.PublicSpendKey
a.PublicViewKey = m.PublicViewKey
return nil
}
func (a MixinnetAddress) CreateUTXO(outputIndex int, amount decimal.Decimal) *Output {
r := NewKey(rand.Reader)
pubGhost := DeriveGhostPublicKey(&r, &a.PrivateSpendKey, &a.PrivateViewKey, outputIndex)
return &Output{
Type: 0,
Script: NewThresholdScript(1),
Amount: NewIntegerFromDecimal(amount),
Mask: r.Public(),
Keys: []Key{*pubGhost},
}
}
// 检查 transaction 是否是由该主网地址签发。满足以下所有条件则返回 true:
// 1. 该 transaction 只有一个 input ,且该 input 的类型为普通转账类型, 即 Hash 不为空
// 2. 该 input 对应的 utxo 只有一个 keys, 即 不是多签地址 转出
// 3. 该 input 的 mask & keys 可以使用该地址的 private view 和 public spend 碰撞通过
func VerifyTransaction(ctx context.Context, addr *MixinnetAddress, txHash Hash) (bool, error) {
if !addr.PrivateViewKey.HasValue() || !addr.PublicSpendKey.HasValue() {
return false, errors.New("invalid address: must contains both private view key and public spend key")
}
tx, err := GetTransaction(ctx, txHash)
if err != nil || !tx.Asset.HasValue() {
return false, err
}
if len(tx.Inputs) > 1 || tx.Inputs[0].Index < 0 || tx.Inputs[0].Hash == nil || !tx.Inputs[0].Hash.HasValue() {
return false, nil
}
preTx, err := GetTransaction(ctx, *tx.Inputs[0].Hash)
if err != nil || !preTx.Asset.HasValue() {
return false, err
}
if tx.Inputs[0].Index >= len(preTx.Outputs) {
return false, err
}
output := preTx.Outputs[tx.Inputs[0].Index]
if len(output.Keys) == 1 {
k := ViewGhostOutputKey(&output.Keys[0], &addr.PrivateViewKey, &output.Mask, tx.Inputs[0].Index)
if bytes.Compare(k[:], addr.PublicSpendKey[:]) == 0 {
return true, nil
}
}
return false, nil
}