Skip to content

Commit 79f657a

Browse files
fix: linter errors
1 parent d228859 commit 79f657a

5 files changed

Lines changed: 254 additions & 26 deletions

File tree

programs/bpf-loader/loader.go

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
package bpfloader
2+
3+
import (
4+
"encoding/binary"
5+
"fmt"
6+
7+
"github.com/gagliardetto/solana-go"
8+
"github.com/gagliardetto/solana-go/programs/system"
9+
"github.com/gagliardetto/solana-go/rpc"
10+
)
11+
12+
const (
13+
PACKET_DATA_SIZE int = 1280 - 40 - 8
14+
)
15+
16+
// https://github.com/solana-labs/solana/blob/v1.7.15/cli/src/program.rs#L1683
17+
func calculateMaxChunkSize(
18+
createBuilder func(offset int, data []byte) *solana.TransactionBuilder,
19+
) (size int, err error) {
20+
transaction, err := createBuilder(0, []byte{}).Build()
21+
if err != nil {
22+
return
23+
}
24+
signatures := make(
25+
[]solana.Signature,
26+
transaction.Message.Header.NumRequiredSignatures,
27+
)
28+
transaction.Signatures = append(transaction.Signatures, signatures...)
29+
serialized, err := transaction.MarshalBinary()
30+
if err != nil {
31+
return
32+
}
33+
size = PACKET_DATA_SIZE - len(serialized) - 1
34+
return
35+
}
36+
37+
// https://github.com/solana-labs/solana/blob/v1.7.15/cli/src/program.rs#L2006
38+
func completePartialProgramInit(
39+
loaderId solana.PublicKey,
40+
payerPubkey solana.PublicKey,
41+
elfPubkey solana.PublicKey,
42+
account *rpc.Account,
43+
accountDataLen int,
44+
minimumBalance uint64,
45+
allowExcessiveBalance bool,
46+
) (instructions []solana.Instruction, balanceNeeded uint64, err error) {
47+
if account.Executable {
48+
err = fmt.Errorf("buffer account is already executable")
49+
return
50+
}
51+
if !account.Owner.Equals(loaderId) &&
52+
!account.Owner.Equals(solana.SystemProgramID) {
53+
err = fmt.Errorf(
54+
"buffer account passed is already in use by another program",
55+
)
56+
return
57+
}
58+
if len(account.Data.GetBinary()) > 0 &&
59+
len(account.Data.GetBinary()) < accountDataLen {
60+
err = fmt.Errorf(
61+
"buffer account passed is not large enough, may have been for a " +
62+
" different deploy?",
63+
)
64+
return
65+
}
66+
67+
if len(account.Data.GetBinary()) == 0 &&
68+
account.Owner.Equals(solana.SystemProgramID) {
69+
instructions = append(
70+
instructions,
71+
system.NewAllocateInstruction(uint64(accountDataLen), elfPubkey).
72+
Build(),
73+
)
74+
instructions = append(
75+
instructions,
76+
system.NewAssignInstruction(loaderId, elfPubkey).Build(),
77+
)
78+
if account.Lamports < minimumBalance {
79+
balance := minimumBalance - account.Lamports
80+
instructions = append(
81+
instructions,
82+
system.NewTransferInstruction(balance, payerPubkey, elfPubkey).
83+
Build(),
84+
)
85+
balanceNeeded = balance
86+
} else if account.Lamports > minimumBalance &&
87+
account.Owner.Equals(solana.SystemProgramID) &&
88+
!allowExcessiveBalance {
89+
err = fmt.Errorf(
90+
"buffer account has a balance: %v.%v; it may already be in use",
91+
account.Lamports/solana.LAMPORTS_PER_SOL,
92+
account.Lamports%solana.LAMPORTS_PER_SOL,
93+
)
94+
return
95+
}
96+
}
97+
return
98+
}
99+
100+
func load(
101+
payerPubkey solana.PublicKey,
102+
account *rpc.Account,
103+
programData []byte,
104+
bufferDataLen int,
105+
minimumBalance uint64,
106+
loaderId solana.PublicKey,
107+
bufferPubkey solana.PublicKey,
108+
allowExcessiveBalance bool,
109+
) (
110+
initialBuilder *solana.TransactionBuilder,
111+
writeBuilders []*solana.TransactionBuilder,
112+
finalBuilder *solana.TransactionBuilder,
113+
balanceNeeded uint64,
114+
err error,
115+
) {
116+
var instructions []solana.Instruction
117+
if account != nil {
118+
instructions, balanceNeeded, err = completePartialProgramInit(
119+
loaderId,
120+
payerPubkey,
121+
bufferPubkey,
122+
account,
123+
bufferDataLen,
124+
minimumBalance,
125+
allowExcessiveBalance,
126+
)
127+
if err != nil {
128+
return
129+
}
130+
} else {
131+
instructions = append(
132+
instructions,
133+
system.NewCreateAccountInstruction(
134+
minimumBalance,
135+
uint64(bufferDataLen),
136+
loaderId,
137+
payerPubkey,
138+
bufferPubkey,
139+
).Build(),
140+
)
141+
balanceNeeded = minimumBalance
142+
}
143+
if len(instructions) > 0 {
144+
initialBuilder = solana.NewTransactionBuilder().SetFeePayer(payerPubkey)
145+
for _, instruction := range instructions {
146+
initialBuilder = initialBuilder.AddInstruction(instruction)
147+
}
148+
}
149+
150+
createBuilder := func(offset int, chunk []byte) *solana.TransactionBuilder {
151+
data := make([]byte, len(chunk)+16)
152+
binary.LittleEndian.PutUint32(data[0:], 0)
153+
binary.LittleEndian.PutUint32(data[4:], uint32(offset))
154+
binary.LittleEndian.PutUint32(data[8:], uint32(len(chunk)))
155+
binary.LittleEndian.PutUint32(data[12:], 0)
156+
copy(data[16:], chunk)
157+
instruction := solana.NewInstruction(
158+
loaderId,
159+
solana.AccountMetaSlice{
160+
solana.NewAccountMeta(bufferPubkey, true, true),
161+
},
162+
data,
163+
)
164+
return solana.NewTransactionBuilder().
165+
AddInstruction(instruction).
166+
SetFeePayer(payerPubkey)
167+
}
168+
169+
chunkSize, err := calculateMaxChunkSize(createBuilder)
170+
if err != nil {
171+
return
172+
}
173+
writeBuilders = []*solana.TransactionBuilder{}
174+
for i := 0; i < len(programData); i += chunkSize {
175+
end := i + chunkSize
176+
if end > len(programData) {
177+
end = len(programData)
178+
}
179+
writeBuilders = append(
180+
writeBuilders,
181+
createBuilder(i, programData[i:end]),
182+
)
183+
}
184+
185+
finalBuilder = solana.NewTransactionBuilder().SetFeePayer(payerPubkey)
186+
{
187+
data := make([]byte, 4)
188+
binary.LittleEndian.PutUint32(data[0:], 1)
189+
instruction := solana.NewInstruction(
190+
loaderId,
191+
solana.AccountMetaSlice{
192+
solana.NewAccountMeta(bufferPubkey, true, true),
193+
},
194+
data,
195+
)
196+
finalBuilder.AddInstruction(instruction)
197+
}
198+
return
199+
}
200+
201+
func Deploy(
202+
payerPubkey solana.PublicKey,
203+
account *rpc.Account,
204+
programData []byte,
205+
minimumBalance uint64,
206+
loaderId solana.PublicKey,
207+
bufferPubkey solana.PublicKey,
208+
allowExcessiveBalance bool,
209+
) (
210+
initialBuilder *solana.TransactionBuilder,
211+
writeBuilders []*solana.TransactionBuilder,
212+
finalBuilder *solana.TransactionBuilder,
213+
balanceNeeded uint64,
214+
err error,
215+
) {
216+
return load(
217+
payerPubkey,
218+
account,
219+
programData,
220+
len(programData),
221+
minimumBalance,
222+
loaderId,
223+
bufferPubkey,
224+
allowExcessiveBalance,
225+
)
226+
}

