Skip to content
This repository was archived by the owner on Oct 15, 2024. It is now read-only.

Commit 5866c35

Browse files
committed
feat: Go wasm - extracting common code without generics...
1 parent b88b7e1 commit 5866c35

File tree

9 files changed

+49
-27
lines changed

9 files changed

+49
-27
lines changed

go/README.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
# Go smartweave example contract
22

3-
Note: we're using `tinygo` compiler, as the default compiler produces huuuuge binaries
4-
(`hello world` example - ~2MB)
5-
![img.png](img.png)
3+
## Install go 1.17
4+
https://go.dev/doc/install
65

76
## How to use (default Go compiler)
87
- [Install easyjson](https://github.com/mailru/easyjson#install)
@@ -17,5 +16,16 @@ Note: we're using `tinygo` compiler, as the default compiler produces huuuuge bi
1716
- Build wasm contract file: `bash build-tiny.sh` (it should create `out` folder)
1817
- Run wasm contract simulation: `node run-tiny.js`
1918

20-
Size comparison for PST contract:
21-
![img_1.png](img_1.png)
19+
### Size comparison for PST contract (default go compiler vs `tinygo` compiler):
20+
![img_1.png](img_1.png)
21+
22+
### Folder structure
23+
- `common` - package for commons code, that will be reused between contracts (
24+
handles the low-level WASM-JS communication for Go). Contains `SwContract` interface
25+
that has to implemented by contract developers.
26+
- `common_types` - package for commons structs. Have to be a separate package,
27+
otherwise easyjson throws error during code generation
28+
- `impl` - package that contains implementation of the given contract (i.e. implementation of the `SwContract`
29+
interface)
30+
- `types` - package that contains types specific to the contract that needs to be generated with easyjson
31+
- `main.go` - main file - entry point to the wasm library.

go/common/imports/console/imports.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
)
66

77
func Log(args ...interface{}) {
8-
importConsole().Call("log", args)
8+
importConsole().Call("log", args[0], args[1:])
99
}
1010

