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

Commit 57edf7d

Browse files
authored
Fix custom signers (#158)
1 parent d0ff6be commit 57edf7d

26 files changed

+700
-188
lines changed

.changeset/afraid-planets-beam.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@onflow/flow-js-testing": minor
3+
---
4+
5+
Flow JS Testing now exports [`SignatureAlgorithm`](/docs/api.md#signaturealgorithm) and [`HashAlgorithm`](/docs/api.md#hashalgorithm) which are enums that may be used with [`createAccount`](/docs/accounts.md#createaccountname-keys) and [`sendTransaction`](/docs/send-transactions.md)

.changeset/clean-oranges-hammer.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
"@onflow/flow-js-testing": minor
33
---
44

5-
Allow custom transaction signers to be provided as object with `addr`, `privateKey`, `keyId`, `hashAlgorithm` keys as an alternative to supplying merely the signer's account address and having Flow JS Testing determine the rest. This allows for more complex transaction authorizers. See [documentation for examples](/docs/send-transactions.md).
5+
Allow custom transaction signers to be provided as object with `addr`, `privateKey`, `keyId`, `hashAlgorithm`, `signatureAlgorithm` keys as an alternative to supplying merely the signer's account address and having Flow JS Testing determine the rest. This allows for more complex transaction authorizers. See [documentation for examples](/docs/send-transactions.md).

.changeset/kind-fishes-switch.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@onflow/flow-js-testing": minor
3+
---
4+
5+
Flow JS Testing now exports the [`pubFlowKey`](/docs/api.md#pubflowkeykeyobject) method which may be used to generate an RLP-encoded `Buffer` representing a public key corresponding to a particular private key.

.changeset/pre.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"@onflow/flow-js-testing": "0.2.3-alpha.7"
66
},
77
"changesets": [
8-
"clean-oranges-hammer",
98
"giant-dots-trade",
109
"gold-cheetahs-attend",
1110
"long-hairs-collect",

.changeset/tasty-fans-repeat.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@onflow/flow-js-testing": minor
3+
---
4+
5+
Flow JS Testing now exports the [`createAccount`](/docs/accounts.md#createaccountname-keys) method which may be used to manually create an account with a given human-readable name & specified keys.

CHANGELOG.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
# flow-js-testing
22

3-
## 0.3.0-alpha.13
4-
5-
### Minor Changes
6-
7-
- [#155](https://github.com/onflow/flow-js-testing/pull/155) [`9dcab53`](https://github.com/onflow/flow-js-testing/commit/9dcab535393654e3c6ba41a3ac41095519446c27) Thanks [@jribbink](https://github.com/jribbink)! - Allow custom transaction signers to be provided as object with `addr`, `privateKey`, `keyId`, `hashAlgorithm` keys as an alternative to supplying merely the signer's account address and having Flow JS Testing determine the rest. This allows for more complex transaction authorizers. See [documentation for examples](/docs/send-transactions.md).
8-
93
## 0.3.0-alpha.12
104

115
### Patch Changes
Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
import FlowManager from 0x01
22

3-
transaction (_ name: String, pubKey: String, manager: Address) {
3+
transaction (_ name: String?, pubKey: [String], manager: Address) {
44
prepare( admin: AuthAccount) {
55
let newAccount = AuthAccount(payer:admin)
6-
newAccount.addPublicKey(pubKey.decodeHex())
6+
for key in pubKey {
7+
newAccount.addPublicKey(key.decodeHex())
8+
}
79

8-
let linkPath = FlowManager.accountManagerPath
9-
let accountManager = getAccount(manager)
10-
.getCapability(linkPath)!
11-
.borrow<&FlowManager.Mapper>()!
12-
13-
// Create a record in account database
14-
let address = newAccount.address
15-
accountManager.setAddress(name, address: address)
10+
if name != nil {
11+
let linkPath = FlowManager.accountManagerPath
12+
let accountManager = getAccount(manager)
13+
.getCapability(linkPath)!
14+
.borrow<&FlowManager.Mapper>()!
15+
16+
// Create a record in account database
17+
let address = newAccount.address
18+
accountManager.setAddress(name!, address: address)
19+
}
1620
}
1721
}

dev-test/account.test.js

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import path from "path"
2+
import {
3+
emulator,
4+
init,
5+
getAccountAddress,
6+
executeScript,
7+
createAccount,
8+
HashAlgorithm,
9+
SignatureAlgorithm,
10+
} from "../src"
11+
import {playgroundImport} from "../src/transformers"
12+
import * as fcl from "@onflow/fcl"
13+
import {validateKeyPair} from "./util/validate-key-pair"
14+
import {permute} from "./util/permute"
15+
import {isAddress} from "../src"
16+
17+
beforeEach(async () => {
18+
const basePath = path.resolve(__dirname, "./cadence")
19+
await init(basePath)
20+
return emulator.start()
21+
})
22+
23+
afterEach(async () => {
24+
emulator.stop()
25+
})
26+
27+
it("createAccount - should work with name and resolves to correct getAccountAddress", async () => {
28+
const addr1 = await createAccount({
29+
name: "Billy",
30+
})
31+
32+
const addr2 = await getAccountAddress("Billy")
33+
34+
expect(addr1).toBe(addr2)
35+
})
36+
37+
it("createAccount - should work without name and returns address", async () => {
38+
const Billy = await createAccount({
39+
name: "Billy",
40+
})
41+
42+
expect(isAddress(Billy)).toBe(true)
43+
})
44+
45+
test.each(permute(Object.keys(HashAlgorithm), Object.keys(SignatureAlgorithm)))(
46+
"createAccount - should work with custom keys - hash algorithm %s - signing algorithm %s",
47+
async (hashAlgorithm, signatureAlgorithm) => {
48+
const privateKey = "1234"
49+
const Billy = await createAccount({
50+
name: "Billy",
51+
keys: [
52+
{
53+
privateKey,
54+
hashAlgorithm: HashAlgorithm[hashAlgorithm],
55+
signatureAlgorithm: SignatureAlgorithm[signatureAlgorithm],
56+
},
57+
],
58+
})
59+
60+
expect(isAddress(Billy)).toBe(true)
61+
62+
const keys = await fcl.account(Billy).then(d => d.keys)
63+
64+
expect(keys.length).toBe(1)
65+
expect(keys[0].hashAlgoString).toBe(hashAlgorithm)
66+
expect(keys[0].signAlgoString).toBe(signatureAlgorithm)
67+
expect(keys[0].weight).toBe(1000)
68+
expect(
69+
validateKeyPair(keys[0].publicKey, privateKey, signatureAlgorithm)
70+
).toBe(true)
71+
}
72+
)
73+
74+
test("createAccount - should work with custom keys - defaults to SHA3_256, ECDSA_P256", async () => {
75+
const privateKey = "1234"
76+
const Billy = await createAccount({
77+
name: "Billy",
78+
keys: [
79+
{
80+
privateKey,
81+
},
82+
],
83+
})
84+
85+
expect(isAddress(Billy)).toBe(true)
86+
87+
const keys = await fcl.account(Billy).then(d => d.keys)
88+
89+
expect(keys.length).toBe(1)
90+
expect(keys[0].hashAlgoString).toBe("SHA3_256")
91+
expect(keys[0].signAlgoString).toBe("ECDSA_P256")
92+
expect(keys[0].weight).toBe(1000)
93+
expect(
94+
validateKeyPair(
95+
keys[0].publicKey,
96+
privateKey,
97+
SignatureAlgorithm.ECDSA_P256
98+
)
99+
).toBe(true)
100+
})
101+
102+
it("createAccount - should add universal private key to account by default", async () => {
103+
const Billy = await createAccount({
104+
name: "Billy",
105+
})
106+
107+
expect(isAddress(Billy)).toBe(true)
108+
})
109+
110+
it("getAccountAddress - should return proper playground addresses", async () => {
111+
const accounts = ["Alice", "Bob", "Charlie", "Dave", "Eve"]
112+
for (const i in accounts) {
113+
await getAccountAddress(accounts[i])
114+
}
115+
116+
const code = `
117+
pub fun main(address:Address):Address{
118+
return getAccount(address).address
119+
}
120+
`
121+
122+
const playgroundAddresses = ["0x01", "0x02", "0x03", "0x04", "0x05"]
123+
for (const i in playgroundAddresses) {
124+
const [result] = await executeScript({
125+
code,
126+
transformers: [playgroundImport(accounts)],
127+
args: [playgroundAddresses[i]],
128+
})
129+
const account = await getAccountAddress(accounts[i])
130+
expect(result).toBe(account)
131+
}
132+
})
133+
134+
it("getAccountAddress - should create an account if does not exist", async () => {
135+
const Billy = await getAccountAddress("Billy")
136+
137+
expect(isAddress(Billy)).toBe(true)
138+
})
139+
140+
it("getAccountAddress - should resolve an already created account", async () => {
141+
const Billy1 = await getAccountAddress("Billy")
142+
const Billy2 = await getAccountAddress("Billy")
143+
144+
expect(isAddress(Billy1)).toBe(true)
145+
expect(Billy1).toMatch(Billy2)
146+
})

dev-test/interaction.test.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import {
1111
shallThrow,
1212
shallPass,
1313
} from "../src"
14+
import {createAccount} from "../src/account"
15+
import {HashAlgorithm, pubFlowKey, SignatureAlgorithm} from "../src/crypto"
16+
import {permute} from "./util/permute"
1417

1518
// We need to set timeout for a higher number, cause some transactions might take up some time
1619
jest.setTimeout(10000)
@@ -116,6 +119,82 @@ describe("interactions - sendTransaction", () => {
116119
await shallPass(sendTransaction({code, signers}))
117120
})
118121

122+
test.each(
123+
permute(Object.keys(HashAlgorithm), Object.keys(SignatureAlgorithm))
124+
)(
125+
"sendTransaction - shall pass with custom signer - %s hash algorithm, %s signature algorithm",
126+
async (hashAlgorithm, signatureAlgorithm) => {
127+
const privateKey = "1234"
128+
const Adam = await createAccount({
129+
name: "Adam",
130+
keys: [
131+
await pubFlowKey({
132+
privateKey,
133+
hashAlgorithm,
134+
signatureAlgorithm,
135+
weight: 1000,
136+
}),
137+
],
138+
})
139+
140+
const code = `
141+
transaction{
142+
prepare(signer: AuthAccount){
143+
assert(signer.address == ${Adam}, message: "Signer address must be equal to Adam's Address")
144+
}
145+
}
146+
`
147+
const signers = [
148+
{
149+
addr: Adam,
150+
keyId: 0,
151+
privateKey,
152+
hashAlgorithm,
153+
signatureAlgorithm,
154+
},
155+
]
156+
157+
await shallPass(sendTransaction({code, signers}))
158+
}
159+
)
160+
161+
test("sendTransaction - shall pass with custom signer - hashAlgorithm, signatureAlgorithm resolved via string - pubKey resolved via privKey", async () => {
162+
const hashAlgorithm = "ShA3_256" //varying caps to test case insensitivity
163+
const signatureAlgorithm = "eCdSA_P256"
164+
165+
const privateKey = "1234"
166+
const Adam = await createAccount({
167+
name: "Adam",
168+
keys: [
169+
{
170+
privateKey,
171+
hashAlgorithm,
172+
signatureAlgorithm,
173+
weight: 1000,
174+
},
175+
],
176+
})
177+
178+
const code = `
179+
transaction{
180+
prepare(signer: AuthAccount){
181+
assert(signer.address == ${Adam}, message: "Signer address must be equal to Adam's Address")
182+
}
183+
}
184+
`
185+
const signers = [
186+
{
187+
addr: Adam,
188+
keyId: 0,
189+
privateKey,
190+
hashAlgorithm,
191+
signatureAlgorithm,
192+
},
193+
]
194+
195+
await shallPass(sendTransaction({code, signers}))
196+
})
197+
119198
test("sendTransaction - argument mapper - basic", async () => {
120199
await shallPass(async () => {
121200
const code = `

dev-test/util/permute.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const permute = (...values) =>
2+
values.length > 1
3+
? permute(
4+
values[0].reduce((acc, i) => {
5+
return [...acc, ...values[1].map(j => [].concat(i).concat(j))]
6+
}, [])
7+
)
8+
: values[0]

0 commit comments

Comments
 (0)