-
Notifications
You must be signed in to change notification settings - Fork 3
/
wallet.go
260 lines (229 loc) · 5.36 KB
/
wallet.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
package simpleBlockchain
import (
"bytes"
"crypto/ecdsa"
"crypto/rand"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
)
const (
walletFile = "wallet_%s"
)
type Wallet struct {
KeyPairs map[string]*KeyPair
}
func NewWallet(name string) (*Wallet, error){
filename := fmt.Sprintf(walletFile, name)
if IsFileExists(filename) == true {
return nil, errors.New("this file exist!!")
}
keypair := NewKeypair()
keyPairs := make(map[string]*KeyPair, 0)
keyPairs[publicKeyToPublicKeyHash(keypair.PublicKey)] = keypair
wallet := Wallet{
KeyPairs:keyPairs,
}
jsonBytes, err := json.Marshal(wallet)
if err != nil{
return nil, err
}
err = ioutil.WriteFile(filename, jsonBytes, 0644)
if err != nil {
return nil, err
}
return &wallet, nil
}
func GetExistWallet(name string) (*Wallet, error){
var wallet Wallet
filename := fmt.Sprintf(walletFile, name)
if IsFileExists(filename) == false {
return nil, errors.New("this file doesn't exist!!")
}
jsonBytes, err:= ioutil.ReadFile(filename)
if err!= nil {
return nil, err
}
err = json.Unmarshal(jsonBytes, &wallet)
if err!= nil {
return nil, err
}
return &wallet, nil
}
func (wallet *Wallet) getAddresses() ([]string, error) {
var addresses []string
for _, keypair := range wallet.KeyPairs {
addr, err := keypair.getAddress()
if err!= nil {
return nil, err
}
addresses = append(addresses, addr)
}
return addresses, nil
}
func (wallet *Wallet) getPublickeyHash() ([][]byte, error) {
var hashes [][]byte
addresses, err := wallet.getAddresses()
if err!= nil {
return nil, err
}
for _, addr := range addresses {
hash := AddressToPubkeyHash(addr)
hashes = append(hashes, hash)
}
return hashes, nil
}
func (wallet *Wallet) getPublickeys() [][]byte {
var publicKeys [][]byte
for _, keypair := range wallet.KeyPairs {
pubkey := keypair.PublicKey
publicKeys = append(publicKeys, pubkey)
}
return publicKeys
}
func (wallet *Wallet) signMessageByKey(key string, message []byte) ([]byte, error){
priv := wallet.KeyPairs[key].getECDSAPrivateKey()
r, s, err := ecdsa.Sign(rand.Reader, priv, message)
if err != nil {
return nil, err
}
signature := append(r.Bytes(),s.Bytes()...)
return signature, nil
}
func (wallet *Wallet) signTransaction(tx *Transaction) ([]byte, error) {
var scriptSigs [][]byte
scriptSigs = make([][]byte, len(tx.Inputs))
for i, input := range tx.Inputs {
key := hex.EncodeToString(input.ScriptSig)
temp := input.ScriptSig
cleanTx := tx.CopyCleanScriptSigTx()
cleanTx.Inputs[i].ScriptSig = temp
bcleanTx, _ := cleanTx.Serialize()
signature, err := wallet.signMessageByKey(key,bcleanTx)
if err != nil {
return nil, err
}
scriptSigs[i] = append(signature, wallet.KeyPairs[key].PublicKey...)
}
for i, scriptSig := range scriptSigs {
tx.Inputs[i].ScriptSig = scriptSig
}
btx, _:= tx.Serialize()
return btx, nil
}
type UTXO struct {
Unspent *TxOut `json:"unspent"`
Index uint `json:"index"`
Txid string `json:"txid"`
}
func (utxo *UTXO) Serialize() ([]byte, error) {
butxo, err := json.Marshal(utxo)
if err != nil{
return nil, err
}
return butxo, nil
}
func DeserializeUTXO(butxo []byte) (*UTXO, error){
var utxo UTXO
err := json.Unmarshal(butxo, &utxo)
if err != nil {
return nil, err
}
return &utxo, nil
}
func (utxo UTXO) String() string {
butxo, _ := json.MarshalIndent(utxo,""," ")
return string(butxo) + "\n"
}
type BlockchainWallet struct {
wallet *Wallet
server *Server
utxos map[string][]*UTXO
}
func NewBlockchainWallet(server *Server, wallet *Wallet) *BlockchainWallet{
bw := &BlockchainWallet{
wallet: wallet,
server: server,
}
bw.ScanUTXOs()
return bw
}
func (bw BlockchainWallet) GetBalance() int {
sum := 0
bw.ScanUTXOs()
for _, utxos := range bw.utxos {
for _, utxo := range utxos {
sum += utxo.Unspent.Value
}
}
return sum
}
func (bw BlockchainWallet) ScanUTXOs() error{
var utxos map[string][]*UTXO
allutxos, err := bw.server.blockchain.getUTXOs()
if err != nil {
return err
}
pubkeys := bw.wallet.getPublickeys()
for txid, outs := range allutxos {
for _, pubkey := range pubkeys {
for _, out := range outs {
if bytes.Compare(out.Unspent.ScriptPubKey, pubkey) == 0 {
utxos[txid] = append(utxos[txid], out)
}
}
}
}
bw.utxos = utxos
return nil
}
func (bw *BlockchainWallet) CreateTransaction(amount int, fee int, to string, change string) ([]byte, error){
var uses []*UTXO
var cost = 0
if len(bw.utxos) == 0 {
bw.ScanUTXOs()
}
for _, txouts:= range bw.utxos {
for _, txout := range txouts {
if cost < amount+fee {
uses = append(uses, txout)
cost = cost + txout.Unspent.Value
} else {
break
}
}
}
if cost < amount +fee {
return nil, fmt.Errorf("you don't have enough coin")
}
var tx *Transaction
for _, use := range uses {
input := &TxIn{
PrevTxHash: HexStrToBytes(use.Txid),
PrevTxOutIndex: uint(use.Index),
ScriptSig: use.Unspent.ScriptPubKey,
}
tx.Inputs = append(tx.Inputs, input)
}
out := &TxOut{
Value: amount,
ScriptPubKey: AddressToPubkeyHash(to),
}
tx.Outputs = append(tx.Outputs, out)
if cost - amount - fee > 0 {
receive := &TxOut{
Value: cost- amount - fee,
ScriptPubKey: AddressToPubkeyHash(change),
}
tx.Outputs = append(tx.Outputs, receive)
}
rawTx, err := bw.wallet.signTransaction(tx)
if err != nil {
return nil, err
}
return rawTx, nil
}
func (bw *BlockchainWallet) Send(){
}