Skip to content
This repository was archived by the owner on May 24, 2022. It is now read-only.

Commit 5632f87

Browse files
committed
Merge branch 'release/0.0.1'
2 parents e1dd6e5 + beff182 commit 5632f87

File tree

8 files changed

+986
-0
lines changed

8 files changed

+986
-0
lines changed

.gitignore

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# macOS
2+
.DS_Store
3+
4+
# vim
5+
.swp
6+
*~
7+
*.swp
8+
*.swo
9+
10+
# Binaries for programs and plugins
11+
*.exe
12+
*.exe~
13+
*.dll
14+
*.so
15+
*.h
16+
*.dylib
17+
bin/
18+
build/
19+
20+
# Test binary, build with `go test -c`
21+
*.test
22+
23+
# Output of the go coverage tool, specifically when used with LiteIDE
24+
*.out
25+
26+
# misc
27+
notes.txt
28+
29+
.data/
30+
31+
.testfiles/
32+
33+
*.pem
34+
35+
*.tar
36+
37+
node/test_data/go_example_image.tar
38+
39+
coverage.out
40+
41+
__pycache__

GNU-AGPL-3.0.txt

Lines changed: 661 additions & 0 deletions
Large diffs are not rendered by default.

LICENSE

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
c3-sdk-python is an sdk for developing c3 apps in python.
2+
Copyright (C) 2018 C3 Labs
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU Affero General Public License as
6+
published by the Free Software Foundation, either version 3 of the
7+
License, or (at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU Affero General Public License for more details.
13+
14+
You should have received a copy of the GNU Affero General Public License
15+
along with this program. If not, see <http://www.gnu.org/licenses/>.

Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
all: deps
2+
3+
.PHONY: deps
4+
deps: clean
5+
@go get github.com/c3systems/c3-go && \
6+
(cd "${GOPATH}/src/github.com/c3systems/c3-go/lib/c" && \
7+
make) && \
8+
cp "${GOPATH}/src/github.com/c3systems/c3-go/lib/c/common/hashing/hashing.so" ./lib/hashing && \
9+
cp "${GOPATH}/src/github.com/c3systems/c3-go/lib/c/common/hexutil/hexutil.so" ./lib/hexutil && \
10+
cp "${GOPATH}/src/github.com/c3systems/c3-go/lib/c/common/stringutil/stringutil.so" ./lib/stringutil && \
11+
cp "${GOPATH}/src/github.com/c3systems/c3-go/lib/c/config/config.so" ./lib/config
12+
13+
clean:
14+
@-find . -type f -name *.so -delete

README.md

Whitespace-only changes.

lib/state.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"foo": "bar"
3+
}

sdk.py

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
from ctypes import *
2+
from queue import Queue
3+
from threading import Thread
4+
import socket
5+
import json
6+
7+
# note: these files must first be built. see the make file
8+
hashing = CDLL('./lib/hashing/hashing.so')
9+
hexutil = CDLL('./lib/hexutil/hexutil.so')
10+
config = CDLL('./lib/config/config.so')
11+
stringutil = CDLL('./lib/stringutil/stringutil.so')
12+
13+
class BytesResponse(Structure):
14+
_fields_ = [
15+
("r0", c_void_p),
16+
("r1", c_int),
17+
]
18+
19+
stringutil.CompactJSON.restype = BytesResponse
20+
hexutil.DecodeString.restype = BytesResponse
21+
22+
ErrMethodAlreadyRegistered = Exception("method already registered")
23+
ErrMethodNotExists = Exception("method does not exist")
24+
ErrIncorrectNumberOfArgs = Exception("method requires two arguments")
25+
26+
class C3():
27+
def __init__(self, statefile):
28+
self.methods = {}
29+
self.state = {}
30+
self.q = Queue(maxsize=0)
31+
self.statefile = statefile
32+
33+
def registerMethod(self, methodName, ifn):
34+
b = bytearray()
35+
b.extend(map(ord, methodName))
36+
arr = (c_byte * len(b))(*b)
37+
38+
methodNameHash = c_char_p(hashing.HashToHexString(arr, len(arr))).value.decode('utf-8')
39+
if methodNameHash in self.methods:
40+
raise ErrMethodAlreadyExists
41+
42+
def newMethod(*args):
43+
if len(args) != 2:
44+
raise ErrIncorrectNumberOfArgs
45+
46+
key = args[0]
47+
res = hexutil.DecodeString(c_char_p(key.encode('utf-8')))
48+
ArrayType = c_ubyte*(c_int(res.r1).value)
49+
pa = cast(c_void_p(res.r0), POINTER(ArrayType))
50+
key = "".join(map(chr, pa.contents[:]))
51+
52+
val = args[1]
53+
res = hexutil.DecodeString(c_char_p(val.encode('utf-8')))
54+
ArrayType = c_ubyte*(c_int(res.r1).value)
55+
pa = cast(c_void_p(res.r0), POINTER(ArrayType))
56+
val = "".join(map(chr, pa.contents[:]))
57+
58+
try:
59+
res = ifn(key, val)
60+
print("[c3] result", res)
61+
except Exception as inst:
62+
print("[c3] invokation failed", inst)
63+
64+
self.methods[methodNameHash] = newMethod
65+
66+
def setInitialState(self):
67+
currState = ""
68+
69+
file = open(self.statefile, "r")
70+
currState = file.read()
71+
72+
if len(currState) == 0:
73+
print("no current state")
74+
return
75+
76+
b = bytearray()
77+
b.extend(map(ord, currState))
78+
arr = (c_byte * len(b))(*b)
79+
80+
res = stringutil.CompactJSON(arr, len(arr))
81+
ArrayType = c_ubyte*(c_int(res.r1).value)
82+
pa = cast(c_void_p(res.r0), POINTER(ArrayType))
83+
84+
self.state = json.loads("".join(map(chr, pa.contents[:])))
85+
print("initial state loaded")
86+
87+
def process(self, payloadBytes):
88+
payload = json.loads(payloadBytes)
89+
90+
if len(payload) <= 1:
91+
return
92+
93+
# ifc format is [a, b, c]
94+
if isinstance(payload[0], str):
95+
self.invoke(payload[0], payload[1:])
96+
97+
# ifc format is [[a, b, c], [a, b, c]]
98+
for ifc in payload:
99+
self.invoke(ifc[0], *ifc[1:])
100+
101+
def invoke(self, methodName, *params):
102+
b = bytearray()
103+
b.extend(map(ord, methodName))
104+
arr = (c_byte * len(b))(*b)
105+
106+
methodNameHash = c_char_p(hashing.HashToHexString(arr, len(arr))).value.decode('utf-8')
107+
if methodNameHash not in self.methods:
108+
raise ErrMethodNotExists
109+
110+
fn = self.methods[methodNameHash]
111+
try:
112+
fn(*params)
113+
except Exception as inst:
114+
print("[c3] err invoking method", inst)
115+
return
116+
117+
def listen(self):
118+
while True:
119+
self.process(self.q.get())
120+
q.task_done()
121+
122+
def serve():
123+
host = c_char_p(config.ServerHost()).value
124+
port = c_int(config.ServerPort()).value
125+
126+
server = Server(host, port, self.q)
127+
128+
worker = Thread(target=server.run)
129+
# worker.setDaemon(True)
130+
worker.start()
131+
132+
def NewC3(stateFilePath = c_char_p(config.TempContainerStateFilePath()).value.decode('utf-8')):
133+
c3 = C3(stateFilePath)
134+
135+
c3.setInitialState()
136+
137+
worker = Thread(target=c3.listen)
138+
# worker.setDaemon(True)
139+
worker.start()
140+
141+
return c3
142+
143+
class Server():
144+
def __init__(self, host, port, q):
145+
self.host = host
146+
self.port = port
147+
self.q = q
148+
149+
def run():
150+
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
151+
server.bind((self.host, self.port))
152+
server.listen(1) # max backlog of connections
153+
154+
print("Listening on {0}:{1}".format(bind_ip, bind_port))
155+
156+
157+
def handle_conn(conn):
158+
data = []
159+
while 1:
160+
tmp = conn.recv(1024)
161+
if not tmp: break
162+
data.append(tmp)
163+
164+
self.q.put(''.join(data))
165+
conn.close()
166+
167+
while True:
168+
client_sock, address = server.accept()
169+
print('Accepted connection from {0}:{1}'.format(address[0], address[1]))
170+
client_handler = threading.Thread(
171+
target=handle_conn,
172+
args=(client_sock,) # note: comment required!
173+
)
174+
175+
client_handler.start()

