CH

October 7, 2016

Least-Effort Signups in Django

Filed under: django, magic, python, software development — Benjamin Vulpes @ 12:57 a.m.

It's wwwtronix hell weekmonthyeareternity at Cascadian Hacker!

Alonso complains:

No automatic login1 :(

So, have some Django-flavored Python that'll create, save and log your new users in2 in one swell foop:

class SignupForm(UserCreationForm):

    def save(self, commit=True):
        u = super(UserCreationForm, self).save(commit=False)
        u.is_active = True
        u.save()
        return u

class Signup(CreateView):
    model = User
    template_name = 'SOME_TEMPLATE'
    form_class = SignupForm
    success_url = "/"

    def form_valid(self, form):
        u = form.save()
        login(self.request, u)
        return HttpResponseRedirect("/")

There. That wasn't so hard now, was it?

  1. For context: the Django web framework provides some pre-baked forms and validators to handle new user signups. However, none of them go so far as to set the session cookie and tell the framework to "log the user in". That is what Alonso is complaining about. []
  2. The "activate my account" meme is just another step in your funnel in which potential customers will (at some statistical rate) fall out and hit the floor. You didn't need it, you're welcome. []

October 5, 2016

[ANN] Mimisbrunnr (v1): a block slicer

Filed under: mimisbrunnr, tmsr — Benjamin Vulpes @ 12:17 a.m.

I am proud to put at The Republic's disposal a www-tronic block slicer: please direct your attention to Mimisbrunnr's home to see it in action. The `lynx` browser chokes particularly spectacularly on HTML tables, and I recommend not using it for the human-readable pages Mimisbrunnr outputs.

I call this version of Mimisbrunnr a "slicer" and not an "explorer", as a) there aren't many clickable links and b) the underlying systems provide absolutely zero aggregation facilities, instead cutting blocks apart on demand and surfacing the relevant data in human-readable format.

Mimisbrunnr currently makes only cursory attempts to compute destination addresses. I simply do not support stealth addresses, multisig addresses, or any other power ranger horseshit at this time.

Mimisbrunnr will also serve machine-readable data: simply use urls like "http://mimisbrunnr.cascadianhacker.com/blocks?height=170&raw=true". Data is serialized with s-expressions, not JSON. The current format is shaped like:

