Skip to content

Commit c19144c

Browse files
committed
Optimize the process of program deployment and upgrade
1 parent 93606b2 commit c19144c

10 files changed

Lines changed: 55 additions & 41 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ $ python example/program.py --prikey 0x1 --action update --addr 6B7KVuUQ42x8SagF
142142

143143
$ python example/program.py --prikey 0x1 --action call --addr 6B7KVuUQ42x8SagFuFaoiV9jWTSic3Qd771kNrmGwoBG
144144
# Program 6B7KVuUQ42x8SagFuFaoiV9jWTSic3Qd771kNrmGwoBG invoke [1]
145-
# Program log: Hello, Update!
145+
# Program log: Hello, Solana! Hello Update!
146146
# Program log: Our program's Program ID: 6B7KVuUQ42x8SagFuFaoiV9jWTSic3Qd771kNrmGwoBG
147147
# Program 6B7KVuUQ42x8SagFuFaoiV9jWTSic3Qd771kNrmGwoBG consumed 11850 of 200000 compute units
148148
# Program 6B7KVuUQ42x8SagFuFaoiV9jWTSic3Qd771kNrmGwoBG success

example/program.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
print('Program', pubkey, 'create')
4646

4747
if args.action == 'update':
48-
data = bytearray(pathlib.Path('res/hello_solana_program.so.2').read_bytes())
48+
data = bytearray(pathlib.Path('res/hello_update_program.so').read_bytes())
4949
pubkey = pxsol.core.PubKey.base58_decode(args.addr)
5050
user.program_update(pubkey, data)
5151
print('Program', pubkey, 'update')

pxsol/program.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ def close(cls) -> bytearray:
163163
@classmethod
164164
def extend_program(cls, size: int) -> bytearray:
165165
# Extend a program's program data account by the specified number of bytes. Only upgradeable program's can be
166-
# extended. Account references:
166+
# extended. Extend program was superseded by extend program checked. Account references:
167167
# 0. -w the program data account.
168168
# 1. -w the program data account's associated program account.
169169
# 2. -r system program, optional, used to transfer lamports from the payer to the program data account.

pxsol/rpc.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def step() -> None:
5252
# Waiting for at least one new block.
5353
data = get_block_height({})
5454
for _ in itertools.repeat(0):
55+
time.sleep(max(0.5, 1 / pxsol.config.current.rpc.qps))
5556
if get_block_height({}) != data:
5657
break
5758

pxsol/wallet.py

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,37 +22,23 @@ def json(self) -> typing.Dict:
2222
'pubkey': self.pubkey.base58(),
2323
}
2424

25-
def program_buffer_closed(self, program_buffer: pxsol.core.PubKey) -> None:
26-
# Close a buffer account. This method is used to withdraw all lamports when the buffer account is no longer in
27-
# use due to unexpected errors.
28-
rq = pxsol.core.Requisition(pxsol.program.LoaderUpgradeable.pubkey, [], bytearray())
29-
rq.account.append(pxsol.core.AccountMeta(program_buffer, 1))
30-
rq.account.append(pxsol.core.AccountMeta(self.pubkey, 1))
31-
rq.account.append(pxsol.core.AccountMeta(self.pubkey, 2))
32-
rq.data = pxsol.program.LoaderUpgradeable.close()
33-
tx = pxsol.core.Transaction.requisition_decode(self.pubkey, [rq])
34-
tx.message.recent_blockhash = pxsol.base58.decode(pxsol.rpc.get_latest_blockhash({})['blockhash'])
35-
tx.sign([self.prikey])
36-
txid = pxsol.rpc.send_transaction(base64.b64encode(tx.serialize()).decode(), {})
37-
pxsol.rpc.wait([txid])
38-
39-
def program_buffer_create(self, bincode: bytearray) -> pxsol.core.PubKey:
25+
def program_buffer(self, bincode: bytearray) -> pxsol.core.PubKey:
4026
# Writes a program into a buffer account. The buffer account is randomly generated, and its public key serves
4127
# as the function's return value.
4228
tempory_prikey = pxsol.core.PriKey.random()
4329
program_buffer = tempory_prikey.pubkey()
30+
pxsol.log.debugln(f'pxsol: program buffer prikey={tempory_prikey}')
31+
pxsol.log.debugln(f'pxsol: program buffer pubkey={program_buffer}')
32+
account_length = pxsol.program.LoaderUpgradeable.size_program_buffer + len(bincode)
4433
# Sends a transaction which creates a buffer account large enough for the byte-code being deployed. It also
4534
# invokes the initialize buffer instruction to set the buffer authority to restrict writes to the deployer's
4635
# chosen address.
4736
r0 = pxsol.core.Requisition(pxsol.program.System.pubkey, [], bytearray())
4837
r0.account.append(pxsol.core.AccountMeta(self.pubkey, 3))
4938
r0.account.append(pxsol.core.AccountMeta(program_buffer, 3))
5039
r0.data = pxsol.program.System.create_account(
51-
pxsol.rpc.get_minimum_balance_for_rent_exemption(
52-
pxsol.program.LoaderUpgradeable.size_program_data + len(bincode),
53-
{},
54-
),
55-
pxsol.program.LoaderUpgradeable.size_program_buffer + len(bincode),
40+
pxsol.rpc.get_minimum_balance_for_rent_exemption(account_length, {}),
41+
account_length,
5642
pxsol.program.LoaderUpgradeable.pubkey,
5743
)
5844
r1 = pxsol.core.Requisition(pxsol.program.LoaderUpgradeable.pubkey, [], bytearray())
@@ -100,10 +86,12 @@ def program_closed(self, program: pxsol.core.PubKey) -> None:
10086