sdk_test.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from ctypes import *
2+
import unittest
3+
import sdk
4+
import json
5+
6+
hexutil = CDLL('./lib/hexutil/hexutil.so')
7+
8+
c3 = sdk.NewC3(stateFilePath = './lib/state.json')
9+
10+
class TestSDK(unittest.TestCase):
11+
def test_registerAndInvokeMethod(self):
12+
key = ""
13+
val = ""
14+
expectKey = "expectKey"
15+
expectVal = "expectVal"
16+
methodName = "foo"
17+
18+
inputKey = c_char_p(hexutil.EncodeString(c_char_p(expectKey.encode('utf-8')))).value.decode('utf-8')
19+
inputVal = c_char_p(hexutil.EncodeString(c_char_p(expectVal.encode('utf-8')))).value.decode('utf-8')
20+
21+
def setStuff(k, v):
22+
nonlocal key
23+
key = k
24+
25+
nonlocal val
26+
val = v
27+
28+
c3.registerMethod(methodName, setStuff)
29+
c3.invoke(methodName, inputKey, inputVal)
30+
31+
self.assertEqual(expectKey, key)
32+
self.assertEqual(expectVal, val)
33+
34+
def test_store(self):
35+
key = "foo"
36+
val = "bar"
37+
38+
c3.state[key] = val
39+
40+
self.assertEqual(c3.state[key], val)
41+
del c3.state[key]
42+
43+
def test_state(self):
44+
methodName = "setState"
45+
46+
key1 = "foo"
47+
val1 = "bar"
48+
49+
key2 = "foofoo"
50+
val2 = "barbar"
51+
52+
def setState(k, v):
53+
c3.state[k] = v
54+
55+
c3.registerMethod(methodName, setState)
56+
57+
p1 = [
58+
methodName,
59+
c_char_p(hexutil.EncodeString(c_char_p(key1.encode('utf-8')))).value.decode('utf-8'),
60+
c_char_p(hexutil.EncodeString(c_char_p(val1.encode('utf-8')))).value.decode('utf-8'),
61+
]
62+
p2 = [
63+
methodName,
64+
c_char_p(hexutil.EncodeString(c_char_p(key2.encode('utf-8')))).value.decode('utf-8'),
65+
c_char_p(hexutil.EncodeString(c_char_p(val2.encode('utf-8')))).value.decode('utf-8'),
66+
]
67+
68+
params = [p1, p2]
69+
paramsJSON = json.dumps(params)
70+
71+
c3.process(paramsJSON)
72+
73+
self.assertEqual(c3.state[key1], val1)
74+
self.assertEqual(c3.state[key2], val2)
75+
76+
if __name__ == '__main__':
77+
unittest.main()

0 commit comments

Comments
 (0)