((:HEIGHT . 170) (:TOTAL-SATOSHIS . 10000000000) (:VERSION . 1)
 (:PREVIOUS-BLOCK-HASH
  . "00002A22CFEE1F2C846ADBD12B3E183D4F97683F85DAD08A79780A84BD55")
 (:MERKLE-ROOT
  . "7DAC2C5666815C17A3B36427DE37BB9D2E2C5CCEC3F8633EB91A4205CB4C10FF")
 (:TIME-STAMP . 1231731025) (:TARGET . 486604799) (:NONCE . 1889418792)
 (:TRANSACTIONS
  (
(:VERSION 1 :INPUTS
 (
((:INPUT-HASH . "000000000000000000000000000000000000000000000000000000000000")
 (:INDEX . 4294967295) (:INPUT-SCRIPT (4 255 255 0 29 1 2))
 (:SEQUENCE-NUMBER . 4294967295)) )
 :OUTPUTS
 (
((:SATOSHIS . 5000000000)
 (:OUTPUT-SCRIPT
  (65 4 212 108 73 104 189 224 40 153 210 170 9 99 54 124 122 108 227 78 236 51
   43 50 228 46 95 52 7 224 82 214 74 198 37 218 111 7 24 231 179 2 20 4 52 189
   114 87 6 149 124 9 45 181 56 5 184 33 168 91 35 167 172 97 114 91 172))
 (:DESTINATION-ADDR . "1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc")) )
 :LOCK-TIME 0)

(:VERSION 1 :INPUTS
 (
((:INPUT-HASH
  . "437CD7F8525CEED2324359C2D0BA26006D92D856A9C20FA0241106EE5A597C9")
 (:INDEX . 0)
 (:INPUT-SCRIPT
  (71 48 68 2 32 78 69 225 105 50 184 175 81 73 97 161 211 161 162 95 223 63 79
   119 50 233 214 36 198 198 21 72 171 95 184 205 65 2 32 24 21 34 236 142 202
   7 222 72 96 164 172 221 18 144 157 131 28 197 108 187 172 70 34 8 34 33 168
   118 141 29 9 1))
 (:SEQUENCE-NUMBER . 4294967295)) )
 :OUTPUTS
 (
((:SATOSHIS . 1000000000)
 (:OUTPUT-SCRIPT
  (65 4 174 26 98 254 9 197 245 27 19 144 95 7 240 107 153 162 247 21 155 34 37
   243 116 205 55 141 113 48 47 162 132 20 231 170 179 115 151 245 84 167 223
   95 20 44 33 193 183 48 59 138 6 38 241 186 222 213 199 42 112 79 126 108 216
   76 172))
 (:DESTINATION-ADDR . "1Q2TWHE3GMdB6BZKafqwxXtWAWgFt5Jvm3"))

((:SATOSHIS . 4000000000)
 (:OUTPUT-SCRIPT
  (65 4 17 219 147 225 220 219 138 1 107 73 132 15 140 83 188 30 182 138 56 46
   151 177 72 46 202 215 177 72 166 144 154 92 178 224 234 221 251 132 204 249
   116 68 100 248 46 22 11 250 155 139 100 249 212 192 63 153 155 134 67 246 86
   180 18 163 172))
 (:DESTINATION-ADDR . "12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S")) )
 :LOCK-TIME 0) )))

This is a Common Lisp "alist". Once I finish writing the block-to-relational-db ingestor-cum-persistence-layer, I will update this data format so that the data structure currently emitted as a list of transactions will appear as an ordered alist keyed on transaction hashes. Please leave a comment here if you take a dependency on the current format and would like a notification before I rip the rug out from underneath you.

Even though I prefer delivery to promises, I commit to upgrading the underlying storage such that:

  • One can look transactions up by hash
  • One can look blocks up by hash
  • One can find input addresses paying to output addresses

Rewriting the storage layer, however, is not at the top of my list. You'll know what was once I ship it.

October 3, 2016

Base58 Encoding in Common Lisp

Filed under: bitcoin, common lisp — Benjamin Vulpes @ 6:02 p.m.

I complained, and was roundly caned.

For my sins, here's a Base58 encoder in CL. Specific CL idioms cribbed and condensed from Fernando Boretti (a nonperson in the WOT, as far as I can tell); insights from Andresen of all folks (having written the source for Boretti's port); and of course, the original codebase.

(defparameter +b58-alphabet+ "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")

(defun b58-encode (bytes)
  (let ((string (make-string-output-stream))
        (bignum (loop for i to (- (length bytes) 1) summing
               (* (elt bytes i)
                  (expt 256 i)))))

   (loop while (> bignum 0) do
        (multiple-value-bind (x r) (floor bignum (length +b58-alphabet+))
          (write-char (elt +b58-alphabet+ r) string)
          (setf bignum x)))

   (loop for b across (reverse bytes) do
        (if (= 0 b)
            (write-char (elt +b58-alphabet+ 0) string)
            (return)))

   (reverse (get-output-stream-string string))))

The above is not a "fork for glory", but a from-scratch reimplementation a) for my sins and b) because I'm not pulling a library in from Quicklisp from someone who's not even in the wot just for binary arithmetic without reading and understanding the problem in depth (and by the time I understood the encoding process, I'd also written my own entirely adequate implementation, and so here we are). I also discovered in this process that Bitcoin stores both public keys and public key hashes in scripts in big-endian format. Another entry for the list of Bitcoin endianness mistakes I've made...

« Newer Posts

---