Closed vans163 closed 6 years ago
Yes, Amoveo has a lot of progress to smart contract technology. We have the Chalang VM, with a couple of compilers. And we have several smart contracts written for it.
Any tutorial how to use it / write one?
https://github.com/zack-bitcoin/chalang
Here is the repository for the VM and some compilers.
It has sample code for the different compilers.
If you have some project in mind, you should ask me for advice first. Contracts have to be formatted as derivatives.
Yea but I mean after compile a contract to make it live on the chain? A basic hello world.
Amoveo contracts usually do not get written to the chain. They are in channels.
So amoveo contracts are protocols.
You could take a look at lightning payments protocol as a basic example. You can run it from the light wallet, and see some javascript version of part of the protocol. You can read there definitions for the entire lightning if you start with the ext_handler.erl file and the api.erl file.
Jeez reading channel_feeder.erl makes me realize why I switched to elixir a year ago :P. That whole thing can be written in 1/5 the lines of code plus much more legible.
Ex:
Acc1 = SPK#spk.acc1,
Acc2 = SPK#spk.acc2,
From = case MyID of
Acc1 -> Acc2;
Acc2 -> Acc1;
X -> X = Acc1
end,
cond do
_MyID == spk(_SPK, :acc1) -> spk(_SPK, :acc2)
_MyID == spk(_SPK, :acc2) -> spk(_SPK, :acc1)
true -> _MyID = spk(_SPK, :acc1)
end
But records suck in general, in both Erlang and Elixir are pretty unreadable. Why not use maps? Also maps work much better over decentralized/distributed Erlang when nodes are out of date. Otherwise an out of date node needs to fetch new record defines and completely breaks without them. Not to mention DB schema (ets/mnesia) and having to transform everytime you change a record def, PITA.
Using maps:
cond do
_MyID == _SPK.acc1 -> _SPK.acc2
_MyID == _SPK.acc2 -> _SPK.acc1
true -> _MyID = _SPK.acc1
end
And you keep all your guarantees.
I guess the major difference is brain cache size, a brain looking at _SPK.acc1 knows immediately its key 'acc1' from _SPK map, but Acc1 by itself, brain needs to xref were Acc1 is set Acc1 = SPK#spk.acc1. So brain does 1 read, 1 xref (read), 1 brain cache store, 1 read again (now know what Acc1 is), 1 comprehend, while with the elixir code it just does 1 read operation + 1 comprehend. 60% less operations plus avoid expensive cache store operation which for people that don't have brain like Nikolai Tesla (photographic) find it hard to remember things after 1 view of them.
true = CD0#cd.live,
SPKME = CD0#cd.me,
SSME = CD0#cd.ssme,
true = testnet_sign:verify(keys:sign(ThemSPK)),
true = CD#cd.live,
NewSPK = testnet_sign:data(ThemSPK),
NewSPK = CD#cd.me,
SS = CD#cd.ssme,
SS4 = CD#cd.ssthem,%is this wrong?
B2 = spk:force_update(SPKME, SSME, SS4),
CID = CD#cd.cid,
This is how code the compiler generates looks like after unwinding :P.
Anyways just making a point its harder for outside contributors when reading code and following logic flow takes 5 hours vs 1 hour.
Aww why write contracts in Forth omg. Like 5 ppl are fluent in that lang. Your locking up the ecosystem to people that know Forth or Lisp or are able to write a transcompiler/compiler target.
Anyways again back to business, I went through the code abit and here
new_lightning(Amount) ->
%delay for canceling is 100
S = crypto:strong_rand_bytes(constants:hash_size()),
SH = hash:doit(S),
PrivDir = code:priv_dir(amoveo_core),
{ok, Contract} = file:read_file(PrivDir ++ "/lightning.fs"),
ESH = " binary 32 " ++ binary_to_list(base64:encode(SH)) ++ " >r " ++ Contract,
ESS = " binary 32 " ++ binary_to_list(base64:encode(S)),
Code = compiler_chalang:doit(list_to_binary(ESH)),
SS = spk:new_ss(compiler_chalang:doit(list_to_binary(ESS)), []),
add(Code, SS, Amount),
{Code, SS}.
It seems like your just compiling a contract then sending it to the other party via talker
{ok, SSPK2} = talker:talk({locked_payment, SSPK, Amount, Fee, Code, keys:pubkey(), Pubkey, ESS}, IP, Port),
The thing I am confused about if its in channel is what if the other party lies about its result. So your execution result != other party, does the execution get wound back / dropped as if nothing happened?
Records are faster, and since they have an order, I can convert then into keyless Json objects more easily. I wanted keyless objects for the Api so that we wouldn't waste bandwidth. We could have used maps too. Not a big difference really. With blockchains updating is a much bigger pain than dealing with records. You have to know if your update is hard or soft, and you need to get the support of the biggest mining pools. With all that going on, using records doesn't make it harder to update.
Elixir is fine. They look almost the same to me. I switched from elixir to erlang a couple years ago because erlang documentation was better at that time.
Bitcoin vm is forth. ethereum vm is forth. Forth type virtual machines are good for smart contracts. We can build whatever compilers on top for languages people prefer. The lisp I wrote has a full macro system. So you can define any language on top of that lisp just by making macros.
Keep in mind that the smart contract code is the easy part. The hard part is the handshake to negotiate forming and updating the channel smart contract.
Both parties calculate the smart con tract independently. Then they check if their results match. They only sign if they are in agreement.
Amoveo is currently not using ets or mnesia. For consensus state we are using the Merkel tree library I wrote. It writes chunks of bytes into files. For everything else, the memory requirements are so small, I just convert binary-to-term and write it directly to a file.
Amoveo light nodes come with the smart contract VM in Javascript, as well as the byte code for lightning payments, and market smart contracts.
IIRC there was a benchmark on R19 or R20 that showed neglible performance difference between tuples (records), small maps under 10-100 elements (dont recall, there is a threshold after which maps are unsorted, otherwise they are sorted), proc dict, dict and ets, basically all were pretty much negligible in speed except dict which was 2-3x slower.
The bandwidth I think is negligible, either you do custom serialization at bit level or forgoe it completely and just apply a gzip or lz4 on packets larger than a certain size.
Heh yea, I used Elixir at first then thought it was poorly documented, esp when you had to venture into erlang side for things. Switched to Erlang for years, then had a good friend pull me into a project and forced me to use Elixir, I realized how a subset of Elixir (I hate elixir Macros, protocols never use em due to how they mess with the compiler) solved most of my issues at the time with erlangs verbosity and general unreadability of produced code.
Now the ecosystem/documentation is at the point where im googling erlang problems by prefixing elixir just to get more search results.
"Bitcoin vm is forth. ethereum vm is forth. Forth type virtual machines are good for smart contracts."
This is interesting I was not aware, I thought eth is solc compiler; a bunch of weird stuff they rolled on their own for some reason.
"We can build whatever compilers on top for languages people prefer."
Why not target WASM in this case, imo thats the compiler of the future and its well maintained and growing.
"Both parties calculate the smart con tract independently. Then they check if their results match. They only sign if they are in agreement."
This part is so simple but I keep thinking there can be issues here. Like you cannot have a contract that lives on the blockchain can you? Example a queue: on eth you can easily write a queue on the blockchain, with state channel contracts, how do you implement a queue, between any parties, where anyone can push or pop to it? Basically if its contracts between 2 parties offchain, those 2 parties can conspire to lie for example and instead of poping 1 item from the queue like the contract allows, they pop the full queue.
We are using Json syntax for easy javascript. Requiring javascript developers to both use keys and to maintain alphabetical order of keys seems complicated.
Erlang vs elixir doesn't really matter, either can be readable. but it would take at least a week to rewrite everything to elixir.
It seems like you are confused about the difference between compilers and virtual machines and languages. Solidity is not a VM. It is a language. For every VM it is possible to write a Solidity compiler so that Solidity code can run on that VM.
WASM is great for running arbitrary computation as fast as possible, but you need a wasm optimized vm for it to be useful.
Our VM is optimized for other things. Minimizing bandwidth. Merkelized abstract syntax tree compatibility. Writing smart contracts where it is easy to verify that they are secure, since our VM uses functions instead of gotos from WASM.
Amoveo does not have on-chain smart contracts at all. You can not store arbitrary state on-chain. Contracts are agreements made between pairs of people. The agreement is usually never written on the blockchain.
If the 2 participants of a contract can agree together, then they can make arbitrary updates to their contract, even rewriting it entirely. This isn't a "lie", and no one is being cheated.
We can have a queue in our smart contract if we wanted to. The smart contracts are turing complete.
Amoveo smart contracts are almost never processed on-chain. So the speed of processing a contract is not important to us.
WASM is used in cases where you need to optimize for speed at the expense of everything else.
Queues are one of the basic data types of chalang vm.
Register type vms (like wasm) tend to run programs faster, but the source code for programs are longer.
Stack based vms (vms based on forth) tend to run programs more slowly, but the source code for programs are shorter.
For amoveo bandwidth is more important than speed, so we use a forth-like stack based vm.
Why is bandwidth more important if the code is not on the blockchain? I would argue that code should be shared in plaintext source code. For maximized inspectability and thus safety.
bandwidth and execution speed are both less important because the code is usually not written on the blockchain. Relative to each other, bandwidth is still more important than execution speed.
you can decompile the bytecode into forth-like plaintext. You just lose function names, variables names, and whitespace. Currently the light node has a VM, so it can process byte code, but the light node does not have a compiler. So you cannot verify that some plain text code matches some byte code.
"Writing smart contracts where it is easy to verify that they are secure, since our VM uses functions instead of gotos from WASM."
It still wont be secure from improper/buggy logic, which is the biggest problem. Also gotos dont make something less secure, the CPU executes everything based on gotos if you get down to the machine code.
Merkelized abstract syntax tree compatibility is probably the biggest issue to get right for something like the WASM VM.
"WASM is used in cases where you need to optimize for speed at the expense of everything else."
WASM has a clean target plus LLVM already targets it, also its actively being developed. I did some looking into nga which is what I think you meant by Forth compiler, and indeed NGA VM is 10 worlds simpler than WASM VM, so its a strong point if execution speed is irrelevent.
My main argument for using WASM VM is that everyone is creating targets for it these days, so you can write in JS (+typepscript), C, C++ your smart contracts if you target it, more langs are getting WASM target every few months.
"We can have a queue in our smart contract if we wanted to. The smart contracts are turing complete."
Any idea how to implement this then? A global queue for anyone on the network that works if any possible node is down at any given time but not all nodes are down at the same time.
There is no global state in amoveo. Only channel relationships. You can connect channel together with hash locking.
I explain how I built a market in channels here. https://github.com/zack-bitcoin/amoveo/blob/master/docs/design/limit_order_in_channel.md
I did some research about merkelized abstract syntax trees in WASM, and I can't find anything about this. Looks like WASM doesn't support this feature.
Looking at the erlang code that vans quoted. It is better to store Acc1 and Acc2 into variables, since we are using each of these things more than once. It is possible to write this code in elixir or in erlang with variables or without variables. Even if we switched to programming in elixir, I would still define the two variables, because the resultant code is shorter and less repetitive.
If you want to prove the correctness of code, the length of the proof is in proportion to the number of ways that the code can be executed. Every time you use a goto in software, it increases the number of ways that the code can execute exponentially.
The easiest form of computation to reason about are pure stateless functions. With this type of software is easy to prove correctness. That is why our VM uses functions instead of gotos.
The ability to write Amoveo contracts in JS/C/C++ is not useful to us. Those languages all lack the properties were desire.
Im still studying the market in channels implementation was a bit pressed on quality time last few days.
About the WASM yea :(.
If its used more than once then yea prob makes sense. I just wanted to draw up the example that an elixir cond can evaluate expressions vs just being stuck to guards.
Sure agree on the goto.
You can write pure stateless functions in C, C++ and JS ;). Indeed pure functions are the way to do things and I am very upset with the eth community and all the global variables plus unpure state mutations used in smart contracts, its stupid and leads to errors.
I brought JS,C,C++ up as examples for mass adoption. Personally I just wanna be able to write my smart contracts in the Erlang AST. Then can use Erlang | Elixir | LFE | etc.
sure, you can have pure stateless functions in C, but C is a language. You can write a compiler from C for the Chalang VM. You can write a compiler from C for WASM or EVM.
WASM and EVM use gotos, so even if you programmed using pure functions in C, the resulting bytecode will have gotos and will be hard to reason about. If there is a mistake in the compiler, we wont be able to tell by looking at the bytecode of the smart contract. So proving that code is correct involves proving that the compiler is correct, which makes it difficult to ever update the compiler, since you don't want to break any correctness proofs. Proving that a compiler is correct is usually impossible: https://www.win.tue.nl/~aeb/linux/hh/thompson/trust.html Even when it is possible, it is extremely difficult. We don't want verification of the security of smart contracts to involve such a difficult problem.
Amoveo's Chalang VM uses functions, not gotos. So if there is a problem with the smart contract, or with the compiler, you will be able to tell just by looking at the bytecode output. So we can modify compilers for Amoveo's VM much more easily.
Amoveo does not need mass adoption of smart contract developers. It is a very different project from Ethereum. Amoveo needs adoption of channel hubs to act as market makers. There will be a handful of standardized contracts, and the community will generate new standardized contracts occasionally. Market maker hubs just select one of the existing contract templates when they host a market. Probably we only need one or two developers capable of writing Amoveo smart contracts at a time.
Title