10187
def program_deploy(self, bincode: bytearray) -> pxsol.core.PubKey:
10288
# Deploying a program on solana, returns the program's public key.
89+
program_buffer = self.program_buffer(bincode)
10390
tempory_prikey = pxsol.core.PriKey.random()
104-
program_buffer = self.program_buffer_create(bincode)
10591
program = tempory_prikey.pubkey()
10692
program_data = pxsol.program.LoaderUpgradeable.pubkey.derive_pda(program.p)
93+
pxsol.log.debugln(f'pxsol: program prikey={tempory_prikey}')
94+
pxsol.log.debugln(f'pxsol: program pubkey={program}')
10795
# Deploy with max data len.
10896
r0 = pxsol.core.Requisition(pxsol.program.System.pubkey, [], bytearray())
10997
r0.account.append(pxsol.core.AccountMeta(self.pubkey, 3))
@@ -122,19 +110,37 @@ def program_deploy(self, bincode: bytearray) -> pxsol.core.PubKey:
122110
r1.account.append(pxsol.core.AccountMeta(pxsol.program.SysvarClock.pubkey, 0))
123111
r1.account.append(pxsol.core.AccountMeta(pxsol.program.System.pubkey, 0))
124112
r1.account.append(pxsol.core.AccountMeta(self.pubkey, 2))
125-
r1.data = pxsol.program.LoaderUpgradeable.deploy_with_max_data_len(len(bincode) * 2)
113+
r1.data = pxsol.program.LoaderUpgradeable.deploy_with_max_data_len(len(bincode))
126114
tx = pxsol.core.Transaction.requisition_decode(self.pubkey, [r0, r1])
127115
tx.message.recent_blockhash = pxsol.base58.decode(pxsol.rpc.get_latest_blockhash({})['blockhash'])
128116
tx.sign([self.prikey, tempory_prikey])
129117
txid = pxsol.rpc.send_transaction(base64.b64encode(tx.serialize()).decode(), {})
130118
pxsol.rpc.wait([txid])
131-
pxsol.rpc.step()
132119
return program
133120