1111
func importConsole() js.Value {

go/wasm/wasm.go renamed to go/common/wasm.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
package wasm
1+
package common
22

33
import (
44
"encoding/json"
55
"github.com/redstone-finance/redstone-contracts-wasm/go/common_types"
6-
"github.com/redstone-finance/redstone-contracts-wasm/go/impl"
76
"syscall/js"
87
)
98

10-
func Run(contract *impl.PstContract) {
9+
func Run(contract common_types.SwContract) {
1110
// the Go way of defining WASM exports...
1211
// standard "exports" from the wasm module do not work here...
1312
// that's kinda ugly TBH
@@ -22,7 +21,7 @@ func Run(contract *impl.PstContract) {
2221
<-make(chan bool)
2322
}
2423

25-
func handle(contract *impl.PstContract) js.Func {
24+
func handle(contract common_types.SwContract) js.Func {
2625
// note: each 'exported' function has to be wrapped into
2726
// js.FuncOf(func(this js.Value, args []js.Value) interface{}
2827
// - that's kinda ugly too...
@@ -80,14 +79,14 @@ func lang() interface{} {
8079
})
8180
}
8281

83-
func currentState(contract *impl.PstContract) interface{} {
82+
func currentState(contract common_types.SwContract) interface{} {
8483
return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
8584
data, _ := json.Marshal(contract.CurrentState())
8685
return string(data)
8786
})
8887
}
8988

90-
func initState(contract *impl.PstContract) interface{} {
89+
func initState(contract common_types.SwContract) interface{} {
9190
return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
9291
contract.InitState(args[0].String())
9392
return nil

go/common_types/types.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ type Action struct {
66

77
type ActionResult = interface{}
88

9+
// SwContract We need to use "any" (interface{}) type here for the state - as this is
10+
// a common interface for all contracts implemented in Go WASM.
11+
// Version with generics is available on branch ppe/go-generics (but to use it real life
12+
// we need to wait for the official release of Go 1.18 and tinygo compiler with generics
13+
// support added - https://github.com/tinygo-org/tinygo/issues/2158)
914
//easyjson:skip
1015
type SwContract interface {
16+
Handle(action Action, actionBytes []byte) (interface{}, ActionResult, error)
17+
InitState(stateJson string)
18+
UpdateState(newState interface{})
19+
CurrentState() interface{}
1120
}

go/img.png

-48.1 KB
Binary file not shown.

go/impl/pst-contract.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type PstContract struct {
1313
}
1414

1515
// Handle the function that contract developers actually need to implement
16-
func (c *PstContract) Handle(action common_types.Action, actionBytes []byte) (*types.PstState, common_types.ActionResult, error) {
16+
func (c *PstContract) Handle(action common_types.Action, actionBytes []byte) (interface{}, common_types.ActionResult, error) {
1717
fn := action.Function
1818

1919
console.Log("Calling", fn)
@@ -22,6 +22,8 @@ func (c *PstContract) Handle(action common_types.Action, actionBytes []byte) (*t
2222
console.Log("Block indep_hash", block.IndepHash())
2323
console.Log("Block timestamp", block.Timestamp())
2424

25+
clonedState := c.CloneState().(types.PstState)
26+
2527
switch fn {
2628
case "transfer":
2729
// not sure how to "automatically" handle casting to concrete action impl in Go.
@@ -32,23 +34,23 @@ func (c *PstContract) Handle(action common_types.Action, actionBytes []byte) (*t
3234
if err != nil {
3335
return nil, nil, err
3436
}
35-
state, err := Transfer(c.CloneState(), transfer)
37+
state, err := Transfer(clonedState, transfer)
3638
return state, nil, err
3739
case "balance":
3840
var balance types.BalanceAction
3941
err := balance.UnmarshalJSON(actionBytes)
4042
if err != nil {
4143
return nil, nil, err
4244
}
43-
result, err := Balance(c.CloneState(), balance)
45+
result, err := Balance(clonedState, balance)
4446
return nil, result, err
4547
case "foreignCall":
4648
var foreignCall types.ForeignCallAction
4749
err := foreignCall.UnmarshalJSON(actionBytes)
4850
if err != nil {
4951
return nil, nil, err
5052
}
51-
result, err := ForeignCall(c.CloneState(), foreignCall)
53+
result, err := ForeignCall(clonedState, foreignCall)
5254
return nil, result, err
5355
default:
5456
return nil, nil, errors.New("[RE:WTF] unknown function: " + fn)
@@ -64,18 +66,20 @@ func (c *PstContract) InitState(stateJson string) {
6466
c.UpdateState(&state)
6567
}
6668

67-
func (c *PstContract) UpdateState(newState *types.PstState) {
68-
c.state = *newState
69+
func (c *PstContract) UpdateState(newState interface{}) {
70+
// note: we're first type asserting here to the pointer to types.PstState
71+
// - and the retrieving value from the pointer
72+
c.state = *(newState.(*types.PstState))
6973
}
7074

71-
func (c *PstContract) CurrentState() types.PstState {
75+
func (c *PstContract) CurrentState() interface{} {
7276
return c.state
7377
}
7478

7579
// CloneState TODO: discuss whether it is necessary
7680
// it allows to make the given action transactional, but
7781
// at the cost of performance
78-
func (c *PstContract) CloneState() types.PstState {
82+
func (c *PstContract) CloneState() interface{} {
7983
json, _ := c.state.MarshalJSON()
8084
state := types.PstState{}
8185
err := state.UnmarshalJSON(json)

go/main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package main
22

33
import (
4+
"github.com/redstone-finance/redstone-contracts-wasm/go/common"
45
"github.com/redstone-finance/redstone-contracts-wasm/go/impl"
5-
"github.com/redstone-finance/redstone-contracts-wasm/go/wasm"
66
)
77

8-
// the current state of the contract that contract developers have to define
8+
// contract - implementation of the SwContract interface
99
var contract = impl.PstContract{}
1010

1111
// handles all the WASM-JS related trickery...
1212
func main() {
13-
wasm.Run(&contract)
13+
common.Run(&contract)
1414
}

go/run-tiny.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ async function main() {
2626
global.redstone.go = {
2727
console: {
2828
log: function (...args) {
29-
console.log(args[0], args.slice(1));
29+
console.log(`[WASM] ${args[0]}`, ...args.slice(1));
3030
}
3131
},
3232
Transaction: {
@@ -102,7 +102,7 @@ async function main() {
102102
}
103103
}
104104
)));
105-
console.log('\ncurrentState()', currentState());
105+
console.log('\ncurrentState()', JSON.parse(currentState()));
106106

107107
console.log("\nCalling async handle - transfer");
108108

@@ -117,7 +117,7 @@ async function main() {
117117
console.log('Result from transfer:', resultTransfer);
118118
console.log('Gas used', usedGas);
119119

120-
console.log('\ncurrentState()', currentState());
120+
console.log('\ncurrentState()', JSON.parse(currentState()));
121121

122122
console.log("\nCalling async handle - balance");
123123
const resultBalance = await handle(JSON.stringify({

go/run.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ async function main() {
2626
global.redstone.go = {
2727
console: {
2828
log: function (...args) {
29-
console.log(args[0], args.slice(1));
29+
console.log(args[0], ...args.slice(1));
3030
}
3131
},
3232
Transaction: {

0 commit comments

Comments
 (0)