forked from lightningnetwork/lightning-onion
/
main.go
161 lines (130 loc) · 4 KB
/
main.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
package main
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
"github.com/Actinium-project/acmd/btcec"
"github.com/Actinium-project/acmd/chaincfg"
sphinx "github.com/Actinium-project/lightning-onion"
)
type OnionHopSpec struct {
Realm int `json:"realm"`
PublicKey string `json:"pubkey"`
Payload string `json:"payload"`
}
type OnionSpec struct {
SessionKey string `json:"session_key,omitempty"`
Hops []OnionHopSpec `json:"hops"`
}
func parseOnionSpec(spec OnionSpec) (*sphinx.PaymentPath, *btcec.PrivateKey, error) {
var path sphinx.PaymentPath
var binSessionKey []byte
var err error
if spec.SessionKey != "" {
binSessionKey, err = hex.DecodeString(spec.SessionKey)
if err != nil {
log.Fatalf("Unable to decode the sessionKey %v: %v\n", spec.SessionKey, err)
}
if len(binSessionKey) != 32 {
log.Fatalf("Session key must be a 32 byte hex string: %v\n", spec.SessionKey)
}
} else {
binSessionKey = bytes.Repeat([]byte{'A'}, 32)
}
sessionKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), binSessionKey)
for i, hop := range spec.Hops {
binKey, err := hex.DecodeString(hop.PublicKey)
if err != nil || len(binKey) != 33 {
log.Fatalf("%s is not a valid hex pubkey %s", hop.PublicKey, err)
}
pubkey, err := btcec.ParsePubKey(binKey, btcec.S256())
if err != nil {
log.Fatalf("%s is not a valid hex pubkey %s", hop.PublicKey, err)
}
path[i].NodePub = *pubkey
payload, err := hex.DecodeString(hop.Payload)
if err != nil {
log.Fatalf("%s is not a valid hex payload %s",
hop.Payload, err)
}
hopPayload, err := sphinx.NewHopPayload(nil, payload)
if err != nil {
log.Fatalf("unable to make payload: %v", err)
}
path[i].HopPayload = hopPayload
fmt.Fprintf(os.Stderr, "Node %d pubkey %x\n", i, pubkey.SerializeCompressed())
}
return &path, sessionKey, nil
}
// main implements a simple command line utility that can be used in order to
// either generate a fresh mix-header or decode and fully process an existing
// one given a private key.
func main() {
args := os.Args
assocData := bytes.Repeat([]byte{'B'}, 32)
if len(args) < 3 {
fmt.Printf("Usage: %s (generate|decode) <input-file>\n", args[0])
return
} else if args[1] == "generate" {
var spec OnionSpec
jsonSpec, err := ioutil.ReadFile(args[2])
if err != nil {
log.Fatalf("Unable to read JSON onion spec from file %v: %v", args[2], err)
}
if err := json.Unmarshal(jsonSpec, &spec); err != nil {
log.Fatalf("Unable to parse JSON onion spec: %v", err)
}
path, sessionKey, err := parseOnionSpec(spec)
if err != nil {
log.Fatalf("could not parse onion spec: %v", err)
}
msg, err := sphinx.NewOnionPacket(
path, sessionKey, assocData,
sphinx.DeterministicPacketFiller,
)
if err != nil {
log.Fatalf("Error creating message: %v", err)
}
w := bytes.NewBuffer([]byte{})
err = msg.Encode(w)
if err != nil {
log.Fatalf("Error serializing message: %v", err)
}
fmt.Printf("%x\n", w.Bytes())
} else if args[1] == "decode" {
binKey, err := hex.DecodeString(args[2])
if len(binKey) != 32 || err != nil {
log.Fatalf("Argument not a valid hex private key")
}
hexBytes, _ := ioutil.ReadAll(os.Stdin)
binMsg, err := hex.DecodeString(strings.TrimSpace(string(hexBytes)))
if err != nil {
log.Fatalf("Error decoding message: %s", err)
}
privkey, _ := btcec.PrivKeyFromBytes(btcec.S256(), binKey)
replayLog := sphinx.NewMemoryReplayLog()
s := sphinx.NewRouter(privkey, &chaincfg.TestNet4Params, replayLog)
replayLog.Start()
defer replayLog.Stop()
var packet sphinx.OnionPacket
err = packet.Decode(bytes.NewBuffer(binMsg))
if err != nil {
log.Fatalf("Error parsing message: %v", err)
}
p, err := s.ProcessOnionPacket(&packet, assocData, 10)
if err != nil {
log.Fatalf("Failed to decode message: %s", err)
}
w := bytes.NewBuffer([]byte{})
err = p.NextPacket.Encode(w)
if err != nil {
log.Fatalf("Error serializing message: %v", err)
}
fmt.Printf("%x\n", w.Bytes())
}
}