Skip to content

Commit

Permalink
initital commit
Browse files Browse the repository at this point in the history
  • Loading branch information
pipermerriam committed Jul 8, 2016
0 parents commit d11ee21
Show file tree
Hide file tree
Showing 45 changed files with 10,740 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
public/
*.pyc
*.sqlite
.env
bower_components/
node_modules/
.cache/
tmp/
*.log
build/
Binary file added db.sqlite3
Binary file not shown.
Empty file added func_sig_registry/__init__.py
Empty file.
Empty file.
3 changes: 3 additions & 0 deletions func_sig_registry/registry/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
5 changes: 5 additions & 0 deletions func_sig_registry/registry/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class RegistryConfig(AppConfig):
name = 'registry'
40 changes: 40 additions & 0 deletions func_sig_registry/registry/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-07-06 22:20
from __future__ import unicode_literals

import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import func_sig_registry.registry.validators


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='BytesSignature',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('bytes4_signature', models.CharField(max_length=4, unique=True, validators=[django.core.validators.MinLengthValidator(4)])),
],
),
migrations.CreateModel(
name='Signature',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('text_signature', models.TextField(unique=True, validators=[func_sig_registry.registry.validators.validate_text_signature, django.core.validators.MinLengthValidator(3)])),
('bytes_signature', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='registry.BytesSignature')),
],
),
migrations.AlterUniqueTogether(
name='signature',
unique_together=set([('text_signature', 'bytes_signature')]),
),
]
Empty file.
59 changes: 59 additions & 0 deletions func_sig_registry/registry/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from django.db import models
from django.core.validators import MinLengthValidator

from .validators import (
validate_text_signature,
)
from .parsers import (
extract_function_signatures,
normalize_function_signature,
)
from .utils import (
make_4byte_signature,
encode_hex,
force_text,
force_bytes,
)


