diff --git a/oss_server/base/urls.py b/oss_server/base/urls.py
index e33168a..ed4b21d 100644
--- a/oss_server/base/urls.py
+++ b/oss_server/base/urls.py
@@ -1,14 +1,6 @@
from django.conf.urls import url
-from .v1.views import (CreateLicenseRawTxView,
- CreateLicenseTransferRawTxView,
- CreateMintRawTxView,
- CreateSmartContractRawTxView,
- CreateRawTxView,
- GetBalanceView,
- GetLicenseInfoView,
- GetRawTxView,
- SendRawTxView)
+from .v1.views import *
urlpatterns = [
url('^v1/balance/(?P
[a-zA-Z0-9]+)$', GetBalanceView.as_view()),
@@ -22,5 +14,6 @@
url('^v1/smartcontract/send$', SendRawTxView.as_view()),
url('^v1/transaction/prepare$', CreateRawTxView.as_view()),
url('^v1/transaction/send$', SendRawTxView.as_view()),
+ url('^v1/exchange/prepare$', CreateExchangeRawTxView.as_view()),
url('^v1/transaction/(?P[a-z0-9]+)$', GetRawTxView.as_view()),
]
diff --git a/oss_server/base/v1/forms.py b/oss_server/base/v1/forms.py
index 39ceea6..0acf1d3 100644
--- a/oss_server/base/v1/forms.py
+++ b/oss_server/base/v1/forms.py
@@ -129,3 +129,52 @@ class CreateLicenseTransferRawTxForm(forms.Form):
'min_value': '`color_id` should be greater than or equal to %(limit_value)s',
'max_value': '`color_id` should be less than or equal to %(limit_value)s'
})
+
+
+class ExchangeRawTxForm(forms.Form):
+ fee_address = AddressField(error_messages={
+ 'required': '`fee_address` is required',
+ 'invalid': '`fee_address` is not an address'
+ })
+ address1 = AddressField(error_messages={
+ 'required': '`address1` is required',
+ 'invalid': '`address1` is not an address'
+ })
+ address2 = AddressField(error_messages={
+ 'required': '`address2` is required',
+ 'invalid': '`address2` is not an address'
+ })
+ color_id1 = ColorField(error_messages={
+ 'required': '`color_id1` is required',
+ 'invalid': '`color_id1` is invalid',
+ 'min_value': '`color_id1` should be greater than or equal to %(limit_value)s',
+ 'max_value': '`color_id1` should be less than or equal to %(limit_value)s'
+ })
+ color_id2 = ColorField(error_messages={
+ 'required': '`color_id2` is required',
+ 'invalid': '`color_id2` is invalid',
+ 'min_value': '`color_id2` should be greater than or equal to %(limit_value)s',
+ 'max_value': '`color_id2` should be less than or equal to %(limit_value)s'
+ })
+ amount1 = TxAmountField(error_messages={
+ 'required': '`amount1` is required',
+ 'invalid': '`amount1` is invalid',
+ 'min_value': '`amount1` should be greater than or equal to %(limit_value)s',
+ 'max_value': '`amount1` should be less than or equal to %(limit_value)s',
+ 'max_decimal_places': '`amount1` only allow up to %(max)s decimal digits'
+ })
+ amount2 = TxAmountField(error_messages={
+ 'required': '`amount2` is required',
+ 'invalid': '`amount2` is invalid',
+ 'min_value': '`amount2` should be greater than or equal to %(limit_value)s',
+ 'max_value': '`amount2` should be less than or equal to %(limit_value)s',
+ 'max_decimal_places': '`amount2` only allow up to %(max)s decimal digits'
+ })
+
+ def clean(self):
+ address1 = self.cleaned_data.get('address1')
+ address2 = self.cleaned_data.get('address2')
+
+ if address1 and address2:
+ if address1 == address2:
+ raise forms.ValidationError("`address1` and `address2` can't be the same")
diff --git a/oss_server/base/v1/views.py b/oss_server/base/v1/views.py
index 4eab34c..daf0469 100644
--- a/oss_server/base/v1/views.py
+++ b/oss_server/base/v1/views.py
@@ -11,9 +11,8 @@
from gcoinrpc import connect_to_remote
from gcoinrpc.exceptions import InvalidAddressOrKey, InvalidParameter
+from .forms import *
from ..utils import balance_from_utxos, select_utxo, utxo_to_txin
-from .forms import (CreateLicenseRawTxForm, CreateLicenseTransferRawTxForm,
- CreateSmartContractRawTxForm, MintRawTxForm, RawTxForm)
logger = logging.getLogger(__name__)
@@ -315,3 +314,68 @@ def _get_license_utxo(self, utxos, color):
if utxo['color'] == color:
return utxo
return None
+
+
+class CreateExchangeRawTxView(View):
+
+ def get(self, request, *args, **kwargs):
+ form = ExchangeRawTxForm(request.GET)
+
+ if not form.is_valid():
+ errors = ', '.join(reduce(lambda x, y: x + y, form.errors.values()))
+ response = {'error': errors}
+ return JsonResponse(response, status=httplib.BAD_REQUEST)
+
+ fee_address = form.cleaned_data['fee_address']
+ address1 = form.cleaned_data['address1']
+ address2 = form.cleaned_data['address2']
+ color_id1 = form.cleaned_data['color_id1']
+ color_id2 = form.cleaned_data['color_id2']
+ amount1 = form.cleaned_data['amount1']
+ amount2 = form.cleaned_data['amount2']
+
+ ins = []
+ outs = []
+ fee_included = False
+
+ for address, color_id, amount in [(address1, color_id1, amount1), (address2, color_id2, amount2)]:
+ utxos = get_rpc_connection().gettxoutaddress(address)
+
+ inputs = select_utxo(utxos, color_id, amount)
+ if not inputs:
+ return JsonResponse({'error': 'insufficient funds'}, status=httplib.BAD_REQUEST)
+
+ inputs_value = balance_from_utxos(inputs)[color_id]
+ change = inputs_value - amount
+
+ if color_id == 1 and address == fee_address:
+ inputs = select_utxo(utxos, color_id, amount + 1)
+ if not inputs:
+ return JsonResponse({'error': 'insufficient fee'}, status=httplib.BAD_REQUEST)
+ fee_included = True
+ inputs_value = balance_from_utxos(inputs)[color_id]
+ change = inputs_value - amount - 1
+
+ if change:
+ outs.append({'address': address,
+ 'value': int(change * 10**8), 'color': color_id})
+
+ ins += [utxo_to_txin(utxo) for utxo in inputs]
+
+ if not fee_included:
+ fee_address_utxos = get_rpc_connection().gettxoutaddress(fee_address)
+ inputs = select_utxo(fee_address_utxos, 1, 1)
+ if not inputs:
+ return JsonResponse({'error': 'insufficient fee in fee_address'}, status=httplib.BAD_REQUEST)
+ ins += [utxo_to_txin(utxo) for utxo in inputs]
+ inputs_value = balance_from_utxos(inputs)[1]
+ change = inputs_value - 1
+
+ if change:
+ outs.append({'address': fee_address,
+ 'value': int(change * 10**8), 'color': 1})
+
+ outs.append({'address': address1, 'value': int(amount2 * 10**8), 'color': color_id2})
+ outs.append({'address': address2, 'value': int(amount1 * 10**8), 'color': color_id1})
+ raw_tx = make_raw_tx(ins, outs)
+ return JsonResponse({'raw_tx': raw_tx})
diff --git a/oss_server/oss_server/settings/base.py b/oss_server/oss_server/settings/base.py
index 25ac048..bf66da7 100644
--- a/oss_server/oss_server/settings/base.py
+++ b/oss_server/oss_server/settings/base.py
@@ -72,7 +72,7 @@
GCOIN_RPC = {
'user': '',
- 'password': '',
+ 'password': '',
'host': '',
'port': '',
}
@@ -135,7 +135,7 @@
STATIC_URL = '/static/'
-LOG_DIR = os.path.dirname(BASE_DIR) + '/log/'
+LOG_DIR = os.path.dirname(BASE_DIR) + '/log/'
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
diff --git a/requirements.txt b/requirements.txt
index f14e024..8c118c6 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,5 +4,5 @@ mock==1.0.1
MySQL-python==1.2.5
tornado==4.4.1
-git+ssh://git@github.com/OpenNetworking/gcoin-rpc.git@develop#egg=gcoin-python
+git+ssh://git@github.com/OpenNetworking/gcoin-rpc.git@develop
git+ssh://git@github.com/OpenNetworking/pygcointools@master