134121
def program_update(self, program: pxsol.core.PubKey, bincode: bytearray) -> None:
135122
# Updating an existing solana program by new program data and the same program id.
136-
program_buffer = self.program_buffer_create(bincode)
123+
program_buffer = self.program_buffer(bincode)
137124
program_data = pxsol.program.LoaderUpgradeable.pubkey.derive_pda(program.p)
125+
# Check the existing program data account size. If the new program data is larger than the existing one,
126+
# extend the program data account first.
127+
program_data_info = pxsol.rpc.get_account_info(program_data.base58(), {})
128+
assert len(base64.b64decode(program_data_info['data'][0])) == program_data_info['space']
129+
addi = pxsol.program.LoaderUpgradeable.size_program_data + len(bincode) - program_data_info['space']
130+
if addi > 0:
131+
pxsol.log.debugln(f'pxsol: extend program data addi={addi}')
132+
rq = pxsol.core.Requisition(pxsol.program.LoaderUpgradeable.pubkey, [], bytearray())
133+
rq.account.append(pxsol.core.AccountMeta(program_data, 1))
134+
rq.account.append(pxsol.core.AccountMeta(program, 1))
135+
rq.account.append(pxsol.core.AccountMeta(self.pubkey, 2))
136+
rq.account.append(pxsol.core.AccountMeta(pxsol.program.System.pubkey, 0))
137+
rq.account.append(pxsol.core.AccountMeta(self.pubkey, 3))
138+
rq.data = pxsol.program.LoaderUpgradeable.extend_program_checked(addi)
139+
tx = pxsol.core.Transaction.requisition_decode(self.pubkey, [rq])
140+
tx.message.recent_blockhash = pxsol.base58.decode(pxsol.rpc.get_latest_blockhash({})['blockhash'])
141+
tx.sign([self.prikey])
142+
txid = pxsol.rpc.send_transaction(base64.b64encode(tx.serialize()).decode(), {})
143+
pxsol.rpc.wait([txid])
138144
rq = pxsol.core.Requisition(pxsol.program.LoaderUpgradeable.pubkey, [], bytearray())
139145
rq.account.append(pxsol.core.AccountMeta(program_data, 1))
140146
rq.account.append(pxsol.core.AccountMeta(program, 1))
@@ -149,7 +155,6 @@ def program_update(self, program: pxsol.core.PubKey, bincode: bytearray) -> None
149155
tx.sign([self.prikey])
150156
txid = pxsol.rpc.send_transaction(base64.b64encode(tx.serialize()).decode(), {})
151157
pxsol.rpc.wait([txid])
152-
pxsol.rpc.step()
153158

154159
def sol_balance(self) -> int:
155160
# Returns the lamport balance of the account.

res/hello_solana_program.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44

55
```sh
66
$ git clone https://github.com/solana-developers/program-examples
7-
$ cd program-examples/basics/hello-solana/native
8-
$ cargo build-bpf --manifest-path=./program/Cargo.toml --bpf-out-dir=./program/target/so
7+
$ cd program-examples/basics/hello-solana/pinocchio
8+
$ cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./program/target/so
99
```

res/hello_solana_program.so

-32.6 KB
Binary file not shown.

res/hello_solana_program.so.2

-38 KB
Binary file not shown.

res/hello_update_program.so

5.47 KB
Binary file not shown.

test/test_wallet.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,31 @@
22
import pathlib
33
import pxsol
44
import random
5+
import typing
56

67

78
def test_program():
89
user = pxsol.wallet.Wallet(pxsol.core.PriKey.int_decode(1))
9-
program_data = bytearray(pathlib.Path('res/hello_solana_program.so').read_bytes())
10-
program_pubkey = user.program_deploy(program_data)
11-
program_data_update = bytearray(pathlib.Path('res/hello_solana_program.so.2').read_bytes())
12-
user.program_update(program_pubkey, program_data_update)
13-
pxsol.rpc.step()
14-
user.program_closed(program_pubkey)
10+
program_hello_solana = bytearray(pathlib.Path('res/hello_solana_program.so').read_bytes())
11+
program_hello_update = bytearray(pathlib.Path('res/hello_update_program.so').read_bytes())
1512

13+
def call(program_pubkey: pxsol.core.PubKey) -> typing.List[str]:
14+
rq = pxsol.core.Requisition(program_pubkey, [], bytearray())
15+
tx = pxsol.core.Transaction.requisition_decode(user.pubkey, [rq])
16+
tx.message.recent_blockhash = pxsol.base58.decode(pxsol.rpc.get_latest_blockhash({})['blockhash'])
17+
tx.sign([user.prikey])
18+
txid = pxsol.rpc.send_transaction(base64.b64encode(tx.serialize()).decode(), {})
19+
pxsol.rpc.wait([txid])
20+
r = pxsol.rpc.get_transaction(txid, {})
21+
return r['meta']['logMessages']
1622

17-
def test_program_buffer():
18-
user = pxsol.wallet.Wallet(pxsol.core.PriKey.int_decode(1))
19-
pubkey = user.program_buffer_create(bytearray(pathlib.Path('res/hello_solana_program.so').read_bytes()))
20-
pxsol.rpc.step()
21-
user.program_buffer_closed(pubkey)
23+
program_pubkey = user.program_deploy(program_hello_solana)
24+
assert call(program_pubkey)[1] == 'Program log: Hello, Solana!'
25+
user.program_update(program_pubkey, program_hello_update)
26+
assert call(program_pubkey)[1] == 'Program log: Hello, Solana! Hello Update!'
27+
user.program_update(program_pubkey, program_hello_solana)
28+
assert call(program_pubkey)[1] == 'Program log: Hello, Solana!'
29+
user.program_closed(program_pubkey)
2230

2331

2432
def test_sol_transfer():

0 commit comments

Comments
 (0)