class Signature(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

text_signature = models.TextField(unique=True,
validators=[
validate_text_signature,
MinLengthValidator(3),
])
bytes_signature = models.ForeignKey('registry.BytesSignature')

class Meta:
unique_together = (
('text_signature', 'bytes_signature'),
)

def save(self, *args, **kwargs):
if self.bytes_signature_id is None:
self.bytes_signature, _ = BytesSignature.objects.get_or_create(
bytes4_signature=force_text(make_4byte_signature(self.text_signature)),
)
super(Signature, self).save()

@classmethod
def import_from_solidity_source(cls, file_obj):
source_code = force_text(file_obj.read())
raw_function_signatures = extract_function_signatures(source_code)
function_signatures = (
normalize_function_signature(sig) for sig in raw_function_signatures
)
for text_signature in function_signatures:
Signature.objects.get_or_create(text_signature=text_signature)


class BytesSignature(models.Model):
bytes4_signature = models.CharField(max_length=4,
unique=True,
validators=[MinLengthValidator(4)])

def get_hex_display(self):
return force_text(encode_hex(force_bytes(self.bytes4_signature)))
66 changes: 66 additions & 0 deletions func_sig_registry/registry/parsers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import itertools
import re


DYNAMIC_TYPES = ['bytes', 'string']

STATIC_TYPE_ALIASES = ['uint', 'int', 'byte']
STATIC_TYPES = list(itertools.chain(
['address', 'bool'],
['uint{0}'.format(i) for i in range(8, 257, 8)],
['int{0}'.format(i) for i in range(8, 257, 8)],
['bytes{0}'.format(i) for i in range(1, 33)],
))

TYPE_REGEX = '|'.join(itertools.chain(
DYNAMIC_TYPES,
STATIC_TYPES,
STATIC_TYPE_ALIASES,
))

NAME_REGEX = '[a-zA-Z_][a-zA-Z0-9_]*'

ARGUMENT_REGEX = '(?:{type})(?:(?:\[[0-9]*\])*)?\s+[a-zA-Z_][a-zA-Z0-9_]*'.format(
type=TYPE_REGEX,
)


FUNCTION_REGEX = (
'function\s+[a-zA-Z_][a-zA-Z0-9_]*\s*\(\s*(?:{arg}(?:(?:\s*,\s*{arg}\s*)*)?)?\s*\)'.format(
arg=ARGUMENT_REGEX,
)
)


def extract_function_signatures(code):
matches = re.findall(FUNCTION_REGEX, code)
return matches or []


EXTRACT_ARGUMENTS_REGEX = '(?P<type>{type})(?P<sub_type>(?:\[[0-9]*\])*)?\s+[a-zA-Z_][a-zA-Z0-9_]*'.format(
type=TYPE_REGEX,
)


def to_canonical_type(value):
if value == 'int':
return 'int256'
elif value == 'uint':
return 'uint256'
elif value == 'byte':
return 'bytes1'
else:
return value


def normalize_function_signature(raw_signature):
fn_name_match = re.match('function\s+(?P<fn_name>[a-zA-Z_][a-zA-Z0-9_]*)', raw_signature)
group_dict = fn_name_match.groupdict()
fn_name = group_dict['fn_name']
raw_arguments = re.findall(EXTRACT_ARGUMENTS_REGEX, raw_signature)

arguments = [
"".join((to_canonical_type(t), sub))
for t, sub in raw_arguments
]
return "{fn_name}({fn_args})".format(fn_name=fn_name, fn_args=','.join(arguments))
31 changes: 31 additions & 0 deletions func_sig_registry/registry/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from rest_framework import serializers

from .models import Signature

from .utils import (
clean_text_signature,
)


class SignatureSearchSerializer(serializers.Serializer):
bytes4_signature = serializers.CharField()


class SignatureSerializer(serializers.ModelSerializer):
bytes4_signature = serializers.CharField(read_only=True,
source='bytes_signature.get_hex_display')

class Meta:
model = Signature
fields = ('id', 'text_signature', 'bytes4_signature')
read_only_fields = ('bytes4_signature',)

def validate_signature(self, value):
return clean_text_signature(value)


class SolidityImportSerializer(serializers.Serializer):
source_file = serializers.FileField()

def create(self, validated_data):
return validated_data['source_file']
23 changes: 23 additions & 0 deletions func_sig_registry/registry/tables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import django_tables2 as tables

from .models import (
Signature,
)


class SignatureTable(tables.Table):
bytes_signature = tables.TemplateColumn(
'<code>{{ record.bytes_signature.get_hex_display }}</code>',
)

class Meta:
model = Signature
fields = (
'id',
'text_signature',
'bytes_signature',
)
template = 'partials/table.html'
attrs = {
'class': 'table table-striped table-bordered',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% extends 'layout_full.html' %}
{% load rest_framework %}


{% block content %}
<div class="row">
<div class="col-md-12">
<form class="form-inline" action="{% url 'signature-create' %}" method="post" novalidate>
{% csrf_token %}
{% render_form serializer template_pack='rest_framework/inline' %}
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
{% endblock content %}
17 changes: 17 additions & 0 deletions func_sig_registry/registry/templates/registry/signature_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{% extends 'layout_full.html' %}
{% load render_table from django_tables2 %}
{% load rest_framework %}


{% block content %}
<div class="row">
<div class="col-md-12">
<form class="form-inline" action="{% url 'site-index' %}" method="get" novalidate>
<label for="bytes4_signature">Search Signatures</label>
{% render_form serializer template_pack='rest_framework/inline' %}
<button type="submit" class="btn btn-default">Search</button>
</form>
{% render_table table %}
</div>
</div>
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% extends 'layout_full.html' %}
{% load rest_framework %}


{% block content %}
<div class="row">
<div class="col-md-12">
<form class="form-inline" action="{% url 'import-solidity' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{% render_form serializer template_pack='rest_framework/inline' %}
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
{% endblock content %}
3 changes: 3 additions & 0 deletions func_sig_registry/registry/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
47 changes: 47 additions & 0 deletions func_sig_registry/registry/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import codecs

from sha3 import sha3_256


def force_bytes(value):
if isinstance(value, bytes):
return value
elif isinstance(value, str):
return bytes(value, 'latin1')
else:
raise TypeError("Unsupported type: {0}".format(type(value)))


def force_text(value):
if isinstance(value, str):
return value
elif isinstance(value, bytes):
return str(value, 'latin1')
else:
raise TypeError("Unsupported type: {0}".format(type(value)))


def remove_0x_prefix(value):
if force_bytes(value).startswith(b'0x'):
return value[2:]
return value


def encode_hex(value):
return b'0x' + codecs.encode(force_bytes(value), 'hex')


def decode_hex(value):
return codecs.decode(remove_0x_prefix(force_bytes(value)), 'hex')


def clean_text_signature(value):
return value.strip().replace(' ', '').replace('\n', '').replace('\t', '')


# sanity check we are using the right sha3 function
assert sha3_256(b'').hexdigest() == 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', sha3_256(b'').hexdigest()


def make_4byte_signature(text_signature):
return sha3_256(force_bytes(text_signature)).digest()[:4]
6 changes: 6 additions & 0 deletions func_sig_registry/registry/validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.core.exceptions import ValidationError


def validate_text_signature(value):
# TODO
pass

0 comments on commit d11ee21

Please sign in to comment.