我在Golang中的锚交易,尽管在锚JS代码中工作良好,但不想玩得很好,并且给了我一个关于签名者帐户(在这种情况下称为authority
)没有签名的错误消息,即使我确实签署了。
我的指令代码结构是这样的:
#[account]
#[derive(Default)]
pub struct Device {
pub ipv4: [u8; 4],
pub hostname: String,
bump: u8,
status: DeviceStatus,
local_key: Pubkey,
}
#[derive(Accounts)]
#[instruction(local_key: Pubkey)]
pub struct RegisterDevice<'info> {
#[account(init,
space = 128,
seeds = [local_key.as_ref()],
bump,
payer = authority,
)]
pub device: Box<Account<'info, Device>>,
#[account(mut)]
pub authority: Signer<'info>,
pub system_program: Program<'info, System>,
}
我的锚代码工作正常,看起来像这样:
it("registers a device", async () => {
const localKey = anchor.web3.Keypair.generate();
const [devicePDA, _devicePDABump] =
await anchor.web3.PublicKey.findProgramAddress(
[localKey.publicKey.toBuffer()],
program.programId
);
await program.methods
.registerDevice(localKey.publicKey)
.accounts({
device: devicePDA,
authority: provider.wallet.PublicKey,
systemProgram: SystemProgram.programId,
})
.rpc();
});
Go代码(使用solana-go库):
registerDeviceInst, err := worknet.NewRegisterDeviceInstruction(
devicePubkey,
pdaPubkey,
*walletPubKey,
gagliardetto.SystemProgramID,
).ValidateAndBuild()
if err != nil {
return err
}
blockHash, err := client.GetRecentBlockhash(context.TODO(), gagliardettorpc.CommitmentFinalized)
if err != nil {
return nil
}
txn, err := gagliardetto.NewTransaction([]gagliardetto.Instruction{registerDeviceInst}, blockHash.Value.Blockhash)
if err != nil {
return err
}
_, err = txn.Sign(func(key gagliardetto.PublicKey) *gagliardetto.PrivateKey {
return walletPrivKey
})
if err != nil {
return err
}
以下是TXN的转储和响应:
(*solana.Transaction)(0x14000110000)(
├─ Signatures[len=1]
│ └─ v1f9bZhMuJkbsrB54y2w36TQdpZDG4bxgbpwrkuD5xSbtCT9kshRnymKB28b2peb5u8YKDoAPRNz4rVz8Vt3pAY
├─ Message
│ ├─ RecentBlockhash: 6PXLreSEHvqP22AsGcAqFzt5Wv1oMGXH8WfUwVuDUtXH
│ ├─ AccountKeys[len=4]
│ │ ├─ HEwe5nVn4y2gV4tCQ3hZ6a3QxB9a72NyXstWxBk4q5ct
│ │ ├─ HqhAGJwjHo36AJwfPzaWAu224rVF9bDWnN9tx6JSdjot
│ │ ├─ 11111111111111111111111111111111
│ │ └─ EdUCoDdRnT5HsQ2Ejy3TWMTQP8iUyMQB4WzoNh45pNX9
│ └─ Header
│ ├─ NumRequiredSignatures: 1
│ ├─ NumReadonlySignedAccounts: 0
│ └─ NumReadonlyUnsignedAccounts: 2
└─ Instructions[len=1]
└─ Program: Worknet EdUCoDdRnT5HsQ2Ejy3TWMTQP8iUyMQB4WzoNh45pNX9
└─ Instruction: RegisterDevice
├─ Params[len=1]
│ └─ LocalKey: (solana.PublicKey) (len=32 cap=32) 8kb1Y6pkENNHnjzkeqWobdjnKTxiHD7fkjAEks2oEoyD
└─ Accounts[len=3]
├─ device: HqhAGJwjHo36AJwfPzaWAu224rVF9bDWnN9tx6JSdjot [WRITE]
├─ authority: HEwe5nVn4y2gV4tCQ3hZ6a3QxB9a72NyXstWxBk4q5ct [WRITE, SIGN]
└─ systemProgram: 11111111111111111111111111111111 []
)
daoctl: error: (*jsonrpc.RPCError)(0x1400038d1a0)({
Code: (int) -32002,
Message: (string) (len=90) "Transaction simulation failed: Error processing Instruction 0: custom program error: 0xbc2",
Data: (map[string]interface {}) (len=3) {
(string) (len=8) "accounts": (interface {}) <nil>,
(string) (len=3) "err": (map[string]interface {}) (len=1) {
(string) (len=16) "InstructionError": ([]interface {}) (len=2 cap=2) {
(json.Number) (len=1) "0",
(map[string]interface {}) (len=1) {
(string) (len=6) "Custom": (json.Number) (len=4) "3010"
}
}
},
(string) (len=4) "logs": ([]interface {}) (len=5 cap=8) {
(string) (len=63) "Program EdUCoDdRnT5HsQ2Ejy3TWMTQP8iUyMQB4WzoNh45pNX9 invoke [1]",
(string) (len=40) "Program log: Instruction: RegisterDevice",
(string) (len=151) "Program log: AnchorError caused by account: authority. Error Code: AccountNotSigner. Error Number: 3010. Error Message: The given account did not sign.",
(string) (len=90) "Program EdUCoDdRnT5HsQ2Ejy3TWMTQP8iUyMQB4WzoNh45pNX9 consumed 4085 of 200000 compute units",
(string) (len=88) "Program EdUCoDdRnT5HsQ2Ejy3TWMTQP8iUyMQB4WzoNh45pNX9 failed: custom program error: 0xbc2"
}
}
})
锚JS做什么魔法,我在我的Go客户端代码缺失?
好的,我已经成功地找出了问题所在。
阅读https://docs.solana.com/developing/programming-model/transactions,我注意到它提到:
Solana runtime…验证每个签名都是由与同一索引上的公钥对应的私钥签名的。在消息的帐户地址数组中。
如果你看一下我在Go代码中传递的帐户,authority
位于索引1,而不是像它的签名那样索引0:
└─ Accounts[len=3]
├─ device: HqhAGJwjHo36AJwfPzaWAu224rVF9bDWnN9tx6JSdjot [WRITE]
├─ authority: HEwe5nVn4y2gV4tCQ3hZ6a3QxB9a72NyXstWxBk4q5ct [WRITE, SIGN]
└─ systemProgram: 11111111111111111111111111111111 []
因此,解决方案是改变锚结构定义中帐户的顺序,重新生成Go绑定,并以正确的顺序传递帐户,以便authority
帐户在事务中索引为0。
#[derive(Accounts)]
#[instruction(local_key: Pubkey)]
pub struct RegisterDevice<'info> {
+ #[account(mut)]
+ pub authority: Signer<'info>,
+
#[account(init,
space = 128,
seeds = [local_key.as_ref()],
bump,
payer = authority,
)]
pub device: Box<Account<'info, Device>>,
- #[account(mut)]
- pub authority: Signer<'info>,
pub system_program: Program<'info, System>,
}
现在交易符号正确。我认为导致锚代码工作的原因是它将在RPC调用中执行一些自动帐户解析,这将正确地放置授权/签名(?),尽管它在生成的Go代码中无序。