Check-in [c316c57485]
Overview
Comment:Add an example for a batch offline signing/sweep system
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: c316c57485bfabc8bd32b34a2f738edb21d040bc2e2f0f540930c71e143820ae
User & Date: rkeene on 2018-07-03 19:52:28
Other Links: manifest | tags
Context
2018-07-05
14:56
Added high-level work interfaces check-in: 5c0fe18b67 user: rkeene tags: trunk
2018-07-03
19:52
Add an example for a batch offline signing/sweep system check-in: c316c57485 user: rkeene tags: trunk
19:51
Made default address format "xrb_" for now check-in: a6dade57ee user: rkeene tags: trunk
Changes

Added examples/offline-batch-sign/sign version [9a3ce01965].















































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#! /usr/bin/env tclsh

package require nano

# Generate (derive) many accounts for every supplied seed to ensure we find
# all pending balances
set numAccountsPerSeed 500

proc prompt {message var {default ""}} {
	upvar $var data

	puts -nonewline "${message}: "
	flush stdout

	gets stdin data
	if {$data eq ""} {
		set data $default
	}

	return $data
}

# Prompt the user for input
puts "> Nano Offline Batch Sign"
puts ">> Ultimate Destination"
while true {
	prompt "Nano Address" ultimateDestination
	if {[catch {
		set ultimateDestinationPubKey [::nano::address::toPublicKey $ultimateDestination -hex -verify]
	} err]} {
		puts "Error: $err"
		continue
	}
	break
}

puts ">> Seeds (blank line when done)"
while true {
	prompt "Seed" seed
	if {[string length $seed] == 0} {
		break
	}

	if {[catch {
		# Verify the seed
		::nano::internal::generateKey [binary decode hex $seed] 0

		lappend seeds $seed
	} err]} {
		puts "Error: $err"
	}
}
set penultimateDestinationKey [::nano::internal::generateKey [binary decode hex [lindex $seeds 0]] 0]
set penultimateDestination    [::nano::address::fromPrivateKey $penultimateDestinationKey]

puts ">> Output File"
while true {
	prompt "Filename" outputFileName
	set outputFileName [file normalize $outputFileName]
	if {[catch {
		set outputFD [open $outputFileName w]
	} err]} {
		puts "Error: $err"

		continue
	}

	break
}

puts ">> Database File (Frontiers and Pending)"
while true {
	prompt "Filename" inputDBFileName
	if {[catch {
		set inputDBFD [open $inputDBFileName rb]
		zlib push gunzip $inputDBFD
	} err]} {
		puts "Error: $err"

		continue
	}

	break
}

puts ""
puts ">> Summary"
puts ">>> Destinations"
puts ">>>> Penultimate Destination: $penultimateDestination"
puts ">>>> Ultimate Destination:    $ultimateDestination"
puts ">>> Derived keys:             $numAccountsPerSeed * [llength $seeds]"
puts ">>> Output File:              $outputFileName"
puts ">>> Database File:            $inputDBFileName"
puts ""

puts ">> Loading Database File"
apply {{inputDBFD} {
	while true {
		gets $inputDBFD line
		if {[eof $inputDBFD] && $line eq ""} {
			break
		}

		set type [lindex $line 0]
		set args [lrange $line 1 end]

		switch -exact -- $type {
			"pending" - "::nano::account::addPending" {
				::nano::account::addPending {*}$args
			}
			"frontier" - "::nano::account::setFrontier" {
				::nano::account::setFrontier {*}$args
			}
		}
	}

	close $inputDBFD
}} $inputDBFD

set keys [list]
puts stderr ">> Generating [expr {[llength $seeds] * $numAccountsPerSeed}] keys"
foreach seed $seeds {
	set seed [binary decode hex $seed]
	for {set index 0} {$index < $numAccountsPerSeed} {incr index} {
		set key [::nano::internal::generateKey $seed $index]

		set accountPubKeys($key) [::nano::key::publicKeyFromPrivateKey $key -hex]
		lappend keys $key
	}
}

puts stderr ">> Working"
set resultingBlocks [list]
set iteration 0
puts -nonewline $outputFD "\["
set firstBlock true
while true {
	incr iteration

	set resultingBlocksCountBefore [llength $resultingBlocks]

	set index -1
	foreach key $keys {
		incr index

		set account [::nano::address::fromPrivateKey $key]

		if {($index % 100) == 0} {
			puts stderr ">>> $iteration/1/$index Processing $account"
		}

		foreach blockJSON [::nano::account::receiveAllPending $key $accountPubKeys($key)] {
			lappend resultingBlocks $blockJSON
			if {!$firstBlock} {
				puts -nonewline $outputFD ","
			}
			puts -nonewline $outputFD "$blockJSON"
			set firstBlock false
		}
	}

	set index -1
	foreach key $keys {
		incr index

		set account [::nano::address::fromPrivateKey $key]

		if {($index % 100) == 0} {
			puts ">>> $iteration/2/$index Processing $account"
		}

		set balance [::nano::account::getFrontier $account balance]
		if {$balance == 0} {
			continue
		}

		if {$account in [list $penultimateDestination $ultimateDestination]} {
			continue
		}

		set blockJSON [::nano::account::send $account $penultimateDestination $balance $key]
		if {!$firstBlock} {
			puts -nonewline $outputFD ","
		}
		puts -nonewline $outputFD "$blockJSON"
		set firstBlock false
	}

	set resultingBlocksCountAfter [llength $resultingBlocks]
	if {$resultingBlocksCountBefore == $resultingBlocksCountAfter} {
		break
	}
}

if {[::nano::address::toPublicKey $penultimateDestination] ne [::nano::address::toPublicKey $ultimateDestination]} {
	set balance [::nano::account::getFrontier $penultimateDestination balance]
	set blockJSON [::nano::account::send $penultimateDestination $ultimateDestination $balance $penultimateDestinationKey]
	if {!$firstBlock} {
		puts -nonewline $outputFD ","
	}
	puts -nonewline $outputFD "$blockJSON"
}

puts $outputFD "\]"

puts ">> Done !"