rpc/jsonrpc/jsonrpc.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"sync/atomic"
1313

1414
"github.com/davecgh/go-spew/spew"
15-
stdjson "github.com/goccy/go-json"
1615
gojson "github.com/goccy/go-json"
1716
"github.com/google/uuid"
1817
)
@@ -205,10 +204,10 @@ func NewRequest(method string, params ...any) *RPCRequest {
205204
//
206205
// See: http://www.jsonrpc.org/specification#response_object
207206
type RPCResponse struct {
208-
JSONRPC string `json:"jsonrpc"`
209-
Result stdjson.RawMessage `json:"result,omitempty"`
210-
Error *RPCError `json:"error,omitempty"`
211-
ID any `json:"id"`
207+
JSONRPC string `json:"jsonrpc"`
208+
Result gojson.RawMessage `json:"result,omitempty"`
209+
Error *RPCError `json:"error,omitempty"`
210+
ID any `json:"id"`
212211
}
213212

214213
// RPCError represents a JSON-RPC error object if an RPC error occurred.
@@ -293,7 +292,7 @@ func (res RPCResponses) AsMap() map[any]*RPCResponse {
293292
for _, r := range res {
294293
actualID := r.ID
295294
if actualID != nil {
296-
if asFloat, ok := actualID.(stdjson.Number); ok {
295+
if asFloat, ok := actualID.(gojson.Number); ok {
297296
asInt64, err := asFloat.Int64()
298297
if err == nil {
299298
actualID = int(asInt64)

rpc/jsonrpc/jsonrpc_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"strconv"
1111
"testing"
1212

13-
stdjson "github.com/goccy/go-json"
13+
gojson "github.com/goccy/go-json"
1414
. "github.com/onsi/gomega"
1515
"github.com/stretchr/testify/require"
1616
)
@@ -386,14 +386,14 @@ func TestRpcJsonResponseStruct(t *testing.T) {
386386
res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3)
387387
<-requestChan
388388
Expect(err).To(BeNil())
389-
Expect(res.Result).To(Equal(stdjson.RawMessage([]byte(strconv.Quote("ok")))))
389+
Expect(res.Result).To(Equal(gojson.RawMessage([]byte(strconv.Quote("ok")))))
390390

391391
// result with error null is ok
392392
responseBody = `{"result": "ok", "error": null}`
393393
res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3)
394394
<-requestChan
395395
Expect(err).To(BeNil())
396-
Expect(res.Result).To(Equal(stdjson.RawMessage([]byte(strconv.Quote("ok")))))
396+
Expect(res.Result).To(Equal(gojson.RawMessage([]byte(strconv.Quote("ok")))))
397397

398398
// error with result null is ok
399399
responseBody = `{"error": {"code": 123, "message": "something wrong"}, "result": null}`
@@ -655,8 +655,8 @@ func TestRpcBatchJsonResponseStruct(t *testing.T) {
655655
})
656656
<-requestChan
657657
Expect(err).To(BeNil())
658-
Expect(res[0].Result).To(Equal(stdjson.RawMessage([]byte(strconv.Quote("ok")))))
659-
Expect(res[0].ID).To(Equal(stdjson.Number("1")))
658+
Expect(res[0].Result).To(Equal(gojson.RawMessage([]byte(strconv.Quote("ok")))))
659+
Expect(res[0].ID).To(Equal(gojson.Number("1")))
660660

661661
// result with error null is ok
662662
responseBody = `[{"result": "ok", "error": null}]`
@@ -665,7 +665,7 @@ func TestRpcBatchJsonResponseStruct(t *testing.T) {
665665
})
666666
<-requestChan
667667
Expect(err).To(BeNil())
668-
Expect(res[0].Result).To(Equal(stdjson.RawMessage([]byte(strconv.Quote("ok")))))
668+
Expect(res[0].Result).To(Equal(gojson.RawMessage([]byte(strconv.Quote("ok")))))
669669

670670
// error with result null is ok
671671
responseBody = `[{"error": {"code": 123, "message": "something wrong"}, "result": null}]`
@@ -731,10 +731,10 @@ func TestRpcBatchJsonResponseStruct(t *testing.T) {
731731
Expect(err).To(BeNil())
732732

733733
Expect(res[0].Error).To(BeNil())
734-
Expect(res[0].ID).To(Equal(stdjson.Number("1")))
734+
Expect(res[0].ID).To(Equal(gojson.Number("1")))
735735

736736
Expect(res[1].Error).To(BeNil())
737-
Expect(res[1].ID).To(Equal(stdjson.Number("2")))
737+
Expect(res[1].ID).To(Equal(gojson.Number("2")))
738738

739739
err = res[0].GetObject(&p)
740740
require.NoError(t, err)

rpc/util_test.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,21 @@ import (
1111
// Layout references for the test data below:
1212
//
1313
// SPL Token (solana-program/token):
14-
// Mint::LEN = 82
15-
// Account::LEN = 165
14+
//
15+
// Mint::LEN = 82
16+
// Account::LEN = 165
1617
//
1718
// Token-2022 (solana-program/token-2022):
18-
// Extended records place a 1-byte AccountType discriminator at offset
19-
// Account::LEN (= 165). Mint base (82 bytes) is padded with 83 zeros so
20-
// Mint and Account share the discriminator offset.
2119
//
22-
// AccountType::Uninitialized = 0
23-
// AccountType::Mint = 1
24-
// AccountType::Account = 2
20+
// Extended records place a 1-byte AccountType discriminator at offset
21+
// Account::LEN (= 165). Mint base (82 bytes) is padded with 83 zeros so
22+
// Mint and Account share the discriminator offset.
23+
//
24+
// AccountType::Uninitialized = 0
25+
// AccountType::Mint = 1
26+
// AccountType::Account = 2
2527
//
26-
// Extensions follow as TLV: [u16 LE type][u16 LE length][value...].
28+
// Extensions follow as TLV: [u16 LE type][u16 LE length][value...].
2729
const (
2830
testAccountTypeUninitialized uint8 = 0
2931
testAccountTypeMint uint8 = 1

transaction_bench_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import (
1111
//
1212
// numInstructions: how many instructions in the transaction.
1313
// accountsPerIx: how many AccountMeta entries each instruction references.
14-
// (first is always a signer; second is always writable; rest readonly)
14+
//
15+
// (first is always a signer; second is always writable; rest readonly)
1516
func buildBenchInstructions(numInstructions, accountsPerIx int) ([]Instruction, Hash) {
1617
// Pre-generate a pool of unique accounts so instructions share some accounts
1718
// (realistic — the same fee payer / writable state account appears in many ixs)
@@ -98,9 +99,9 @@ func buildBenchInstructionsWithLookups(numInstructions, accountsPerIx int) ([]In
9899
// Upper bound is ~10 instructions / ~30 accounts per ix — beyond that
99100
// becomes a synthetic stress shape that doesn't represent real traffic.
100101
var benchTxShapes = []struct {
101-
name string
102+
name string
102103
numInstructions int
103-
accountsPerIx int
104+
accountsPerIx int
104105
}{
105106
{"small_2ix_5accts", 2, 5},
106107
{"medium_5ix_15accts", 5, 15},

0 commit comments

Comments
 (0)