-
Notifications
You must be signed in to change notification settings - Fork 0
/
pool.ride
200 lines (179 loc) · 8.71 KB
/
pool.ride
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
{-# STDLIB_VERSION 6 #-}
{-# CONTENT_TYPE DAPP #-}
{-# SCRIPT_TYPE ACCOUNT #-}
# Replace the following constants with your own values. The only necessary change is the operatorPk. The rest can be left as is for testing purposes.
# Operator (relayer) public key
let operatorPk = base58'GyVshojyNKdnR3NVsHttiwRDYmcodrtCj7V9sFbNAuxF'
let operatorAddress = addressFromPublicKey(operatorPk)
# Denominator for storing amounts
let denominator = 100000000
# Transfer and tree verification keys: base64-encoded binary format taken from https://github.com/zeropoolnetwork/zeropool-test-params
let transferVk = base64'SYrHhkeDrw+dFMD6Yr/Vrvr2pPyDHCWoMO8w9LxFaQABrVkxqK1PRfgZw0t9bDNsxFEJNHhErc9XHTkkU1OiEYYWiz2VdqW6jQewS86Z3lshxW6Fdf1MU0MDPt1NoikSf2YEIBvI+gscAfqmtvJxZbkVA/Lrr7r+HyVUoZGt1QB46Zv9umeZBwmTWiDHaLPPgOVOWQAVYLMCwpEzEzIdF2C8KzI/CsCeCv3raf56RXmhFAWuJCbMtO6My12PxFQDRv98IWP0FGhY/m6sQ/9r58GInq4bSVYWKlGucbXbayaYF35Fp3o6VaRXVWzJ6FesrH8dS+DxeadscnAN4IQEB4J5YcwZvAX/fjE5JEHNC4WrNfiUe+fe6FAQ4ttHlRgKS+PbN1butH1dDkfQkBKC5Om0WI3D45eaRbN6Taxt7ha5xCUwGVh0LQ+Wf/j9bY+un1Cwur9wx+HmB6PPGC2pEFLSQhkZjtvNJTs6e1c0i3FAOhFDSNqEjcqORSer2aUGdJMXhB6YEv/XcumUDyQbem+f0wX2qOgtwVFLtGOq7S65sHzNcHpdJP/htHnhqkQErwmjDG/JTs9oc1mu5jOsGAYAAAAovMt7vHGhKq+EA+6JlSFfVMj7aU/V67EpBI64f73xJKTpnqCOEMhaQMegFHTJ/W5Cp9iTd4i3AqnoTQj+L1UOXKmddpvHhg0WJHFbosaPhv8EY7+cAaHmn1JPO30BBRPqTsN53lfykgE2oF9vGPyU4u0Y3Oh8YG2p7I2Oz+NaKkzrJo2SamjhbLOofkEX14HbjdZLWD5T07/J4qfaYb8Eervvcr/H2IS0GJ7UFTdqJGwWa/1IZZF6RN8rj5y8xg2BoioqwyhpS3I7gROorvBg6hMSFh4eYXROfzISFgWiF5eouqLDFV3P8geZwxSRDnzOaDyCe3Y7EaxhPBjHbtYA/DLJnfWLC6H6AnAA+fLxTZHjtEL/7Mcc+8MYPzH1mAf9gA0UiXxfzFWLSiAzWDBF+Uzpxn2XO25DNREAiZeYIHqYaq6iaMlcNtk5iiSMm+aO6VPqh1tuPoIND/ry5aALN3TlgjnOoDOTGpJIlakKGfGnRHaM/Jd0vbiJ723UUAM='
let treeVk = base64'qGBPhudr70q2hdpdMHxxPVdk+MJDazUwWbBTl+eJCx/zUEagbSAhrTUc40Jajsexh+ThMSv1ZlKhNgbjvc8GFwfScaiOtcM0fDjNcUeChr2NpgbQV+y4XTvC8sjwVXEareR6FvrGlyEdKcFx+vIagiT77lQ3MK/EfxwlNHsEdAy0sWThEjpDykFPiYWG6T5IJWIGtdhrcPdePYv1GX0wCakWmTMh9NWYby1erGRPnDxWxeFcOBN4DPhkSQKg/DMH1LkY/GlCqIdfdX5Mr+0CMQWnmr8oECA5QgeOJ1ZnSQmO18Jg9rclNx9mhqD3iux0b+o293HkYg4d75ZOBeUqLdx63/eIe/VEuwIkGFBs4sU9WHEsdqpx8QKpByl6ZKgiOcx/wrW0cwTvm34cq5pRVGunz+qu90se2PqAjOjiIRoDvrcCR7qlN0iTvBedZRTlOtbOxAiXOb/nwFrsmf8wFiebpKkjbNkNvTiLGRiea53xusmuNwANOZtfYMT03Kwf1bgRFrkM6TqgEVZq1Vl7wjFhGu3CFrk5popRZ0X8zhJqn5IPLt6g/0ImUPee8frB6VtMYYkmQ7ZCWTwKEPGDIgQAAACMoBtyPY40yogNVj5bF/LbEe5QGKZjwyfMj58AY5ocK00CEsC3Nm1wSKR6xi8NSbjhPZRw0/zChVK9K8ceAO4ijNFpOFuOpTO6aEq0o7spO/E0JTwTStxF0jdD9NCydSTLZi4DdtHHkcph4Sao8AwV4qd50wavb0/hxCXkVWTvErgnIWCgjrfgzQBDfa99z0alUbwyBBd8odwfJU7bERsH/SfhNempxiwEU1uq6TPQ2NxgN3fgXo5i9dHrSaI2ZQd7R9hyJVeMlBaz6uP4gS1XYlt5LkT+TFGT/vidKlcMHQx945x1Iivq728eKgJ4aBI5Ja1nxj7QC7d98xukukQl'
# The initial root of an empty merkle tree
let initialRoot = toBytes(parseBigIntValue("11469701942666298368112882412133877458305516134926649826543144744382391691533"))
func getRootKey(index: Int) = "R:" + toString(index)
func getNullifierKey(nullifier: ByteVector) = "N:" + toBase58String(nullifier)
func getDepositKey(address: ByteVector) = "D:" + toBase58String(address)
let poolIndexKey = "PoolIndex"
let allMessagesHashKey = "AllMessagesHash"
func takeExt(v: ByteVector, from: Int, to: Int) = {
drop(take(v, to), from)
}
func spendDeposit(address: Address, amount: Int) = {
let currentKey = getDepositKey(address.bytes)
let currentAmount = match getInteger(currentKey) {
case a:Int => a
case _ => 0
}
let newAmount = currentAmount - amount
if (amount < 0) then
throw("Can't spend negative amount")
else if (newAmount < 0) then
throw("Not enough balance")
else [
IntegerEntry(currentKey, newAmount)
]
}
@Callable(i)
func deposit() = {
let pmt = value(i.payments[0])
if (isDefined(pmt.assetId)) then throw("Only WAVES is allowed at the moment")
else {
let currentKey = getDepositKey(i.caller.bytes)
let currentAmount = match getInteger(this, currentKey) {
case a:Int => a
case _ => 0
}
let newAmount = currentAmount + pmt.amount
[IntegerEntry(currentKey, newAmount)]
}
}
@Callable(i)
func withdraw(amount: Int) = {
let currentKey = getDepositKey(i.caller.bytes)
let currentAmount = match getInteger(this, currentKey) {
case a:Int => a
case _ => 0
}
let newAmount = currentAmount - amount
if (amount < 0) then
throw("Can't withdraw negative amount")
else if (newAmount < 0) then
throw("Not enough balance")
else [
IntegerEntry(currentKey, newAmount),
ScriptTransfer(i.caller, amount, unit)
]
}
# nullifier 32 bytes
# outCommit 32 bytes
# assetId 32 bytes
# delta 32 bytes
# nativeAmount 8 bytes
# nativeEnergy 14 bytes
# txIndex 6 bytes
# poolId 3 bytes
# txProof 256 bytes
# treeProof 256 bytes
# rootAfter 32 bytes
# txType 2 bytes
# memo dynamic bytes
# depositPk optional 32 bytes
# depositSignature optional 64 bytes
@Callable(i)
func transact(tx: ByteVector) = {
let nullifier = takeExt(tx, 0, 32)
let outCommit = takeExt(tx, 32, 64)
let assetId = takeExt(tx, 64, 96)
let delta = takeExt(tx, 96, 128)
let nativeAmount = toInt(takeExt(delta, 0, 8))
let nativeEnergy = toInt(takeExt(delta, 8, 22))
let txIndex = toInt(takeExt(delta, 22, 28))
let _poolId = takeExt(delta, 28, 31)
let txProof = takeExt(tx, 128, 384)
let treeProof = takeExt(tx, 384, 640)
let rootAfter = takeExt(tx, 640, 672)
let txType = toInt(takeExt(tx, 672, 674))
let (memo, depositPk, depositSignature) = if (txType == 0) then
let depositDataSize = 32 + 64
let depositPk = dropRight(takeRight(tx, depositDataSize), 64)
let depositSignature = takeRight(tx, 64)
(takeExt(tx, 674, size(tx) - depositDataSize), depositPk, depositSignature)
else
(takeExt(tx, 674, size(tx)), unit, unit)
let fee = toInt(takeExt(memo, 0, 8))
let allMessagesHash = valueOrElse(getBinary(allMessagesHashKey), toBytes(toBigInt(0)))
let memoHash = keccak256(memo)
let hashes = allMessagesHash + memoHash
let newAllMessagesHash = keccak256(hashes)
strict rootBefore = valueOrElse(getBinary(getRootKey(txIndex)), initialRoot)
let poolIndex = valueOrElse(getInteger(poolIndexKey), 0)
let poolRoot = valueOrElse(getBinary(getRootKey(poolIndex)), initialRoot)
let nullifierExists = match getBinary(getNullifierKey(nullifier)) {
case a:Unit => false
case _ => true
}
let transferInputs = memoHash + nullifier + outCommit + delta + memoHash
let treeInputs = poolRoot + rootAfter + outCommit
let dataOps = [
BinaryEntry(getRootKey(txIndex), rootAfter),
BinaryEntry(getNullifierKey(nullifier), keccak256(outCommit + delta)),
IntegerEntry(poolIndexKey, poolIndex + 128),
BinaryEntry(allMessagesHashKey, newAllMessagesHash)
]
let txOps = match txType {
# deposit
case 0 => {
let depositPkValue = value(depositPk)
let depositAddress = addressFromPublicKey(depositPkValue)
if (nativeAmount < 0) then
throw("Can't deposit negative amount")
else if (!sigVerify_8Kb(nullifier + depositPkValue, value(depositSignature), depositPkValue)) then
throw("Invalid deposit signature")
else
spendDeposit(depositAddress, nativeAmount * denominator)
}
# transfer
case 1 => {
if (nativeAmount != 0) then
throw("Native amount must be zero for transfers")
else
[]
}
# withdraw
case 2 => {
let withdrawAmount = toInt(takeExt(memo, 8, 16)) * denominator
if (withdrawAmount > 0) then
throw("Withdraw amount must be positive")
else {
[ScriptTransfer(i.caller, -withdrawAmount, unit)]
}
}
case _ => throw("Unknown tx type")
}
let feeOps = if (fee > 0) then {
let totalFee = fee * denominator
[ScriptTransfer(operatorAddress, fee, unit)]
}
else
[]
if (txIndex > poolIndex) then
throw("Transaction index is too big")
else if (!bn256Groth16Verify_5inputs(transferVk, txProof, transferInputs)) then
throw("Tx proof is invalid")
else if (!bn256Groth16Verify_3inputs(treeVk, treeProof, treeInputs)) then
throw("Tree proof is invalid")
else if (nullifierExists) then
throw("Double spend")
else
txOps ++ dataOps ++ feeOps
}
@Verifier(tx)
func verify() = {
match tx {
case d: SetScriptTransaction =>
sigVerify(tx.bodyBytes, tx.proofs[0], operatorPk)
case _ => false
}
}